SQLite3 How to handle column index out of range - c++

Whenever I want to insert a type object (define below) in the database I get "error 25 : column index out of range".
Which means that the value I try to bind doesn't fit in the query right?
CREATE TABLE color (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
label TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS object (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
label TEXT NOT NULL,
color_id INTEGER,
position_x REAL,
position_y REAL,
position_z REAL,
distance REAL,
FOREIGN KEY(color_id) REFERENCES color(id)
);
-- few insert to populate color
INSERT INTO color(id,label) VALUES (1,'Black');
INSERT INTO color(id,label) VALUES (2,'White');
INSERT INTO color(id,label) VALUES (3,'Red');
INSERT INTO color(id,label) VALUES (4,'Lime');
INSERT INTO color(id,label) VALUES (5,'Blue');
INSERT INTO color(id,label) VALUES (6,'Yellow');
I tried putting the query using the CLI interface and this worked.
insert into object (label,color_id,position_x,position_y,position_z,distance) values ("can",2,0.2,0.2,0.2,1.2);
I checked the number of fields, the name of variables, my database is open when my class is created and close on destruction. And last colorIndex is an int that I retrieve from a function I already use to insert other element in a different table. I also printed the values and they are correct.
I guess I m missing something here. I can't see where else can be my mistake.
typedef struct Object{
std::string label;
std::string color;
float pos_x;
float pos_y;
float pos_z;
float distance;
} Object;
int VisionModel::getColorByLabel(std::string sColor){
int colorId;
query = "SELECT id FROM color WHERE label = (?)";
pStmt = nullptr;
int rc;
rc = sqlite3_prepare_v2(db,query.c_str(), -1, &pStmt, NULL);
if (rc != SQLITE_OK){
std::cout << "prepare getColorByLabel didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return colorId;
}
if (sqlite3_bind_text(pStmt, 1, sColor.c_str(), -1, NULL) != SQLITE_OK){
std::cout << "bind color label didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return colorId;
}
while ( (rc = sqlite3_step(pStmt)) == SQLITE_ROW) {
colorId = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
return colorId;
}
void VisionModel::createObject(Object object){
query = "INSERT INTO object (label,color_id,position_x,position_y,position_z,distance) VALUES (?,?,?,?,?,?)";
pStmt = nullptr;
int rc;
// get the index for given color
int colorIndex = -1;
if (!object.color.empty() && object.color != "0"){
colorIndex = getColorByLabel(object.color);
}
rc = sqlite3_prepare_v2(db,query.c_str(), -1, &pStmt, NULL);
if (rc != SQLITE_OK){
std::cout << "prepare createObject didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
if (sqlite3_bind_text(pStmt, 1, object.label.c_str(), -1, NULL) != SQLITE_OK){
std::cout << "bind object label didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
if (colorIndex != -1){
rc = sqlite3_bind_int(pStmt, 2, colorIndex);
if ( rc != SQLITE_OK){
std::cout << "bind object color index didn t went through" << std::endl;
std::cout << std::to_string(colorIndex) << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
}
if (sqlite3_bind_double(pStmt, 3, object.pos_x) != SQLITE_OK){
std::cout << "bind object pos x didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
if (sqlite3_bind_double(pStmt, 4, object.pos_y) != SQLITE_OK){
std::cout << "bind person object y didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
if (sqlite3_bind_double(pStmt, 5, object.pos_z) != SQLITE_OK){
std::cout << "bind person object z didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
if (sqlite3_bind_double(pStmt, 6, object.distance) != SQLITE_OK){
std::cout << "bind person object distance didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
if ((rc = sqlite3_step(pStmt)) != SQLITE_DONE) { /* 2 */
std::cout << "step didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return ;
}
sqlite3_finalize(pStmt);
}
Here is a minimal reproductible version of the code that you can copy paste and compile. Don't forget to create the database with the query above first.
compile : g++ -o main main.cpp -lsqlite3
code :
#include <string>
#include <iostream>
#include <sqlite3.h>
typedef struct Object
{
std::string label;
std::string color;
float pos_x;
float pos_y;
float pos_z;
float distance;
} Object;
class VisionModel
{
private:
sqlite3 *db;
const char *zErrMsg = 0;
std::string query;
sqlite3_stmt *pStmt;
public:
VisionModel()
{
connect();
}
~VisionModel()
{
close();
}
void connect()
{
int rc;
std::string db_file_path("./database.db");
rc = sqlite3_open(db_file_path.c_str(), &db);
if (rc)
{
std::cerr << "Can't open database" << sqlite3_errmsg(db) << std::endl;
std::cerr << "SQL code error : " << sqlite3_extended_errcode(db) << std::endl;
return;
}
};
void manageSQLiteErrors(sqlite3_stmt *pStmt)
{
std::cerr << "SQL error : " << sqlite3_errmsg(db) << std::endl;
std::cerr << "SQL code error : " << sqlite3_extended_errcode(db) << std::endl;
sqlite3_finalize(pStmt);
}
void close()
{
sqlite3_close(db);
};
int getColorByLabel(std::string sColor)
{
int colorId;
query = "SELECT id FROM color WHERE label = (?)";
pStmt = nullptr;
int rc;
rc = sqlite3_prepare_v2(db, query.c_str(), -1, &pStmt, NULL);
if (rc != SQLITE_OK)
{
std::cout << "prepare getColorByLabel didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return colorId;
}
if (sqlite3_bind_text(pStmt, 1, sColor.c_str(), -1, NULL) != SQLITE_OK)
{
std::cout << "bind color label didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return colorId;
}
while ((rc = sqlite3_step(pStmt)) == SQLITE_ROW)
{
colorId = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
return colorId;
}
void createObject(Object object)
{
query = "INSERT INTO object (label,color_id,position_x,position_y,position_z,distance) VALUES (?,?,?,?,?,?)";
pStmt = nullptr;
int rc;
// get the index for given color
int colorIndex = -1;
if (!object.color.empty() && object.color != "0")
{
colorIndex = getColorByLabel(object.color);
}
rc = sqlite3_prepare_v2(db, query.c_str(), -1, &pStmt, NULL);
if (rc != SQLITE_OK)
{
std::cout << "prepare createObject didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return;
}
if (sqlite3_bind_text(pStmt, 1, object.label.c_str(), -1, NULL) != SQLITE_OK)
{
std::cout << "bind object label didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return;
}
if (colorIndex != -1)
{
rc = sqlite3_bind_int(pStmt, 2, colorIndex);
if (rc != SQLITE_OK)
{
std::cout << "bind object color index didn t went through" << std::endl;
std::cout << std::to_string(colorIndex) << std::endl;
manageSQLiteErrors(pStmt);
return;
}
}
if (sqlite3_bind_double(pStmt, 3, object.pos_x) != SQLITE_OK)
{
std::cout << "bind object pos x didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return;
}
if (sqlite3_bind_double(pStmt, 4, object.pos_y) != SQLITE_OK)
{
std::cout << "bind person object y didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return;
}
if (sqlite3_bind_double(pStmt, 5, object.pos_z) != SQLITE_OK)
{
std::cout << "bind person object z didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return;
}
if (sqlite3_bind_double(pStmt, 6, object.distance) != SQLITE_OK)
{
std::cout << "bind person object distance didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return;
}
if ((rc = sqlite3_step(pStmt)) != SQLITE_DONE)
{ /* 2 */
std::cout << "step didn t went through" << std::endl;
manageSQLiteErrors(pStmt);
return;
}
sqlite3_finalize(pStmt);
}
};
int main()
{
Object obj = {"apple", "Blue", 0.1, 0.2, 0.1, 2.0};
VisionModel vm;
vm.createObject(obj);
}

If you used your debugger to show the value of query here, the problem becomes very easy to see:
rc = sqlite3_prepare_v2(db, query.c_str(), -1, &pStmt, NULL);
You will discover that this query is not really the INSERT statement that you think it is.
This is because just a few lines earlier:
colorIndex = getColorByLabel(object.color);
And this ends up clobbering the query variable, setting it to a "SELECT" with just one ? placeholder.
You can use this as a golden opportunity to learn how to use a debugger, which makes solving these Scooby-Doo mysteries simple. Try to use the shown program in your debugger, exactly as is, setting a breakpoint at the sqlite3_prepare_v2 line in createObject, and then inspecting what's in the query, and having your own "Eureka!" moment.

Related

Creating temporary table while still accessing other tables

I've got 2 or more databases ATTACHed to one SQLite database connection. Each database consists of 3 tables. To have better access for searching/filtering, I create a huge temporary table over all tables and databases like this:
"CREATE TEMPORARY TABLE temp_table AS "
"SELECT * FROM pb.name_table "
"LEFT JOIN pb.phone_table ON (pb.name_table.id=pb.phone_table.id) " \
"LEFT JOIN pb.email_table ON (pb.name_table.id=pb.email_table.id) " \
"UNION SELECT * FROM name_table " \<br>
"LEFT JOIN phone_table ON (name_table.id=phone_table.id) " \
"LEFT JOIN email_table ON (name_table.id=email_table.id);";
If something changes in a table, I have to recreate the temporary table. With an increasing amount of data, creating a temporary table takes some time, but since I'm having continuous read access to the table, my idea was as follows:
Create a thread, which creates a second temp table in background
Block access for the reading clients
DROP first temp table
Rename the second temp table
The problem is now: Creating a temporary table is a write access to the database, which blocks automatically all reading threads also.
Has anyone a good idea how I can handle this? I need read access while recreating the temporary table.
As long as all threads are part of the same program, you have control over all database connections.
So you can write the data in a completely separate database, and ATTACH is quickly later.
Thanks a lot for your answer. I changed my code and found out that I need a new database connection (sqlite3_open) in the working thread not to block the other thread. Also creating a TEMPORARY TABLE in the attached "temporary database" was not possible, because creating a temporary table doesn't allow a qualifier (like: x.temp_table), so I had to create a real table which consumes a lot of memory in our flash file system (which is not allowed).
But wait! I've got an idea
(2 hours later)
I did it! I open an empty database and attach alll relevant databases to the connection. I create a temporary table "in" the empty database which consumes no memory in flash, because it's temporary.
When I have to create a new table, I open another empty database and attach the relevant databases to the connection. When the operation is finished, I switch the old and the new connection. Code as follows:
#include <iostream>
#include "dbAccess.h"
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
bool inProgress = true;
DWORD WINAPI createTempTableThread(void *param);
int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
cout << "*";
return 0;
}
int main(void)
{
sqlite3* db = NULL;
HANDLE hThreadHandle = NULL;
CdbAccess *dba = new CdbAccess();
int i = 0;
db = dba->dbaConnect();
dba->dbaSetDatabase(db);
cout << "INFO: Creating initial temporary table. " << endl;
sqlite3_exec(dba->dbaGetDatabase(), "CREATE TEMPORARY TABLE temp_table AS " \
"SELECT * FROM pb.name_table " \
"LEFT JOIN pb.phone_table ON (pb.name_table.id=pb.phone_table.id) " \
"LEFT JOIN pb.email_table ON (pb.name_table.id=pb.email_table.id) " \
"UNION SELECT * FROM intern.name_table " \
"LEFT JOIN intern.phone_table ON (intern.name_table.id=intern.phone_table.id) " \
"LEFT JOIN intern.email_table ON (intern.name_table.id=email_intern.table.id);", NULL, NULL, NULL);
cout << "INFO: Creating initial temporary table finished. " << endl;
while(1)
{
hThreadHandle = CreateThread(0, 0, createTempTableThread, dba, 0, 0);
while(inProgress)
{
sqlite3_exec(dba->dbaGetDatabase(), "SELECT * FROM name_table WHERE id LIKE 1;", callback, NULL, NULL);
}
for(i = 0; i < 5; i++)
{
sqlite3_exec(dba->dbaGetDatabase(), "SELECT * FROM name_table WHERE id LIKE 2;", callback, NULL, NULL);
}
inProgress = true;
CloseHandle(hThreadHandle);
}
dba->dbaDisconnect();
return 0;
}
CdbAccess::CdbAccess()
{
hSemaphore = CreateSemaphore(NULL, 1, 1, 0);
}
CdbAccess::~CdbAccess()
{
}
sqlite3 *CdbAccess::dbaConnect()
{
sqlite3 *db;
static int num = 1;
int err = SQLITE_OK;
string attach = "ATTACH DATABASE \"";
string internal = "cbInternal.db";
if(num == 1)
{
cout << endl << "INFO: cbTemp1.db";
err = sqlite3_open("cbTemp1.db", &db);
num = 2;
}
else
{
cout << endl << "INFO: cbTemp2.db";
err = sqlite3_open("cbTemp2.db", &db);
num = 1;
}
if(err == SQLITE_OK)
{
cout << endl << "INFO: Temp database opened.";
err = sqlite3_exec(db, "ATTACH DATABASE \"cbInternal.db\" AS intern;", NULL, NULL, NULL);
if(err == SQLITE_OK)
{
cout << endl << "INFO: Internal database attached.";
err = sqlite3_exec(db, "ATTACH DATABASE \"0123456789.db\" AS pb;", NULL, NULL, NULL);
if(err == SQLITE_OK)
{
cout << endl << "INFO: Phone book attached.";
}
else
{
cout << endl << "ERROR: Attaching phone book: " << sqlite3_errmsg(db);
}
}
else
{
cout << endl << "ERROR: Attaching internal database: " << sqlite3_errmsg(db);
}
}
else
{
cout << endl << "ERROR: Opening database: " << sqlite3_errmsg(db);
}
return db;
}
int CdbAccess::dbaDisconnect(void)
{
int err = SQLITE_OK;
err = sqlite3_exec(db, "DETACH DATABASE pb;", NULL, NULL, NULL);
if(err == SQLITE_OK)
{
cout << endl << "INFO: Phone book detached.";
err = sqlite3_exec(db, "DETACH DATABASE intern;", NULL, NULL, NULL);
if(err == SQLITE_OK)
{
cout << endl << "INFO: Internal database detached.";
err = sqlite3_close(db);
if(err == SQLITE_OK)
{
cout << endl << "INFO: Database connection closed.";
}
else
{
cout << endl << "ERROR: Could not close database: " << sqlite3_errmsg(db);
}
}
else
{
cout << endl << "ERROR: Could not detach internal database: " << sqlite3_errmsg(db);
}
}
else
{
cout << endl << "ERROR: Could not detach phone book: " << sqlite3_errmsg(db);
}
return err;
}
sqlite3* CdbAccess::dbaGetDatabase(void)
{
return db;
}
void CdbAccess::dbaSetDatabase(sqlite3 * sqldb)
{
db = sqldb;
}
int CdbAccess::dbaGetTempTableAccess(void)
{
cout << endl << "INFO: Access requested.";
WaitForSingleObject(hSemaphore, INFINITE);
return 0;
}
int CdbAccess::dbaReleaseTempTableAccess(void)
{
cout << endl << "INFO: Access released.";
ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
DWORD WINAPI createTempTableThread(void *param)
{
int err = SQLITE_OK;
CdbAccess *d = (CdbAccess *)param;
sqlite3 *db;
cout << endl << "INFO: createTempTable: IN";
inProgress = true; // global variable for test porpose only
db = d->dbaConnect();
if(db != NULL)
{
cout << endl << "Thread: INFO: Creating temporary table. ";
err = sqlite3_exec(db, "CREATE TEMPORARY TABLE temp_table AS " \
"SELECT * FROM pb.name_table " \
"LEFT JOIN pb.phone_table ON (pb.name_table.id=pb.phone_table.id) " \
"LEFT JOIN pb.email_table ON (pb.name_table.id=pb.email_table.id) " \
"UNION SELECT * FROM intern.name_table " \
"LEFT JOIN intern.phone_table ON (intern.name_table.id=intern.phone_table.id) " \
"LEFT JOIN intern.email_table ON (intern.name_table.id=intern.email_table.id);", NULL, NULL, NULL);
}
if(err != SQLITE_OK)
{
cout << endl << "Thread: ERROR: Creating temporary table: " << sqlite3_errmsg(db);
}
else
{
cout << endl << "Thread: INFO: Creating temporary table finished. ";
}
d->dbaSetDatabase(db);
inProgress = false; // global variable for test porpose only
cout << endl << "Thread: INFO: createTempTable: OUT";
return 0;
}

SQLite, SELECT and max function

Why does the following code gives me the different result when using max function? I thought that it should return SQLITE_DONE in this case too.
#include <boost/scope_exit.hpp>
#include <sqlite3.h>
#include <cstdlib>
#include <iostream>
int main()
{
sqlite3* db;
int rc = sqlite3_open(":memory:", &db);
BOOST_SCOPE_EXIT_ALL(&db)
{
sqlite3_close(db);
};
if (rc != SQLITE_OK)
{
std::cerr << "Can't open database."
<< " Error code: " << rc
<< " Error description: " << sqlite3_errmsg(db);
return EXIT_FAILURE;
}
char* errMsg;
rc = sqlite3_exec(db, "CREATE TABLE foo (bar INTEGER)", NULL, NULL, &errMsg);
if (rc != SQLITE_OK)
{
std::cerr << "Can't create \"foo\" table."
<< " Error code: " << rc
<< " Error description: " << errMsg;
sqlite3_free(errMsg);
return EXIT_FAILURE;
}
{
sqlite3_stmt* stmt;
rc = sqlite3_prepare_v2(db, "SELECT bar FROM foo WHERE bar = 1", -1, &stmt, NULL);
if (rc != SQLITE_OK)
{
std::cerr << "Can't prepare SELECT statement."
<< " Error code: " << rc
<< " Error description: " << sqlite3_errmsg(db);
return EXIT_FAILURE;
}
BOOST_SCOPE_EXIT_ALL(&stmt)
{
sqlite3_finalize(stmt);
};
rc = sqlite3_step(stmt);
std::cout << rc << std::endl; // 101 -- SQLITE_DONE
}
{
sqlite3_stmt* stmt;
rc = sqlite3_prepare_v2(db, "SELECT max(bar) FROM foo WHERE bar = 1", -1, &stmt, NULL);
if (rc != SQLITE_OK)
{
std::cerr << "Can't prepare SELECT statement."
<< " Error code: " << rc
<< " Error description: " << sqlite3_errmsg(db);
return EXIT_FAILURE;
}
BOOST_SCOPE_EXIT_ALL(&stmt)
{
sqlite3_finalize(stmt);
};
rc = sqlite3_step(stmt);
std::cout << rc << std::endl; // 100 -- SQLITE_ROW
}
}
Thanks in advance.
When you are using GROUP BY, you get one group for each unique value of the grouped column(s).
If there does not exist any row to be grouped, there are no groups, and the query does not return any rows.
When you are using an aggregate function like max() without GROUP BY, the entire table becomes a single group.
This happens even if the table is empty, i.e., you then get a single group that aggregates over the empty set.
If you don't want to get a result when there are not bar = 1 rows, add GROUP BY bar.

highscore only refreshing on launch sqlite

I have a highscore system implemented using SQLite for a c++ shooter ive made but it only refreshes when I relaunch the game, I think this is because it only re- sorts from largest to smallest on game launch. I don't know why this is, it does seem to record all game scores as it should, but I think it is only ordering them on launch. How could I make it so it re-orders before it prints to screen. Here is the code
in main.cpp
case eHIGH_SCORES:
DrawString( "HIGHSCORES", iScreenWidth * 0.38, iScreenHeight * 0.95);
DrawString( "PRESS C TO CLOSE", iScreenWidth * 0.32, iScreenHeight * 0.1);
DatabaseMaker DatabaseMaker("MyDatabase.db");
DatabaseMaker.CreateDatabase();
DatabaseMaker.CreateTable("HIGHSCOREST");
if (scorer<1)
{
DatabaseMaker.InsertRecordIntoTable("HIGHSCOREST",scoreArray);
scorer++;
for (int i=0; i<10; i++)
{
scoreArray[i]=0;
}
}
DatabaseMaker.RetrieveRecordsFromTable("HIGHSCOREST");
DatabaseMaker.databaseprinter();
then in the Database maker class I have
DatabaseMaker::DatabaseMaker(std::string HighScoresT)
{
this->HighScores = HighScoresT;
myDatabase = NULL;
}
int DatabaseMaker::Callback(void* notUsed, int numRows, char **data, char **columnName)
{
for(int i = 0; i < numRows; i++)
{
std::cout << columnName[i] << ": " << data[i] << std::endl;
holder.push_back(atoi(data[i]));
}
return 0;
}
void DatabaseMaker::databaseprinter()
{
for (int i = 0; i < 10; i++)
{
itoa(holder[i], displayHolder, 10);
DrawString(displayHolder, 780/2, 700-(50*i));
}
}
void DatabaseMaker::CreateDatabase()
{
int errorCode = sqlite3_open(HighScores.c_str(), &myDatabase);
if(errorCode == SQLITE_OK)
{
std::cout << "Database opened successfully." << std::endl;
}
else
{
std::cout << "An error has occured." << std::endl;
}
}
void DatabaseMaker::CreateTable(std::string HIGHSCOREST)
{
char* errorMsg = NULL;
std::string sqlStatement;
sqlStatement = "CREATE TABLE HIGHSCOREST(" \
"ID INT," \
"SCORE INT);";
sqlite3_exec(myDatabase, sqlStatement.c_str(), Callback, 0, &errorMsg);
if(errorMsg != NULL)
{
std::cout << "Error message: " << errorMsg << std::endl;
}
}
void DatabaseMaker::InsertRecordIntoTable(std::string HIGHSCOREST,int (&scoreArray)[10] )
{
char*errorMsg = NULL;
std::string sqlStatement;
int x=5;
for(int i=0;i<10;i++)
{
std::string scorestring;
scorestring = std::to_string(scoreArray[i]);
sqlStatement = "INSERT INTO HIGHSCOREST (ID,SCORE)" \
"VALUES (1,"+scorestring+");";
sqlite3_exec(myDatabase, sqlStatement.c_str(), Callback, 0, &errorMsg);
if(errorMsg != NULL)
{
std::cout << "Error message: " << errorMsg << std::endl;
}
}
}
void DatabaseMaker::RetrieveRecordsFromTable(std::string HIGHSCOREST)
{
char* errorMsg = NULL;
std::string sqlStatement;
sqlStatement = "SELECT SCORE from HIGHSCOREST order by SCORE desc;";
sqlite3_exec(myDatabase, sqlStatement.c_str(), Callback, 0, &errorMsg);
if(errorMsg != NULL)
{
std::cout << "Error message: " << errorMsg << std::endl;
}
sqlite3_close(myDatabase);
}
DatabaseMaker::~DatabaseMaker()
{
}
Well, maybe try to move sqlite3_close statement off the RetrieveRecordsFromTable member function, maybe to destructor or seperate function?
Since you close connection to database after calling it for first time (and this might be a reason the problem is fixed by rerunning your application).
Also, consider changing this DatabaseMaker DatabaseMaker("MyDatabase.db"); into this: DatabaseMaker databaseMaker("MyDatabase.db");, otherwise you might have problem defining another DatabaseMaker in same scope.

Numerical result out of range

I have a program that creates semaphore. But when i try to use SETALL, i get the error in errno as
Numerical result out of range
if((temp_semid = semget(IPC_PRIVATE, 250, 0666 | IPC_CREAT)) != -1)
{
semun arg;
ushort array[100];
memset(array,0, 100);
arg.array = array;
if(semctl(temp_semid, 0, SETALL, arg) == -1){
std::cout << " failed to setall semaphore" << std::endl;
std::cout << strerror(errno) << std::endl;
break;
}
}
What is wrong?
This worked.
if((temp_semid = semget(IPC_PRIVATE, 250, 0666 | IPC_CREAT)) != -1)
{
semun arg;
ushort array[100] = { 0 };
arg.array = array;
if(semctl(temp_semid, 0, SETALL, arg) == -1){
std::cout << " failed to setall semaphore" << std::endl;
std::cout << strerror(errno) << std::endl;
break;
}
}
i was using memset in the wrong way. thanks to hmjd for pointing that out.

FOREIGN KEY example: The return errmsg of sqlite3_errmsg() for using sqlite3_exec() is not the same as for using sqlite3_step() and sqlite3_bind()

Two tables (Teachers and Students) are created while the Id in Students is referenced to the TeacherId in Teachers using FOREIGN KEY. If we use two different methods to insert the values into Students:
1. sqlite3_exec();
2. sqlite3_bind();
The return message from sqlite3_errmsg() is not the same using these two methods:
1. sqlite3_exec(): return "foreign key constraints failed";
2. sqlite3_bind(): return "SQL logic error or missing database";
The message from sqlite3_errmsg() for sqlite3_exec() is more clearer than that for sqlite3_bind();
However, sqlite3_bind() is more convenient and efficient to insert values compared to sqlite3_exec();
My question: How to get a clearer returned error message for sqlite3_bind()?
The following is the full codes:
#include <string.h>
#include <stdio.h>
#include <iostream>
#include "sqlite3.h"
using namespace std;
sqlite3* db;
int first_row;
// callback function;
int select_callback(void *p_data, int num_fields, char **p_fields, char **p_col_names)
{
int i;
int* nof_records = (int*) p_data;
(*nof_records)++;
// first_row was defined in <select_stmt> function;
// if first_row == 1, print the first row
// and then set first_row = 0 to avoid the subsequent execution for the following rows.
if (first_row == 1)
{
first_row = 0;
for (i=0; i < num_fields; i++)
{ printf("%20s", p_col_names[i]);
}
printf("\n");
for (i=0; i< num_fields*20; i++)
{ printf("=");
}
printf("\n");
}
for(i=0; i < num_fields; i++)
{ if (p_fields[i])
{ printf("%20s", p_fields[i]);
}
else
{ printf("%20s", " ");
}
}
printf("\n");
return 0;
}
// With callback function;
void select_stmt(const char* stmt)
{ char *errmsg;
int ret;
int nrecs = 0;
first_row = 1;
ret = sqlite3_exec(db, stmt, select_callback, &nrecs, &errmsg);
if(ret!=SQLITE_OK)
{ printf("Error in select statement %s [%s].\n", stmt, errmsg);
}
else
{ printf("\n %d records returned.\n", nrecs);
}
}
// Without callback function;
void sql_stmt(const char* stmt)
{ char *errmsg;
int ret;
ret = sqlite3_exec(db, stmt, 0, 0, &errmsg);
if(ret != SQLITE_OK)
{ printf("Error in statement: %s [%s].\n", stmt, errmsg);
}
}
//////////////////////////////////////// Main /////////////////////////////////
int main()
{ cout << "sqlite3_open("", &db): " << sqlite3_open("./shcool.db", &db) << endl;
if(db == 0)
{ printf("\nCould not open database.");
return 1;
}
char *errmsg;
int result;
result = sqlite3_exec ( db,
"Drop TABLE IF EXISTS Teachers", // stmt
0,
0,
&errmsg
);
if ( result != SQLITE_OK )
{ cout << "\nCould not prepare statement: Drop TABLE: " << result << endl;
cout << "errmsg: " << errmsg << endl;
return 1;
}
result = sqlite3_exec ( db,
"Drop TABLE IF EXISTS Students", // stmt
0,
0,
&errmsg
);
if ( result != SQLITE_OK )
{ cout << "\nCould not prepare statement: Drop TABLE: " << result << endl;
cout << "errmsg: " << errmsg << endl;
return 1;
}
// CREATE TABLE Teachers;
sql_stmt("CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text,Age integer NOT NULL)");
//////////////////////////// insert values into Teachers; /////////////////////////////////
sqlite3_stmt *stmt;
sqlite3_prepare(db, "PRAGMA foreign_keys = ON;", -1, &stmt, 0);
if ( sqlite3_prepare
( db,
"insert into Teachers values (:Id,:Name,:Age)", // stmt
-1, // If than zero, then stmt is read up to the first nul terminator
&stmt,
0 // Pointer to unused portion of stmt
)
!= SQLITE_OK )
{ printf("\nCould not prepare statement.");
return 1;
}
int index1, index2, index3;
index1 = sqlite3_bind_parameter_index(stmt, ":Id");
index2 = sqlite3_bind_parameter_index(stmt, ":Name");
index3 = sqlite3_bind_parameter_index(stmt, ":Age");
cout << index1 << endl;
cout << index2 << endl;
cout << index3 << endl;
printf("\nThe statement has %d wildcards\n", sqlite3_bind_parameter_count(stmt));
int id[] = {1, 2, 3};
string name[] = {"Zhang", "Hou", "Liu"};
int age[] = {28, 29, 31};
for ( int i = 0; i != 3; i++ )
{ if (sqlite3_bind_int
(stmt,
index1, // Index of wildcard
id[i]
)
!= SQLITE_OK)
{ printf("\nCould not bind sqlite3_bind_int.\n");
return 1;
}
if (sqlite3_bind_text
( stmt,
index2, // Index of wildcard
name[i].c_str(),
strlen(name[i].c_str()),
SQLITE_STATIC
)
!= SQLITE_OK)
{ printf("\nCould not bind sqlite3_bind_text.\n");
return 1;
}
if (sqlite3_bind_int
( stmt,
index3, // Index of wildcard
age[i]
)
!= SQLITE_OK)
{ printf("\nCould not bind sqlite3_bind_int.\n");
return 1;
}
// execute sqlite3_step(), and check the state of the statement
if (sqlite3_step(stmt) != SQLITE_DONE)
{ printf("\nCould not step (execute) stmt.\n");
return 1;
}
// reset the statement if you want to continue the sqlite3_bind().
cout << "sqlite3_reset(stmt): " << sqlite3_reset(stmt) << endl;
}
//////////////////////////// insert values into Students; /////////////////////////////////
// CREATE TABLE Students;
sql_stmt("CREATE TABLE Students (Id integer PRIMARY KEY, TeacherId integer, FOREIGN KEY(TeacherId) REFERENCES Teachers(id) )" );
//////// Method 1: use sqlite3_exec to insert values; ////////////
sql_stmt("INSERT INTO Students Values (0, 1)" );
sql_stmt("INSERT INTO Students Values (1, 2)" );
sql_stmt("INSERT INTO Students Values (2, 9)" );
cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;
//////// Method 2: use sqlite3_bind to insert values; ////////////
result = sqlite3_exec ( db,
"Drop TABLE IF EXISTS Students", // stmt
0,
0,
&errmsg
);
if ( result != SQLITE_OK )
{ cout << "\nCould not prepare statement: Drop TABLE: " << result << endl;
cout << "errmsg: " << errmsg << endl;
return 1;
}
// CREATE TABLE Students;
sql_stmt("CREATE TABLE Students (Id integer PRIMARY KEY, TeacherId integer, FOREIGN KEY(TeacherId) REFERENCES Teachers(id) )" );
if ( sqlite3_prepare
( db,
"insert into Students values ( :Id,:TeacherId )", // stmt
-1, // If than zero, then stmt is read up to the first nul terminator
&stmt,
0 // Pointer to unused portion of stmt
)
!= SQLITE_OK )
{ printf("\nCould not prepare statement.");
return 1;
}
index1 = sqlite3_bind_parameter_index(stmt, ":Id");
index2 = sqlite3_bind_parameter_index(stmt, ":TeacherId");
cout << index1 << endl;
cout << index2 << endl;
printf("\nThe statement has %d wildcards\n", sqlite3_bind_parameter_count(stmt));
int studentId[] = {0, 1, 2};
/// if the FOREIGN KEY works, the teacherId should not be 9;
int teacherId[] = {1, 2, 9};
for ( int i = 0; i != 3; i++ )
{ if (sqlite3_bind_int
(stmt,
index1, // Index of wildcard
studentId[i]
)
!= SQLITE_OK)
{ printf("\nCould not bind sqlite3_bind_int.\n");
return 1;
}
if (sqlite3_bind_int
( stmt,
index2, // Index of wildcard
teacherId[i]
)
!= SQLITE_OK)
{ printf("\nCould not bind sqlite3_bind_int.\n");
cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;
return 1;
}
// execute sqlite3_step(), and check the state of the statement
// cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;
if ( result = sqlite3_step(stmt) != SQLITE_DONE )
{ printf("\nCould not step (execute) stmt.\n");
cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;
cout << "result: " << result << endl;
return 1;
}
cout << "result: " << result << endl;
// reset the statement if you want to continue the sqlite3_bind().
cout << "sqlite3_reset(stmt): " << sqlite3_reset(stmt) << endl;
}
printf("\n");
// Print all;
select_stmt("select * from Teachers");
select_stmt("select * from Students");
sqlite3_close(db);
return 0;
}
It's recommended to use sqlite3_prepare_v2 instead of sqlite3_prepare. Excerpt:
The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are recommended for all new programs. The two older interfaces are retained for backwards compatibility, but their use is discouraged. In the "v2" interfaces, the prepared statement that is returned (the sqlite3_stmt object) contains a copy of the original SQL text. This causes the sqlite3_step() interface to behave differently in three ways:
[Point 1 omitted]
2. When an error occurs, sqlite3_step() will return one of the detailed error codes or extended error codes. The legacy behavior was that sqlite3_step() would only return a generic SQLITE_ERROR result code and the application would have to make a second call to sqlite3_reset() in order to find the underlying cause of the problem. With the "v2" prepare interfaces, the underlying reason for the error is returned immediately.
[Point 3 omitted]