connecting to database in c++ - c++

In php I create a config file that opens a connection to the database and then I use that file in all my other files in order to open a connection also. But I can't seem to find a way to do the same thing with c++. I can connect to the database but I can't use it as a class because I have a main() inside it and I can't seem to make it work without the main. This is my code:
// Standard C++ includes
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
// Include the Connector/C++ headers
#include "cppconn/driver.h"
#include "cppconn/exception.h"
#include "cppconn/resultset.h"
#include "cppconn/statement.h"
// Link to the Connector/C++ library
#pragma comment(lib, "mysqlcppconn.lib")
// Specify our connection target and credentials
const string server = "localhost";
const string username = "root";
const string password = "";
int main()
{
sql::Driver *driver; // Create a pointer to a MySQL driver object
sql::Connection *dbConn; // Create a pointer to a database connection object
sql::Statement *stmt; // Create a pointer to a Statement object to hold our SQL commands
sql::ResultSet *res; // Create a pointer to a ResultSet object to hold the results of any queries we run
// Try to get a driver to use to connect to our DBMS
try
{
driver = get_driver_instance();
}
catch (sql::SQLException e)
{
cout << "Could not get a database driver. Error message: " << e.what() << endl;
system("pause");
exit(1);
}
// Try to connect to the DBMS server
try
{
dbConn = driver->connect(server, username, password);
}
catch (sql::SQLException e)
{
cout << "Could not connect to database. Error message: " << e.what() << endl;
system("pause");
exit(1);
}
stmt = dbConn->createStatement();
// Try to query the database
try
{
stmt->execute("USE test");
res = stmt->executeQuery("SELECT * FROM users");
}
catch (sql::SQLException e)
{
cout << "SQL error. Error message: " << e.what() << endl;
system("pause");
exit(1);
}
sql::ResultSetMetaData *res_meta = res -> getMetaData();
int columns = res_meta -> getColumnCount();
while (res->next())
{
for (int i = 1; i <= columns; i++) {
cout << res->getString(i) << " | " ;
}
cout << endl;
}
delete res;
delete stmt;
delete dbConn;
return 0;
}

You need to create a class (e.g class DBConnector)and declare functions in header file.
For example this piece of code can go into one function:
sql::Driver* DBConnector::GetDriverInstance()
{
try
{
m_driver = get_driver_instance();
}
catch (sql::SQLException e)
{
cout << "Could not get a database driver. Error message: " << e.what() << endl;
system("pause");
exit(1);
}
return m_driver;
}
Here sql::Driver *m_driver is expected to be declared as a member variable of class in header file. You might need to read more about classes and member functions and variables before actually going ahead with code.
Other than this, you need to take very good care of memory management. I would suggest you read more and use smart pointers like std::shared_ptr.

Related

structuring C++ program with mysql connections

I'm trying to create a c++ program that connects to mysql and can perform operations on the database such as add remove and edit.
For each table, I want to be able to add, remove and edit as well as run specific querys to my needs.
So far, I have everything in a single main file as I cannot figure out how to divide up my code to have one connection, and then be able to call other classes to run querys.
I clearly a beginner in C++ and am looking for some input into what I should have in each class
#include <stdlib.h>
#include <iostream>
#include "mysql_connection.h"
#include "cppconn/driver.h"
#include "cppconn/exception.h"
#include "cppconn/resultset.h"
#include "cppconn/statement.h"
using namespace std;
int main(void)
{
cout << endl;
try {
sql::Driver *driver;
sql::Connection *con;
sql::Statement *stmt;
sql::ResultSet *res;
/* Create a connection */
driver = get_driver_instance();
con = driver->connect("tcp://127.0.0.1:3306", "root", "root");
/* Connect to the MySQL test database */
con->setSchema("ParkSmart");
stmt = con->createStatement();
res = stmt->executeQuery("SELECT * FROM user WHERE userID = 9");
while (res->next()) {
cout << res->getString(2) << endl;
// getInt(1) returns the first column
// ... or column names for accessing results.
}
//add person(int personID, string fname, string lname)
try
{
int personID = 9;
string fname = "test";
string lname = "man";
string query = "SELECT * FROM person WHERE personID = " + to_string(personID);
res = stmt->executeQuery(query);
if (res->next()){
cout << "A person with that" << endl;
return 0;
//failure, already exists
}
else {
string query = "INSERT INTO user (personID, fname, lname) VALUES (" + to_string(personID) + ", '" + fname + "', '" + lname + ")" ;
res = stmt->executeQuery(query);
return 1;
//success, user added in to database
}
}
catch (sql::SQLException &e) {
cout << "Exception caught: no query returned" << endl;
//this is when there is no user with that userID, continue
//check for other more specific errors
}
What I am thinking is a main that calls a person class that connects to mysql and for each table there is a class that has its own connection but I'm getting seg faults
The code that I have can connect and edit the database properly, I am just lost on the actual file structures.
Any help or advice is appreciated!

Error message after inserting to a table in MYSQL db

I have this c++ code that works fine, i can read from the tables and write to the tables:
int main()
{
// Try to get a driver to use to connect to our DBMS
try
{
driver = get_driver_instance();
}
catch (sql::SQLException e)
{
cout << "Could not get a database driver. Error message: " << e.what() << endl;
system("pause");
exit(1);
}
// Try to connect to the DBMS server
try
{
dbConn = driver->connect(server, username, password);
}
catch (sql::SQLException e)
{
cout << "Could not connect to database. Error message: " << e.what() << endl;
system("pause");
exit(1);
}
stmt = dbConn->createStatement(); // Specify which connection our SQL statement should be executed on
// Try to query the database
try
{
stmt->execute("USE test"); // Select which database to use. Notice that we use "execute" to perform a command.
res = stmt->executeQuery("INSERT INTO users (fName, lName, age) VALUES ('fname', 'lname', 25)"); // Perform a query and get the results. Notice that we use "executeQuery" to get results back
//res = stmt->executeQuery("SELECT * FROM users");
//return 0;
}
catch (sql::SQLException e)
{
cout << "SQL error. Error message: " << e.what() << endl;
system("pause");
exit(1);
}
sql::ResultSetMetaData *res_meta = res -> getMetaData();
int columns = res_meta -> getColumnCount();
// While there are still results (i.e. rows/records) in our result set...
while (res->next())
{
for (int i = 1; i <= columns; i++) {
cout << res->getString(i) << " | " ;
}
cout << endl;
}
delete res;
delete stmt;
delete dbConn;
//system("pause");
return 0;
}
So, this inserts to the table but then i get this error message
SQL error. Error message: sh: 1: pause: not found
This doesn't happen if i use the "select".
Also i know that this question was already asked here but unfortunately it has no answer so i'm asking again.
Your question looks related to MySQL Query executes but throws exception.
executeQuery() assumes that sql query should return sql::ResultSet but your INSERT INTO query does not. You can use execute() instead, which returns true or false:
try
{
stmt->execute("USE test");
stmt->execute("INSERT INTO users (fName, lName, age) VALUES ('fname', 'lname', 25)");
}
catch (sql::SQLException e)
{
cout << "SQL error. Error message: " << e.what() << endl;
exit(1);
}
An INSERT is not a query. Try using executeUpdate() instead of executeQuery(). Look at the official MySQL example here.
Replace this line
res = stmt->executeQuery("INSERT INTO users (fName, lName, age) VALUES ('fname', 'lname', 25)"); // Perform a query and get the results. Notice that we use "executeQuery" to get results back
with the following lines (you may need a new .h file):
sql::PreparedStatement *pstmt;
pstmt = con->prepareStatement("INSERT INTO users (fName, lName, age)
VALUES ('fname', 'lname', 25)");
res = pstmt->executeUpdate();
delete pstmt;
You may also try using execute(), as shown in this Stackoverflow question. The function execute() is used for generic SQL commands, but may not be as verbose in its return value as more specified functions (it returns a boolean).

Why are decimals dropped in my Qt App in std::vector<double> when the data comes from MySQL?

In my Qt application I use a C++ function that fills a std::vector<double> with data from a MySQL database. The function uses MySQL Connector/C++ and does the following:
void create_price(sql::Connection *con, std::string database, std::string table, std::vector<double> &price_vec)
{
sql::Statement *stmt;
sql::ResultSet *res;
std::string use_database = "USE " + database;
stmt = con->createStatement();
stmt->execute(use_database);
use_database = "SELECT Time,LastVolume,LastPrice FROM "+ table +" WHERE LastVolume >0 OR LastPrice >0 ORDER BY Time ASC";
res = stmt->executeQuery(use_database); //will return many rows
while (res->next()) {
price_vec.push_back(res->getDouble("LastPrice"));
}
}
When I call this function with any std::vector<double> in my plain C++ application I get flawless results. However, when I call this function in my Qt application the passed vector is filled with ints. The decimal part is dropped!
It seems to be an issue with the MySQL part, since this test function returns proper doubles, even when called from within my Qt application.
void double_test(std::vector &doublevec){
//just some doubles to test.
std::vector<double> doublevec;
doublevec.push_back(1.4);
doublevec.push_back(5.324);
}
Is it possible that the double returned by the MySQL query is not actually a real double and therefore not compatible with Qt?
Any hint on what I can do to understand the problem better would also help.
EDIT: The part that establishes the connection looks like this
C++:
int main(int argc, char** argv) {
sql::Driver *driver;
sql::Connection *con;
driver = get_driver_instance();
try{
con = driver->connect("tcp://127.0.0.1:3306","root","pass");
cout << "Connection established" << endl;
}
catch(...){
cout << "Exception: Couldnt' connect to MySQL server. Check if it is running" << endl;
return 0;
}
vector<double> price;
create_price(con,"database","table",price);
driver->threadEnd();
delete con;
cout << "Job done" << endl;
return 0;
}
Qt:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// con and driver are private class members
driver = get_driver_instance();
try{
con = driver->connect("tcp://127.0.0.1:3306","root","pass");
std::cout << "Connection established" << std::endl;
}
catch(...){
std::cout << "Exception: Couldnt' connect to MySQL server. Check if it is running" << std::endl;
}
std::vector<double> price; //this vector now has doubles without decimals.
create_price(con,"database","table",price);
setupPlot();
}

Why is my program crashing when MySQL server is unavalibe

I've been programming in C++ with VS 2010 Professional but I am stuck on this problem:
If the program starts and the connection is good then it loads the "gameactive" loop, and displays the high score of the player.
But when the connection has a error it freezes and crashes. I want to display a error like "error connecting to server" and continue loading the game.
I did use this tutorial:
http://r3dux.org/2010/11/how-to-use-mysql-connectorc-to-connect-to-a-mysql-database-in-windows/
Here is my code:
// Standad C++ includes
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
#include <string.h>
// Include the Connector/C++ headers
#include "cppconn/driver.h"
#include "cppconn/exception.h"
#include "cppconn/resultset.h"
#include "cppconn/statement.h"
#include <cgl\cgl.h>
#include <cgl\core.h>
#include <core\corefile.h>
// Link to the Connector/C++ library
#pragma comment(lib, "mysqlcppconn.lib")
// Specify our connection target and credentials
const string server = "tcp://xxx.xxx.xxx.xxx:3306";
const string username = "xxxxxxx";
const string password = "XXxxXXxxXXxx";
char myoutput[1024];
char myoutput1[1024];
char myoutput2[1024];
s_font font;
s_bitmap bmp_background;
int connectsetup()
{
sql::Driver *driver; // Create a pointer to a MySQL driver object
sql::Connection *dbConn; // Create a pointer to a database connection object
sql::Statement *stmt; // Create a pointer to a Statement object to hold our SQL commands
sql::ResultSet *res; // Create a pointer to a ResultSet object to hold the results of any queries we run
// Try to get a driver to use to connect to our DBMS
try
{
driver = get_driver_instance();
}
catch (sql::SQLException e)
{
cout << "Could not get a database driver. Error message: " << e.what() << endl;
}
// Try to connect to the DBMS server
try
{
dbConn = driver->connect(server, username, password);
}
catch (sql::SQLException e)
{
cout << "Could not connect to database. Error message: " << e.what() << endl;
}
stmt = dbConn->createStatement(); // Specify which connection our SQL statement should be executed on
// Try to query the database
try
{
stmt->execute("USE runner"); // Select which database to use. Notice that we use "execute" to perform a command.
res = stmt->executeQuery("SELECT * FROM highscores"); // Perform a query and get the results. Notice that we use "executeQuery" to get results back
}
catch (sql::SQLException e)
{
cout << "SQL error. Error message: " << e.what() << endl;
}
// While there are still results (i.e. rows/records) in our result set...
while (res->next())
{
// ...get each field we want and output it to the screen
// Note: The first field/column in our result-set is field 1 (one) and -NOT- field 0 (zero)
// Also, if we know the name of the field then we can also get it directly by name by using:
// res->getString("TheNameOfTheField");
//printf(myoutput,"%s %s %s",res->getString(1),res->getString(2),res->getString(3));
strcpy(myoutput,res->getString(1).c_str());
strcpy(myoutput1,res->getString(2).c_str());
strcpy(myoutput2,res->getString(3).c_str());
}
// Clean up after ourselves
delete res;
delete stmt;
delete dbConn;
return 0;
}
void coremain()
{
//Fullscreen, windowed of scaled?
corefile_mountimage("res",MOUNT_DIR);
CGL_InitVideo(1280, 720, CGL_VIDEO_NONE);
CGL_SetTitle("CGL - Endless poepert");
CGL_InitFont("font_heat.tga", &font);
CGL_LoadBitmap("track1.tga",&bmp_background);
connectsetup();
int gameactive=1;
int getal=atoi(myoutput);
do {
do {
CGL_WaitRefresh();
CGL_DrawBitmap(0,0,bmp_background);
CGL_DrawCenteredText(100,font, "%s: %d van %s score %s",myoutput,getal,myoutput1,myoutput2);
int key,keytrig;
CGL_GetKeys(&key,&keytrig);
if (keytrig & CGL_INPUT_KEY_EXIT) exit(EXIT_SUCCESS);
CGL_SwapBuffers();
} while(gameactive);
CGL_FlushGraphics();
} while(1);
CGL_CloseVideo();
}
The issue seems to be your connectSetup function. You have try/catch blocks, but you don't do anything if there is an issue. You just keep going as if nothing is wrong.
Also, this is a great time to learn smart pointers. The reason why smart pointers should be used here is in the case where something goes wrong even if the database connection is successful (you have subsequent try/catch blocks). You want to ensure that anything that was allocated is cleaned up, regardless of when or where you issue a return statement.
#include <memory>
#include <string>
std::string myoutput;
std::string myoutput1;
std::string myoutput2;
int connectsetup()
{
sql::Driver* driver;
std::unique_ptr<sql::Connection> dbConn;
std::unique_ptr<sql::Statement> stmt;
std::unique_ptr<sql::ResultSet> res;
try
{
driver = get_driver_instance();
dbConn.reset(driver->connect(server, username, password))
stmt.reset(dbConn->createStatement());
stmt->execute("USE runner");
res.reset(stmt->executeQuery("SELECT * FROM highscores")); // Perform a query
while (res->next())
{
myoutput = res->getString(1);
myoutput1 = res->getString(2);
myoutput2 = res->getString(3);
}
}
catch (const sql::SQLException& e)
{
cout << "Something went wrong with the database stuff. Here it is: " << e.what() << endl;
return -1;
}
return 0;
}
This code was not compiled, but I attempted to extract what your original code was doing and rewrote it using smart pointers.
The code above will throw an exception if any of those statements fail. Note that we catch the exception by reference, not by value. Also note that there is no need for delete, since std::unique_ptr does this work automatically.
Finally, there is no need for char arrays -- why did you introduce them? It only made another part of your function vulnerable to a memory overwrite, namely in the res->next() loop.

Why is my destructor not being called?

My destructor is not being called when the program exits. The object is a singleton, perhaps I am missing something?
Here is the header and cpp file:
#ifndef MYSQLCONNECTOR_H
#define MYSQLCONNECTOR_H
/* Standard C++ headers */
#include <iostream>
#include <string>
/* MySQL Connector/C++ specific headers */
#include <driver.h>
#include <connection.h>
#include <statement.h>
#include <prepared_statement.h>
#include <resultset.h>
#include <metadata.h>
#include <resultset_metadata.h>
#include <exception.h>
#include <warning.h>
class MysqlConnector {
private:
static bool instanceFlag;
static MysqlConnector* mysqlConnector;
MysqlConnector() {
};
public:
static sql::Driver *driver;
static sql::Connection *conn;
static MysqlConnector* getInstance();
virtual ~MysqlConnector() {
instanceFlag = false;
conn->close();
delete conn;
std::cout << "called" << std::endl;
};
private:
};
#endif /* MYSQLCONNECTOR_H */
And the cpp file
#include "MysqlConnector.h"
using namespace std;
using namespace sql;
bool MysqlConnector::instanceFlag = false;
MysqlConnector* MysqlConnector::mysqlConnector = NULL;
MysqlConnector* MysqlConnector::getInstance() {
if (!instanceFlag) {
mysqlConnector = new MysqlConnector();
instanceFlag = true;
try {
driver = get_driver_instance();
/* create a database connection using the Driver */
conn = driver->connect("tcp://127.0.0.1:3306", "root", "root");
/* turn off the autocommit */
conn -> setAutoCommit(0);
/* select appropriate database schema */
conn -> setSchema("exchange");
} catch (SQLException &e) {
cout << "ERROR: SQLException in " << __FILE__;
cout << " (" << __func__ << ") on line " << __LINE__ << endl;
cout << "ERROR: " << e.what();
cout << " (MySQL error code: " << e.getErrorCode();
cout << ", SQLState: " << e.getSQLState() << ")" << endl;
if (e.getErrorCode() == 1047) {
cout << "\nYour server does not seem to support Prepared Statements at all. ";
cout << "Perhaps MYSQL < 4.1?" << endl;
}
} catch (std::runtime_error &e) {
cout << "ERROR: runtime_error in " << __FILE__;
cout << " (" << __func__ << ") on line " << __LINE__ << endl;
cout << "ERROR: " << e.what() << endl;
}
return mysqlConnector;
} else {
return mysqlConnector;
}
}
Your destructor is not getting called because nobody is calling delete for an object created with new:
mysqlConnector = new MysqlConnector(); // Where's the corresponding call to delete?
You may consider using a smart pointer instead (I would suggest std::unique_ptr, if you can afford C++11). That would automatically called delete on the encapsulated object when the smart pointer itself is destroyed.
Another possibility is to not use pointers at all, and have a static data member of type MysqlConnector. getInstance() could then return a reference (rather than a pointer) to that object.
static MysqlConnector* mysqlConnector;
Is the pointer to the class. When you get the instance it just returns this pointer. You need to call delete on this static pointer for the destructor to be called.
If you do go ahead and call delete on this pointer, make sure you only do so when the program terminates though - since the next call to getInstance() will create a new object.
EDIT - just spotted the instanceFlag so it won't create a new instance but would return a NULL pointer
This is problematic since you might expect the Singleton to be the same object, but of course creating a new instance of it will mean that your data will not be persistent (even though you are accessing a singleton).
The OS will reclaim the memory regardless, so you might be better to expose the connection closing code and calling that by yourself?