mySQL query in C++ - c++

In the following code for retrieving data via a SQL query from the db, is there a way I can replace row[0] with fieldname. I am only enclosing the relevant part of the code
MYSQL_RES *resptr;
MYSQL_ROW row;
while ( ( row = mysql_fetch_row(resptr)) != NULL )
{
for (int i=0;i<8;i++)
string key = row[0];
}
row[0] is the trans_date column. the code works fine as it is right now, but is there a way to assign key by using the fieldname vs. having to remember all the field numbers.
thanks!

You can retrieve field names by doing
field = mysql_fetch_field(resptr);
std::cout << field->name;
(Put in a while loop to loop through all field)
But you can't call row[fieldName]. What you can do though is define variables to map column names and numbers
int id = 0;
int trans_date = 1;
// Code happening here
std::cout << row[id] << " " << row[trans_date];
UPDATE: You could always do something like this (using STL's map):
map<string, int> columns;
int i = 0;
while(field = mysql_fetch_field(resptr)){
columns.insert(pair<string,int>(field->name, i++));
}
And then use row[columns["name"]];.

MySQL++ solves this problem. You can say things like:
string key = row["key"];
or by use of its SSQLS feature, this:
MyTableStructure foo = result[i];
cout << "The key is " << foo.key << endl;

Related

Queries containing table variable fail in Nanodbc

I have a query that declares a table variable. It seems nanodbc cannot fetch data although the query works fine when directly executed on SQL server. Does it mean complicated queries are not supported by nanodbc?
Environment
nanodbc version: 2.14
DBMS name/version: MS SQL Server 2017
ODBC connection string:
OS and Compiler: Windows, Visual C++ 2019
CMake settings:
Actual behavior
sql queries containing table variables do not work.
Expected behavior
sql queries containing table variables should work.
Minimal Working Example
void show(nanodbc::result& results)
{
const short columns = results.columns();
long rows_displayed = 0;
cout << "\nDisplaying " << results.affected_rows() << " rows "
<< "(" << results.rowset_size() << " fetched at a time):" << endl;
// show the column names
cout << "row\t";
for(short i = 0; i < columns; ++i)
cout << results.column_name(i) << "\t";
cout << endl;
// show the column data for each row
while(results.next())
{
cout << rows_displayed++ << "\t";
for(short col = 0; col < columns; ++col)
cout << "(" << results.get<string>(col, "null") << ")\t";
cout << endl;
}
}
nanodbc::connection db_conn_ = nanodbc::connection(db_conn_string_);
execute(db_conn_, NANODBC_TEXT("DROP TABLE IF EXISTS research.dbo.nanodbc_test"));
execute(db_conn_, NANODBC_TEXT("CREATE TABLE research.dbo.nanodbc_test(Name varchar(20), Age int)"));
execute(db_conn_, NANODBC_TEXT("INSERT INTO research.dbo.nanodbc_test(Name, Age) VALUES('Bar', 20)"));
execute(db_conn_, NANODBC_TEXT("INSERT INTO research.dbo.nanodbc_test(Name, Age) VALUES('Foo', 30)"));
nanodbc::result result_working = execute( db_conn_, NANODBC_TEXT("select * from research.dbo.nanodbc_test"));
show(result_working);
//The following query does not return valid result, result_not_working contains 0 observations.
nanodbc::result result_not_working = execute(
db_conn_,
NANODBC_TEXT(
"declare #names table(n varchar(20) not null primary key);"
" insert into #names select * from string_split('Bar,Foo', ',');"
" select[Name], [Age]"
" from research.dbo.nanodbc_test where Age in(20, 30)"
" and [Name] in(select n from #names)"
)
);
show(result_not_working);
The question above is solved by adding 'set nocount on' at the beginning of the query suggested by David and Dan mentioned below.
What I am trying to solve is actually a bit more complicated. I want to run a parameterized query.
string query = NANODBC_TEXT(
"set nocount on"
" declare #names table(n varchar(20) not null primary key);"
" insert into #names select * from string_split(?, ',');"
" select[Name], [Age]"
" from research.dbo.nanodbc_test where Age in(20, 30)"
" and [Name] in(select n from #names)"
);
nanodbc::statement statement = nanodbc::statement(db_conn__);
prepare(statement, query);
string names = "Bar,Foo";
//The error happens when I try to bind a string parameter.
statement.bind(0, names.c_str());
Can someone help on this? Thanks.

sqlite3_get_table - testing for existence of table?

I have the following code, and sql string in which my errors have been solved on another post.
Basically, this function tests whether a table exists or not.. That's it.
I'm posted the relevant section below.. For some reason, even if the table doesn't exist in the database, my function always tells me that the table exists?
Any clues as to what I've done wrong here.. Could it be the way I'm using the return value of sqlite3_get_table?
string sql_string = "SELECT name FROM sqlite_master WHERE type = 'table' AND name='test_table';";
// open database
dbase_return = sqlite3_open_v2(dbase_path.c_str(),&DBHANDLE,SQLITE_OPEN_READWRITE,NULL);
if(!dbase_return)
{
dbase_return_tbl = sqlite3_get_table(DBHANDLE,sql_string.c_str(),&result,&row,&column,&error_msg);
if(dbase_return_tbl==SQLITE_OK)
{
cout << " Table exists" << endl;
sqlite3_close(DBHANDLE);
return 1;
}
else
{
cout << "Table doesn't exists" << endl;
sqlite3_close(DBHANDLE);
return 0;
}
}

Using lower_bound on nested map

I have a map that looks like
map<string , map<int,int>>
the string contains name of a student, the nested map contains ID as key and age as value. When I print the map, it al prints values as it should.
However, I want to find a students with a certain ID and lower. I tried using lower_bound using:
for( auto &x : class ){
auto it = x.second.upper_bound(some_number);
for( ; it != x .second.begin() ; --it){
cout << x.first << " = " << << it -> first << " " <<it -> second << endl;
}
}
This indeed prints right names of students, but their IDs and ages are just zeros or random numbers, what is causing this behavior? It works when I just print it.
I tried to found out about this on cpp map reference but found nothing.
Following code solves your problem:
for( auto &x : Class ){
auto it = x.second.upper_bound(some_number);
while(it!=x.second.begin()){
it=prev(it);
cout<< x.first<< " = "<< it->first<< " "<< it->second<< endl;
}
}
Refer std::map::upper_bound
What above code does is, first it finds the iterator with id strictly greater than some_number. Now because we want to print "students with a certain ID and lower", we print all the id's lower than the return value of upper_bound.
The stopping condition is that if iterator is itself x.second.begin(), that means now we don't have any id's smaller than it.
Plus your data structure is strange, you should have student ID as your primary index.
map<int, pair<string,int> > would be more appropriate data structure. (Assuming unique id's which is mostly the case).
Although, you could do lot better using OOP concepts.
What you see is probably undefined behaviour, std::map::upper_bound returns also end iterator under some conditions and from your code it does not look like you check for this condition. Also you should not use class keyword as variable name for your map, I am preety sure it does not compile. Below is a sample code that should work with no UB and print all IDs less than some number including this ID:
http://coliru.stacked-crooked.com/a/efae1ae4faa3e656
map< string , map<int,int>> classes ={
{ "k1", {{1,1},{2,2},{3,3}} }
};
//int class;
int some_number = 4;
for( auto &x : classes ){
auto it_num_end = x.second.upper_bound(some_number); // some numberis just variable that contains number
for( auto it = x.second.begin(); it != it_num_end ; ++it){
cout << x.first << " = " << it -> first << " " <<it -> second << endl;
}
}

Binding and returning text data with sqlite3 and C++

I am trying to get back into programming for a personal project I would like to work on but I am having trouble figuring out the sqlite3 c/c++ api. I am modifying code snippets I found at various tutorials and I'm trying to get them return data and field names from a database. Here is the section of code that I am having difficulty with:
sqlite3 *db;
sqlite3_stmt *ppStmt;
int col_num;
char *zErrMsg = 0;
const char *zSql = "SELECT ?,? FROM basic_table";
string s = "Field1";
string t = "Field2";
rc = sqlite3_prepare_v2(db,zSql,-1,&ppStmt,0);
sqlite3_bind_text(ppStmt,1,s.c_str(),s.length(),SQLITE_TRANSIENT);
sqlite3_bind_text(ppStmt,2,t.c_str(),t.length(),SQLITE_TRANSIENT);
if(rc)
{
cerr << "SQL error: " << sqlite3_errmsg(db) << endl;
}
else
{
col_num = sqlite3_column_count(ppStmt);
do
{
rc = sqlite3_step(ppStmt);
switch(rc)
{
case SQLITE_DONE:
break;
case SQLITE_ROW:
for( int i=0; i<col_num; i++)
{
cout << sqlite3_column_name(ppStmt, i) << " " <<
sqlite3_column_text(ppStmt,i) << endl;
}
break;
default:
cerr << "Inside error " << sqlite3_errmsg(db) << endl;
break;
}
} while( rc==SQLITE_ROW );
sqlite3_finalize(ppStmt);
The column_name return the correct value but the data just comes back with a question mark. I can get the code to work by calling sqlite3_exec with a callback function but the documentation for sqlite3 recommends using sqlite3_prepare_v2, sqlite3_bind, sqlite3_step, and sqlite3_finalize instead of this method so I am just trying to see if I can get that method to work. Is there a simple error which I am making? Any suggestions would be appreciated.
The name of these columns is ?, because that is how they are written in the query.
The values of these columns are Field1 and Field2, because you told the database that you want to have these string values returned by the SELECT.
The database works as designed.
Parameters can be used only to replace the value of an expression; you cannot use them to replace identifiers such as column/table names.
You have to write the column names directly in the SQL statement:
const char *zSql = "SELECT Field1, Field2 FROM basic_table";

Function returning MYSQL_ROW

I'm working on a system using lots of MySQL queries and I'm running into some memory problems I'm pretty sure have to do with me not handling pointers right...
Basically, I've got something like this:
MYSQL_ROW function1() {
string query="SELECT * FROM table limit 1;";
MYSQL_ROW return_row;
mysql_init(&connection); // "connection" is a global variable
if (mysql_real_connect(&connection,HOST,USER,PASS,DB,0,NULL,0)){
if (mysql_query(&connection,query.c_str()))
cout << "Error: " << mysql_error(&connection);
else{
resp = mysql_store_result(&connection); //"resp" is also global
if (resp) return_row = mysql_fetch_row(resp);
mysql_free_result(resp);
}
mysql_close(&connection);
}else{
cout << "connection failed\n";
if (mysql_errno(&connection))
cout << "Error: " << mysql_errno(&connection) << " " << mysql_error(&connection);
}
return return_row;
}
And function2():
MYSQL_ROW function2(MYSQL_ROW row) {
string query = "select * from table2 where code = '" + string(row[2]) + "'";
MYSQL_ROW retorno;
mysql_init(&connection);
if (mysql_real_connect(&connection,HOST,USER,PASS,DB,0,NULL,0)){
if (mysql_query(&connection,query.c_str()))
cout << "Error: " << mysql_error(&conexao);
else{
// My "debugging" shows me at this point `row[2]` is already fubar
resp = mysql_store_result(&connection);
if (resp) return_row = mysql_fetch_row(resp);
mysql_free_result(resp);
}
mysql_close(&connection);
}else{
cout << "connection failed\n";
if (mysql_errno(&connection))
cout << "Error : " << mysql_errno(&connection) << " " << mysql_error(&connection);
}
return return_row;
}
And main() is an infinite loop basically like this:
int main( int argc, char* args[] ){
MYSQL_ROW row = NULL;
while (1) {
row = function1();
if(row != NULL) function2(row);
}
}
(variable and function names have been generalized to protect the innocent)
But after the 3rd or 4th call to function2, that only uses row for reading, row starts losing its value coming to a segfault error...
Anyone's got any ideas why? I'm not sure the amount of global variables in this code is any good, but I didn't design it and only got until tomorrow to fix and finish it, so workarounds are welcome!
Thanks!
Update: I misunderstood how mysql results are used. It looks like the row pointer array points to the results array which you free in function1() and then use it in function2() after it has been returned to the heap.
What you need to do is copy return_row[2] to a persistent string before freeing the results. Then pass that on to function2(). I see you doing something similar in function2() so you need to fix it there as well (though in your example you aren't doing anything with its return value).
Also, you are correct that free(row); is not the correct thing to do.
You should not be closing the connection or freeing the result set while you are processing a row returned by mysql_fetch_row.