SQLite3 WHERE clause C++ - c++

So I'm trying to make a login function that uses SQLite3, and what I want is the user to input his username and password and the program is going to search in the table for his username than his password and compare it, and if it matches he get access if don't he needs to input his credentials again. And this is how my function look right now:
void logIn(){
std::cout << "Username: "; std::cin >> user;
std::cout << "Password: "; std::cin >> psw;
//Open database
rc = sqlite3_open("Users.db", &db);
//create sql statement
sql = "SELECT Username FROM Users_info"
"WHERE Username='?';";
//Execute sql statement
sqlite3_prepare(db, sql, -1, &st, NULL);
sqlite3_bind_text(st, 1, user.c_str(), name.length(), SQLITE_TRANSIENT);
int rc = sqlite3_exec(db, sql, callback, (void*)data.c_str(), NULL);
//show SQL output
if (rc != SQLITE_OK) {
std::cout << "Error making Log In" ;
sqlite3_free(zErrMsg);
}
else
std::cout << "Log In succesfully" ;
sqlite3_close(db);
And as you can see in these lines I'm trying to use the WHERE clause
//create sql statement
sql = "SELECT Username FROM Users_info"
"WHERE Username='?';";
Because for example, in my table I have this "test" user and if I use this exact code in SQLite3 It's going to return the exact user that I want, and with that in mind, what I want is to somehow my program to compare the user input to the return of the sql code but I don't have any idea to do so. Hope this makes my doubt clear enough to be clarified.

Related

WHERE column = value, only work with INTEGER value

I use sqlite on a c++ project, but I have a problem when i use WHERE on a column with TEXT values
I created a sqlite database:
CREATE TABLE User( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(24))
When i try to get the value of the column with VARCHAR values, it doesn't work, and return me a STATUS_CODE 101 just after the sqlite3_step :
int res = 0;
sqlite3_stmt *request;
char *sqlSelection = (char *)"SELECT * FROM User WHERE name='bob' ";
int id = 0;
res = sqlite3_prepare_v2(db, sqlSelection, strlen(sqlSelection), &request, NULL);
if (!res){
while (res == SQLITE_OK || res == SQLITE_ROW){
res = sqlite3_step(request);
if (res == SQLITE_OK || res == SQLITE_ROW ){
id = sqlite3_column_int(request, 0);
printf("User exist %i \n",id);
}
}
sqlite3_finalize(request);
I also tried with LIKE but it also doesn't work
SELECT * FROM User WHERE name LIKE '%bob%'
But when I execute the same code but for an INTERGER value
SELECT * FROM User WHERE id=1
It work fine.
In DB Browser for SQLite all requests work fine.
To solve the problem I searched what status code 101 means.
Here is what they said.
(101) SQLITE_DONE
The SQLITE_DONE result code indicates that an operation has completed.
The SQLITE_DONE result code is most commonly seen as a return value
from sqlite3_step() indicating that the SQL statement has run to
completion. But SQLITE_DONE can also be returned by other multi-step
interfaces such as sqlite3_backup_step().
https://sqlite.org/rescode.html
So, you're getting 101 because there is no more result from SELECT SQL.
The solution was to replace the VARCHAR fields by TEXT.
SQLite for c++ seems to don't manage VARCHAR fields when they are used after the WHERE

Why does sqlite's delete always succeed, even when the row to delete does not exist?

After I create a table, insert data into it, and then delete a non-existent row, the operation succeeds even though the row does not exist. When I delete a row that actually exists, it succeeds as well and the row is actually deleted. Why doesn't an error occur when I try to delete a row that does not exist?
I am using sqlite3 with c++ on eclipse.
I've been working with some code found on the web, as well as my own.
Other operations, like SELECT and INSERT work fine. DELETE works when rows exist and even when they don't exist.
// Creating a table
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"NAME TEXT NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
// Inserting data
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " \
"VALUES (1, 'Paul', 32, 'California', 20000.00 ); " \
"INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " \
"VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); " \
"INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" \
"VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );" \
"INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" \
"VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );";
// Deleting (this is where the deletion should fail because there is no
ID 30)
rc = sqlite3_open("test.db", &db);
if( rc ) {
cout << "ERROR ----> " << zErrMsg << endl;
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL statement */
sql = "DELETE FROM COMPANY WHERE ID = 30";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if( rc != SQLITE_OK ) {
cout << "ERROR DELETING" << endl;
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Deletion operation done successfully\n");
}
sqlite3_close(db);
I expect the message "ERROR DELETING" to be displayed, but "Deletion operation done successfully" always displays, even when the ID deleted does not exist.
Why doesn't an error occur when I try to delete a row that does not exist?
Databases return number of rows affected (deleted in your case), they don't throw error, unless there is something wrong with the query.
The only guaranteed method to check if the row has been deleted is to execute a SELECT statement with the same conditions. Transaction control is an important factor here, i.e. what happens if something causes a rollback? Are you explicitly committing transactions, or allowing them to auto-commit? You shouldn't just rely on return codes for this. If you do a COUNT as well, then you will know for sure, at least within your session:
SELECT COUNT(*) FROM COMPANY WHERE ID = 30
If after the DELETE, the COUNT is 0, the row no longer exists.
Note: This approach will be helpful if you need concurrency, which will force you to use a different database engine (e.g. PostgreSQL).
I guess the answer is "this is by design".
If you need to know if zero (or more) rows were affected by the last DELETE statement (or INSERT or UPDATE for that matter) then you could look into using:
int sqlite3_changes(sqlite3*);
That will allow you to react accordingly if no rows were deleted.
See https://www.sqlite.org/c3ref/changes.html

MySQL c++ connector how to retrieve auto increment keys from insertion query

I am using mysql C++ connector. I have a table:
CREATE TABLE some_table
(
id INT NOT NULL AUTO_INCREMENT,
col1 INT,
col2 INT,
PRIMARY KEY ( id )
);
To insert multiple records in a query, I use:
INSERT INTO some_table
(col1, col2)
VALUES
(0, 1),
(2, 3),
(4, 5);
My question is: After the insertion, I would like to retrieve all the auto-generated ids. Is it possible that I could use a function in the c++ connector without creating another query?
For example, in the JDBC, the AUTO_INCREMENT column values can be retrieved by using following method.
stmt.executeUpdate(
"INSERT INTO autoIncTutorial (dataField) "
+ "values ('Can I Get the Auto Increment Field?')",
Statement.RETURN_GENERATED_KEYS);
//
// Example of using Statement.getGeneratedKeys()
// to retrieve the value of an auto-increment
// value
//
int autoIncKeyFromApi = -1;
rs = stmt.getGeneratedKeys();
if (rs.next()) {
autoIncKeyFromApi = rs.getInt(1);
} else {
// throw an exception from here
}
https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-usagenotes-last-insert-id.html
Any c++ connector alternatives?
Thanks
Last year I faced the same problem. The solution was to use the builtin LAST_INSERT_ID(). Following I changed the getting start example 2 to show how to use it:
//previous variable declarations and initialisation similar to the original example
driver = get_driver_instance();
con = driver->connect("tcp://127.0.0.1:3306", "root", "root");
con->setSchema("test_schema");
con->setAutoCommit(false);
stmt = con->createStatement();
stmt->execute("DROP TABLE IF EXISTS tbl__test1");
stmt->execute("DROP TABLE IF EXISTS tbl_test2");
const string createTbl1Statement = "CREATE TABLE `tbl__test1` ("
"`id` int(11) NOT NULL AUTO_INCREMENT,"
"`col_value` varchar(45) DEFAULT NULL,"
"PRIMARY KEY (`id`)"
") ENGINE=InnoDB DEFAULT CHARSET=latin1;";
const string createTbl2Statement = "CREATE TABLE `tbl_test2` ("
"`id` int(11) NOT NULL AUTO_INCREMENT,"
"`tbl_test1_id` int(11) NOT NULL,"
"`col_value` varchar(45) DEFAULT NULL,"
"PRIMARY KEY (`id`)"
") ENGINE=InnoDB DEFAULT CHARSET=latin1;";
stmt->execute(createTbl1Statement);
stmt->execute(createTbl2Statement);
pstmt = con->prepareStatement(
"INSERT INTO tbl__test1(col_value) VALUES ('abcde')");
pstmt->executeUpdate();
delete pstmt;
stmt->execute("SET #lastInsertId = LAST_INSERT_ID()");
delete stmt;
const string insertTbl2 = "INSERT INTO tbl_test2(tbl_test1_id, col_value)"
" VALUES (#lastInsertId, '1234')";
pstmt = con->prepareStatement(insertTbl2);
pstmt->executeUpdate();
delete pstmt;
con->commit();
delete con;
//remain code is like the example 2 from mysql site
About how safe is call LAST_INSERT_ID(), as stated in the mysql docs:
The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.
EDIT:
As given here:
With no argument, LAST_INSERT_ID() returns a 64-bit value representing the first automatically generated value successfully inserted for an AUTO_INCREMENT column as a result of the most recently executed INSERT statement.
Thus, the LAST_INSERT_ID returns the last generated id regardless the table where the new row was insert. If you need to insert several rows just call LAST_INSERT_ID immediately after insert each row such you want to get the key.
In the following code it is inserted 1 row in the table 1, get the generated key (returns '1') than that key is used for insert news 2 rows in the associated table 2. Than again it is inserted 1 new row in the table 1, get generated key again (returns '2') and inserted 2 news rows again in the table 2:
#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>
#include <cppconn/prepared_statement.h>
using namespace std;
int main(void) {
cout << endl;
cout << "Let's have MySQL count from 10 to 1..." << endl;
try {
sql::Driver *driver;
sql::Connection *con;
sql::Statement *stmt;
sql::PreparedStatement *pstmt1;
sql::PreparedStatement *pstmt2;
driver = get_driver_instance();
con = driver->connect("tcp://127.0.0.1:3306", "root", "root");
con->setSchema("test_schema");
con->setAutoCommit(false);
stmt = con->createStatement();
stmt->execute("DROP TABLE IF EXISTS tbl__test1");
stmt->execute("DROP TABLE IF EXISTS tbl_test2");
const string createTbl1Statement = "CREATE TABLE `tbl__test1` ("
"`id` int(11) NOT NULL AUTO_INCREMENT,"
"`col_value` varchar(45) DEFAULT NULL,"
"PRIMARY KEY (`id`)"
") ENGINE=InnoDB DEFAULT CHARSET=latin1;";
const string createTbl2Statement = "CREATE TABLE `tbl_test2` ("
"`id` int(11) NOT NULL AUTO_INCREMENT,"
"`tbl_test1_id` int(11) NOT NULL,"
"`col_value` varchar(45) DEFAULT NULL,"
"PRIMARY KEY (`id`)"
") ENGINE=InnoDB DEFAULT CHARSET=latin1;";
stmt->execute(createTbl1Statement);
stmt->execute(createTbl2Statement);
pstmt1 = con->prepareStatement(
"INSERT INTO tbl__test1(col_value) VALUES (?)");
pstmt1->setString(1, "abcde");
pstmt1->executeUpdate();
stmt->execute("SET #lastInsertId = LAST_INSERT_ID()");
const string insertTbl2 =
"INSERT INTO tbl_test2(tbl_test1_id, col_value)"
" VALUES (#lastInsertId, ?)";
pstmt2 = con->prepareStatement(insertTbl2);
pstmt2->setString(1, "child value 1");
pstmt2->executeUpdate();
pstmt2->setString(1, "child value 2");
pstmt2->executeUpdate();
pstmt1->setString(1, "xpto");
pstmt1->executeUpdate();
stmt->execute("SET #lastInsertId = LAST_INSERT_ID()");
pstmt2->setString(1, "child value 3");
pstmt2->executeUpdate();
pstmt2->setString(1, "child value 4");
pstmt2->executeUpdate();
con->commit();
delete stmt;
delete pstmt1;
delete pstmt2;
delete con;
} catch (sql::SQLException &e) {
cout << "# ERR: SQLException in " << __FILE__;
cout << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl;
cout << "# ERR: " << e.what();
cout << " (MySQL error code: " << e.getErrorCode();
cout << ", SQLState: " << e.getSQLState() << " )" << endl;
}
cout << endl;
return EXIT_SUCCESS;
}
The result is the 2 rows in table 1:
And 4 rows in the table 2 each one properly associated with the key in table 1:
So, the key point is to call LAST_INSERT_ID() after insert the new row with the generated key you need.

SQLite Stops at 252 Entries

So I've written some C++ code to select data from one database and INSERT it into another. There's over 50,000 entries in the source db. The table definitions for the destination database are as follows:
CREATE TABLE evidence_master
( id INTEGER NOT NULL,
person_id TEXT NOT NULL,
message TEXT,
eid INTEGER NOT NULL,
frequency INTEGER,
string TEXT,
CONSTRAINT id_pk PRIMARY KEY (id),
CONSTRAINT eid_fk FOREIGN KEY (eid) REFERENCES evidences(eid)
);
CREATE TABLE evidences
(
eid INTEGER NOT NULL,
evidence_name TEXT,
CONSTRAINT eid_pk PRIMARY KEY (eid)
);
The source database is the one found here: http://wing.comp.nus.edu.sg/SMSCorpus/
My problem is, when it gets to the 252nd entry, it claims it cannot open the database (EDIT: the destination database). Despite having performed the work on 251 entries successfully.
Why might this be happening?
EDIT: here's the code that selects from the source:
dbOpenFail=sqlite3_open("../../../db/smscorpus.db", &dbCorpus);
if(dbOpenFail)
{
cerr << "Can't open sms corpus: " << sqlite3_errmsg(dbCorpus);
}
if(sqlite3_prepare(dbCorpus,
queryCorpus.data(),
queryCorpus.length() + 1,
&preparedCorpusQuery, NULL) != SQLITE_OK)
{
cerr << "Failed to prepare corpus query: " << queryCorpus << zErrMsg << endl;
sqlite3_free(zErrMsg);
}
rowSelectCorpus=sqlite3_step(preparedCorpusQuery);
while(rowSelectCorpus != SQLITE_DONE) //need to go through 51652 messages from corpus
{
if(rowSelectCorpus == SQLITE_ROW)
{
personID = sqlite3_column_text(preparedCorpusQuery, 0); //get sender ID
messageContent = sqlite3_column_text(preparedCorpusQuery, 1); //get message itself
db_entry d1(string(reinterpret_cast<const char *>(personID)),
string(reinterpret_cast<const char *>(messageContent)));
insertResult = d1.insertIntoDatabase();
if(insertResult == -1)
{
return 0; //exit
}
}
rowSelectCorpus=sqlite3_step(preparedCorpusQuery);
}
Some C runtime libraries have a default limit of 256 open file handles per process.
Apparently, your program is opening the database for every INSERT operation, and forgets to close it.
You should open every database only once.

"delete from table" doesn't delete table?

I create "database.db" and everything goes ok but why wouldn't it delete the table at the end? Everytime i run it, i get "table already exist" error message on creating the table.
int main()
{
sqlite3 *db; //Database Handle
char *zErr;
int rc;
char *sql;
rc = sqlite3_open("database.db", &db);
if(rc)
{
cout << "Can't open database: " << sqlite3_errmsg(db) << endl;;
sqlite3_close(db);
exit(1);
}
sql = "create table test(PID int primary key, Name text)"; //sql query
rc = sqlite3_exec(db, sql, NULL, NULL, &zErr); //execute sql statement
if(rc != SQLITE_OK)
{
if (zErr != NULL)
{
cout << "SQL error: " << zErr << endl;
sqlite3_free(zErr);
}
}
else
{
sql = "insert into test values (1,'John')";
rc = sqlite3_exec(db, sql, NULL, NULL, &zErr);
sql = "insert into test values (2,'Smith')";
rc = sqlite3_exec(db, sql, NULL, NULL, &zErr);
}
//delete the table on exit.
rc = sqlite3_exec(db, "delete from test", NULL, NULL, &zErr);
sqlite3_close(db);
return 0;
}
Also, can the primary keys be auto-generated following the last greater key existing in database?
You need to use drop table. delete deletes rows in the table.
rc = sqlite3_exec(db, "drop table test", NULL, NULL, &zErr);
SQLite will auto-increment an integer field declared as the primary key. See here.
You use a DELETE command to delete rows of a table.
You use a DROP command to drop a whole table or other DB item.
To create the table you can also use CREATE TABLE IF NOT EXISTS if you aren't sure if it exists.
To get your auto generated row ids use: id INTEGER PRIMARY KEY in your CREATE TABLE command.
Use DROP TABLE.
From SQLite docs:
The usual algorithm is to give the newly created row a ROWID that is one larger than the largest ROWID in the table prior to the insert.
The DML "delete from test" does delete all the rows from the table test. If you wish to drop the table, try "delete table test" or "drop table" instead.