C change sqlite prepare_stmt - c

I have C code that creates different tables based on data being encountered in a separate operation. As a further complication, some of the data needs to be inserted into the same table as other data, and can be encountered in any order. As the data does have common indices, I am handling this using indices and UPSERT statements. Below is a simple example of the setup. The prepared statement doesn't seem to be updating as I am only getting partial insertions or none at all. I think this is related to some memory allocation on the statement?
// System includes
#include <stdlib.h>
#include <stdio.h>
// Local includes
#include <sqlite3.h>
// Function to create the insertion string for sqlite
char* insert_string(int flag)
{
if(flag==1)
{
return("INSERT INTO test(?, ?, ?, ?, ?) ON CONFLICT (a,b) DO UPDATE SET c=excluded.c, d=excluded.d");
}
else if(flag==2)
{
return("INSERT INTO test(?, ?, ?, ?, ?) ON CONFLICT (a,b) DO UPDATE SET e=excluded.e");
}
}
// Function to create tables based on an integer flag
void create_table(int flag, sqlite3* sqldb)
{
if(flag==1)
{
sqlite3_exec(sqldb, "CREATE TABLE IF NOT EXISTS test(a integer, b integer, c real, d real, e real)", NULL, NULL, NULL);
sqlite3_exec(sqldb, "CREATE UNIQUE INDEX IF NOT EXISTS sqldb_idx ON test(a,b)", NULL, NULL, NULL);
}
else if(flag==2)
{
sqlite3_exec(sqldb, "CREATE TABLE IF NOT EXISTS test(a integer, b integer, c real, d real, e real)", NULL, NULL, NULL);
sqlite3_exec(sqldb, "CREATE UNIQUE INDEX IF NOT EXISTS sqldb_idx ON test(a,b)", NULL, NULL, NULL);
}
}
int main()
{
// Initialize database
sqlite3 *sqldb;
int sql_rc;
sqlite3_stmt* sql_stmt;
sqlite3_open("test.db", &sqldb);
// Loop over some integer flags
for(int i=1; i<3; i++)
{
// Create the table and begin the transaction
create_table(i, sqldb);
sqlite3_exec(sqldb, "BEGIN TRANSACTION;", NULL, NULL, NULL);
// Prepare the insertion statement
sqlite3_prepare_v2(sqldb, insert_string(i), -1, &sql_stmt, NULL);
// Insert a different amount of data depending on the flag
sqlite3_bind_int(sql_stmt, 1, 1);
sqlite3_bind_int(sql_stmt, 2, 2);
if(i==1)
{
sqlite3_bind_double(sql_stmt,3,1.0);
sqlite3_bind_double(sql_stmt,4,2.0);
}
else if(i==2)
{
sqlite3_bind_double(sql_stmt,5,3.0);
}
sqlite3_step(sql_stmt);
sqlite3_reset(sql_stmt);
// End the transaction
sqlite3_exec(sqldb, "END TRANSACTION;", NULL, NULL, NULL);
}
// Finalize and close
sqlite3_finalize(sql_stmt);
sqlite3_close(sqldb);
}

The SQL you're attempting to compile isn't valid. This will fix it:
if(flag==1)
{
return("INSERT INTO test VALUES(?, ?, ?, ?, ?) ON CONFLICT (a,b) DO UPDATE SET c=excluded.c, d=excluded.d");
}
else if(flag==2)
{
return("INSERT INTO test VALUES(?, ?, ?, ?, ?) ON CONFLICT (a,b) DO UPDATE SET e=excluded.e");
}
Note the added VALUES in the SQL strings.
I'd also highly recommend checking all of the outputs of sqlite3_ calls, even in test code like this. Doing so will show that without the change here, the first call to sqlite3_prepare_v2 fails with a SQLITE_ERROR, showing a problem with the SQL itself.

Related

How to pass Sqlite query values as variables?

My code is like this...
char dis[20];
int tc,tac,trc;
puts("Enter the data:\n");
puts("District : ");
while((getchar())!='\n');
fgets(dis,20,stdin);
puts("Total Cases : ");
scanf("%d",&tc);
puts("Total Active Cases : ");
scanf("%d",&tac);
puts("Total Recovered Cases : ");
scanf("%d",&trc);
sql = "INSERT INTO COV VALUES (dis,tc,tac,trc);"; //won't work
sql = "INSERT INTO COV VALUES ('abc',1,1,0);"; //works
database = sqlite3_exec(db, sql,0,0,0);
I want to save the values obtained from user in sqlite database but I can't do it as shown below.
It works if I just pass the exact value (i.e. during compile time).
How can I send values computed during runtime execution to sqlite database?
Have a look to the Sqlite C interface documentation.
Assuming you have a table defined like this:
CREATE TABLE COV (id PRIMARY KEY, dis VARCHAR, tc INTEGER, tac INTEGER, trc INTEGER);
You need to bind your parameters with specific bind API to prevent SQL injection.
Prepare your INSERT string using ?N template:
char sql[512];
snprintf(sql, sizeof(sql), "INSERT INTO COV(dis, tc, tac, trc) VALUES (?1,?2,?3,?4);");
Then bind your program variables with the corresponding parameter:
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, sql, sizeof(sql), &stmt, NULL);
sqlite3_bind_text(stmt, 1, dis, 20, NULL);
sqlite3_bind_int(stmt, 2, tc);
sqlite3_bind_int(stmt, 3, tac);
sqlite3_bind_int(stmt, 4, trc);
ret = sqlite3_step(stmt);
if (ret == SQLITE_DONE)
printf("record inserted!\n");
else
printf("Error: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt);
You need to put the actual values in using sprintf
char sqlscript[128];
Then, since you are using sql to send to the DB, assign sqlscript to sql first
sql = sqlscript;
sprintf(sql, "INSERT INTO COV VALUES('%s', %d, %d, %d);", dis, tc, tac, trc);

Swift - SQLite connection code explanation

I am currently trying to connect my iOS app with SQLite database I have on my computer locally, and while I have found a series of codes that work, I would like to understand how they work.
The below is from the post
Use sqlite3_exec to perform SQL (e.g. create table).
if sqlite3_exec(db, "create table if not exists test (id integer primary key autoincrement, name text)", nil, nil, nil) != SQLITE_OK {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("error creating table: \(errmsg)")
}
I understand the if statement but why is there 3 nils in the parameter and where is sqlite3_exec coming from? is it bundled with C when I imported sqlite3.dylib?
The next line as I understand defines errmsg as a string that displays the error message returned by sqlite3 along with the database name.
var statement: COpaquePointer = nil
if sqlite3_prepare_v2(db, "insert into test (name) values (?)", -1, &statement, nil) != SQLITE_OK {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("error preparing insert: \(errmsg)")
}
if sqlite3_bind_text(statement, 1, "foo", -1, SQLITE_TRANSIENT) != SQLITE_OK {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("failure binding foo: \(errmsg)")
}
if sqlite3_step(statement) != SQLITE_DONE {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("failure inserting foo: \(errmsg)")
}
The above also raises some questions for me.
What is COpaquePointer? is it just a pointer?
Again the same questions about the parameter of the first statement (db, "insert into test (name) values (?)", -1, &statement, nil) Why is there -1 or nil?
And is SQLITE_OK returned by SQLite?

JDBC4: The Index 6 Is Out Of Range When 6th parameter is NOT NULL after a series of NULL parameters

UnsignedByte is defined to be 0-255. .getValue() returns Short.
pts1, pts2, pts3 are ALL null or ALL not null (input checks beforehand guarantee).
addlevel(...)'s CallableStatement WORKS for these scenarios:
(gameNo, levelNo, null, null, null, null)
(gameNo, levelNo, pts1, pts2, pts3, null)
(gameNo, levelNo, pts1, pts2, pts3, typeNo)
but throws
com.microsoft.sqlserver.jdbc.SQLServerException: The index 6 is out of
range.
on:
(gameNo, levelNo, null, null, null, typeNo)
the right statement called is marked with a comment in java code segment.
.......................................................................................................................................
SP:
CREATE PROCEDURE addLevel
#gameNo int,
#levelNo int,
#pts1 int = NULL,
#pts2 int = NULL,
#pts3 int = NULL,
#typeNo tinyint = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO tblLevel (gameNo, levelNo, pointsForStar1, pointsForStar2, pointsForStar3, typeNo)
VALUES (#gameNo, #levelNo, #pts1, #pts2, #pts3, #typeNo)
END
GO
java:
public boolean addLevel(int gameNo, int levelNo, Integer pts1star, Integer pts2star, Integer pts3star, UnsignedByte typeNo){
try{
CallableStatement cstmt;
if (pts1star != null){
if (typeNo != null)
cstmt = database.getConnection().prepareCall("{call dbo.addLevel(?,?,?,?,?,?)}");
else
cstmt = database.getConnection().prepareCall("{call dbo.addLevel(?,?,?,?,?)}");
cstmt.setInt("pts1", pts1star);
cstmt.setInt("pts2", pts2star);
cstmt.setInt("pts3", pts3star);
}
else{
if (typeNo != null)
cstmt = database.getConnection().prepareCall("{call dbo.addLevel(?,?,?)}"); //<<<<<--------------- THIS ONE
else
cstmt = database.getConnection().prepareCall("{call dbo.addLevel(?,?)}");
}
cstmt.setInt("gameNo", gameNo);
cstmt.setInt("levelNo", levelNo);
if (typeNo != null)
cstmt.setShort("typeNo", typeNo.getValue());
cstmt.executeUpdate();
return true;
}
catch(SQLException e){
e.printStackTrace();
return false;
}
}
Stored procedure support in Java is positional (even if you use the parameter names to set the parameter value). typeNo is parameter 6 of your stored procedure. Your 'faulty' call defines three parameters. Parameter three is pts1. You need to always call the six parameter version if you want to set typeNo and explicitly setNull the parameters that don't have a value.
Specifically for SQL Server, the {call dbo.addLevel(?,?)} is translated to something like the following (with positional parameters):
EXEC dbo.addLevel ?, ?, ?
When you set the parameter value by name, the driver looks up the parameter position, and attempts to set that parameter. typeNo is position six, so it tries to set parameter 6, which is not defined in this query.
In SQL Server when you execute a stored procedure, you can leave out trailing positional parameters with defaults, or you need to explicitly specify the parameter name (assuming all other parameters have a default):
EXEC dbo.addLevel #gameNo=?, #levelNo=?, #typeNo=?
As far as I know you should be able to use the above query with CallableStatement instead of the call-escape (although I believe this will require using the parameter position to set the value, and not by name).

CREATE and DROP a table while another statement is in place

In my previous implementation, I was streaming results from a sqlite3 table directly into my output application. However, since I am changing the interface to a temporary data structure, I now need to get the number of rows. The preferred way to do that seems to be with a temporary table, so my original
sprintf(query,"SELECT %s AS x, AVG(%s) AS y, AVG((%s)*(%s)) AS ysq FROM %s WHERE %s=%s AND %s GROUP BY x;",x,y,y,y,from,across,val,where);
sqlite3_prepare_v2(db, query, -1, &acResult,NULL);
while(sqlite3_step(acResult)==SQLITE_ROW) { ... }
sqlite3_finalize(acResult);
turns into
sprintf(query,"CREATE TEMP TABLE tt AS SELECT %s AS x, AVG(%s) AS y, AVG((%s)*(%s)) AS ysq FROM %s WHERE %s=%s AND %s GROUP BY x;",x,y,y,y,from,across,val,where);
sqlite3_prepare_v2(db, query, -1, &acResult,NULL);
sqlite3_step(acResult);
sqlite3_finalize(acResult);
sqlite3_prepare_v2(db, "SELECT COUNT(*) FROM tt;", -1, &acResult, NULL);
sqlite3_step(acResult);
int length = sqlite3_column_int(acResult,0);
sqlite3_finalize(acResult);
sqlite3_prepare_v2(db, "SELECT x,y, ysq FROM tt;", -1, &acResult, NULL);
while(sqlite3_step(acResult)==SQLITE_ROW) { ... }
sqlite3_finalize(acResult);
sqlite3_prepare_v2(db, "DROP TABLE tt;", -1, &acResult, NULL);
sqlite3_step(acResult);
sqlite3_finalize(acResult);
Now, this mostly works. The problem is that I have this inside a loop across another stepping query, which seems to be responsible for the table being locked when I try to drop it. If I finalize that query, it "works" (the drop works; everything else breaks because it's part of the logic). There is no possible way the outer query could be referencing tt, because I created it within that "scope".
Is there a way of reminding sqlite that it shouldn't be locked, or am I stuck switching the outer loop away from streaming as well?
This is a read-only application (with the exception of the temp table), if that helps.

An optimized stored procedure to replace this LINQ statement

I have the following two tables in SQL Server 2008
TABLE [JobUnit](
[idJobUnit] [int] IDENTITY(1,1) NOT NULL,
[Job_idJob] [int] NOT NULL, // Foreign key here
[UnitStatus] [tinyint] NOT NULL, // can be (0 for unprocessed, 1 for processing, 2 for processed)
)
TABLE [Job](
[idJob] [int] IDENTITY(1,1) NOT NULL,
[JobName] [varchar(50)] NOT NULL,
)
Job : JobUnit is one-to-many relationship
I am trying to write an efficient store procedure that would replace the following LINQ statement
public enum UnitStatus{
unprocessed,
processing,
processed,
}
int jobId = 10;
using(EntityFramework context = new EntityFramework())
{
if (context.JobUnits.Where(ju => ju.Job_idJob == jobId)
.Any(ju => ju.UnitStatus == (byte)UnitStatus.unproccessed))
{
// Some JobUnit is unprocessed
return 1;
}
else
{
// There is no unprocessed JobUnit
if (context.JobUnits.Where(ju => ju.Job_idJob == jobId) //
.Any(ju => ju.UnitStatus == (byte)UnitStatus.processing))
{
// JobUnit has some unit that is processing, but none is unprocessed
return 2;
}
else
{
// Every JoUnit is processed
return 3;
}
}
}
Thanks for reading
So really, you're just looking for the lowest state of all the units in a particular job?
CREATE PROCEDURE GetJobState #jobId int AS
SELECT MIN(UnitStatus)
FROM JobUnit
WHERE Job_idJob = #jobId
I should also say you could use this approach just as easly in Linq.

Resources