SQLITE update query fails - sql

I am trying to update the table column by appending with new data of QtSQl database. I need to update the column imgpath by appending with new data.
Below is the code, but it always fails, what could be the issue?.
QSqlQuery query(db);
query.exec("create table table1 (id integer primary key autoincrement, time varchar(20), imgpath varchar(20))");
query.exec("insert into table1 values(NULL,'00:15:25','img0.jpg')");
query.exec("insert into table1 values(NULL,'00:15:25','img1.jpg')");
bool up = query.exec("update table1 set imgpath=concat(';newImage.jpg',imgpath) where ID=1");
if(up==false)
qDebug()<<"Update failed";
Update:
Complete code:
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("./newDB");
db.open();
QSqlQuery query(db);
query.exec("create table table1 (id integer primary key autoincrement, time varchar(20), imgpath varchar(20))");
query.exec("insert into table1 values(NULL,'00:15:25','img0.jpg')");
query.exec("insert into table1 values(NULL,'00:15:25','img1.jpg')");
//bool up = query.exec("update table1 set imgpath='newimage.jpg',time='' where ID=1");
bool up = query.exec("update table1 set imgpath=concat(';newImage.jpg',imgpath)");
if(up==false){
qDebug()<<"Update failed";
qDebug() << db.lastError();
}
query.exec("SELECT * FROM table1 limit 100");
QVector<QStringList> lst;
while (query.next())
{
QSqlRecord record = query.record();
QStringList tmp;
for(int i=0; i < record.count(); i++)
{
tmp << record.value(i).toString();
}
lst.append(tmp);
}
foreach (const QStringList &var, lst) {
qDebug() << var;
}

SQLite doesn't support the concat function. If you remove
imgpath=concat(';newImage.jpg',imgpath)");
and replace it with standard colName='Value' syntax I'm betting that everything will start working. If you want to append text to the current value you should be able to do it with something like the following syntax:
bool up = query.exec("update table1 set imgpath=';newImage.jpg'||imgpath");

Related

SQLite error in QT - QSqlError("", "Parameter count mismatch", "")

I'm trying to simply count the amount of records that has a 'true' status.
this is the SQLite table structure:
CREATE TABLE Suppliers(ID INTEGER PRIMARY KEY AUTOINCREMENT,Name varchar(50),Number varchar(15),URL varchar(70),Status bool,ShippingCost integer)
I am then calling a query from QT as follows:
int SQLiteController::ActiveSupplierCount()
{
int count = 0;
QSqlQuery Query;
Query.prepare("SELECT *"
"FROM Suppliers"
"WHERE Status = (:Status)");
Query.bindValue(":Status", true);
Query.exec();
qDebug() << Query.lastError();
while(Query.next() == true)
{
count++;
}
qDebug() << count;
return count;
};
The last error returned here is "Parameter count mismatch"
and I cannot figure out why... There is only 1 parameter, and I assign to that 1 parameter.
Try to add some extra spaces after each line of your query like this
Query.prepare("SELECT * "
"FROM Suppliers "
"WHERE Status = (:Status)");

How to pass a param for a binding in PostgreSQL - COPY (... ) TO STDOUT (FORMAT binary)?

I have some simple test table in postgres like below:
--DROP TABLE test_point
CREATE TABLE test_point
(
serie_id INT NOT NULL,
version_ts INT NOT NULL,
PRIMARY KEY (serie_id, version_ts)
);
I try to load a data from it by using COPY TO STDOUT and binary buffers. This is sql definition I use in a test case:
COPY (
SELECT version_ts
FROM test_point
WHERE
serie_id = $1::int
) TO STDOUT (FORMAT binary);
It works ok, if I don't provide any param to bind to in SQL. If I use simple select it recognizes params also as well.
I was trying to provide explicit info about param type during stmt preparation also, but results were similar (it doesn't recognize param).
This is a message I receive during the test case:
0x000001740a288ab0 "ERROR: bind message supplies 1 parameters, but prepared statement \"test1\" requires 0\n"
How to properly provide a param for COPY() statement?
I don't want to cut/concatenate strings for timestamp params and similar types.
Below is a test case showing the issue.
TEST(TSStorage, CopyParamTest)
{
auto sql = R"(
COPY (
SELECT version_ts
FROM test_point
WHERE
serie_id = $1::int
) TO STDOUT (FORMAT binary);
)";
auto connPtr = PQconnectdb("postgresql://postgres:pswd#localhost/some_db");
auto result = PQprepare(connPtr, "test1", sql, 0, nullptr);
// Lambda to test result status
auto testRes = [&](ExecStatusType status)
{
if (PQresultStatus(result) != status)
{
PQclear(result);
auto errorMsg = PQerrorMessage(connPtr);
PQfinish(connPtr);
throw std::runtime_error(errorMsg);
}
};
testRes(PGRES_COMMAND_OK);
PQclear(result);
int seriesIdParam = htonl(5);
const char *paramValues[] = {(const char *)&seriesIdParam};
const int paramLengths[] = {sizeof(seriesIdParam)};
const int paramFormats[] = {1}; // 1 means binary
// Execute prepared statement
result = PQexecPrepared(connPtr,
"test1",
1, //nParams,
paramValues,
paramLengths,
paramFormats,
1); // Output format - binary
// Ensure it's in COPY_OUT state
//testRes(PGRES_COPY_OUT);
if (PQresultStatus(result) != PGRES_COPY_OUT)
{
auto errorMsg = PQerrorMessage(connPtr);
int set_breakpoint_here = 0; // !!! !!! !!!
}
PQclear(result);
PQfinish(connPtr);
}

Removing column gives syntax error [duplicate]

I have a problem: I need to delete a column from my SQLite database. I wrote this query
alter table table_name drop column column_name
but it does not work. Please help me.
Update: SQLite 2021-03-12 (3.35.0) now supports DROP COLUMN. The FAQ on the website is still outdated.
From: http://www.sqlite.org/faq.html:
(11) How do I add or delete columns from an existing table in SQLite.
SQLite has limited ALTER TABLE support that you can use to add a
column to the end of a table or to change the name of a table. If you
want to make more complex changes in the structure of a table, you
will have to recreate the table. You can save existing data to a
temporary table, drop the old table, create the new table, then copy
the data back in from the temporary table.
For example, suppose you have a table named "t1" with columns names
"a", "b", and "c" and that you want to delete column "c" from this
table. The following steps illustrate how this could be done:
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;
Instead of dropping the backup table, just rename it...
BEGIN TRANSACTION;
CREATE TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
ALTER TABLE t1_backup RENAME TO t1;
COMMIT;
For simplicity, why not create the backup table from the select statement?
CREATE TABLE t1_backup AS SELECT a, b FROM t1;
DROP TABLE t1;
ALTER TABLE t1_backup RENAME TO t1;
This option works only if you can open the DB in a DB Browser like DB Browser for SQLite.
In DB Browser for SQLite:
Go to the tab, "Database Structure"
Select you table Select Modify table (just under the tabs)
Select the column you want to delete
Click on Remove field and click OK
=>Create a new table directly with the following query:
CREATE TABLE table_name (Column_1 TEXT,Column_2 TEXT);
=>Now insert the data into table_name from existing_table with the following query:
INSERT INTO table_name (Column_1,Column_2) FROM existing_table;
=>Now drop the existing_table by following query:
DROP TABLE existing_table;
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE table1 RENAME TO _table1_old;
CREATE TABLE table1 (
( column1 datatype [ NULL | NOT NULL ],
column2 datatype [ NULL | NOT NULL ],
...
);
INSERT INTO table1 (column1, column2, ... column_n)
SELECT column1, column2, ... column_n
FROM _table1_old;
COMMIT;
PRAGMA foreign_keys=on;
For more info:
https://www.techonthenet.com/sqlite/tables/alter_table.php
I've made a Python function where you enter the table and column to remove as arguments:
def removeColumn(table, column):
columns = []
for row in c.execute('PRAGMA table_info(' + table + ')'):
columns.append(row[1])
columns.remove(column)
columns = str(columns)
columns = columns.replace("[", "(")
columns = columns.replace("]", ")")
for i in ["\'", "(", ")"]:
columns = columns.replace(i, "")
c.execute('CREATE TABLE temptable AS SELECT ' + columns + ' FROM ' + table)
c.execute('DROP TABLE ' + table)
c.execute('ALTER TABLE temptable RENAME TO ' + table)
conn.commit()
As per the info on Duda's and MeBigFatGuy's answers this won't work if there is a foreign key on the table, but this can be fixed with 2 lines of code (creating a new table and not just renaming the temporary table)
For SQLite3 c++ :
void GetTableColNames( tstring sTableName , std::vector<tstring> *pvsCols )
{
UASSERT(pvsCols);
CppSQLite3Table table1;
tstring sDML = StringOps::std_sprintf(_T("SELECT * FROM %s") , sTableName.c_str() );
table1 = getTable( StringOps::tstringToUTF8string(sDML).c_str() );
for ( int nCol = 0 ; nCol < table1.numFields() ; nCol++ )
{
const char* pch1 = table1.fieldName(nCol);
pvsCols->push_back( StringOps::UTF8charTo_tstring(pch1));
}
}
bool ColExists( tstring sColName )
{
bool bColExists = true;
try
{
tstring sQuery = StringOps::std_sprintf(_T("SELECT %s FROM MyOriginalTable LIMIT 1;") , sColName.c_str() );
ShowVerbalMessages(false);
CppSQLite3Query q = execQuery( StringOps::tstringTo_stdString(sQuery).c_str() );
ShowVerbalMessages(true);
}
catch (CppSQLite3Exception& e)
{
bColExists = false;
}
return bColExists;
}
void DeleteColumns( std::vector<tstring> *pvsColsToDelete )
{
UASSERT(pvsColsToDelete);
execDML( StringOps::tstringTo_stdString(_T("begin transaction;")).c_str() );
std::vector<tstring> vsCols;
GetTableColNames( _T("MyOriginalTable") , &vsCols );
CreateFields( _T("TempTable1") , false );
tstring sFieldNamesSeperatedByCommas;
for ( int nCol = 0 ; nCol < vsCols.size() ; nCol++ )
{
tstring sColNameCurr = vsCols.at(nCol);
bool bUseCol = true;
for ( int nColsToDelete = 0; nColsToDelete < pvsColsToDelete->size() ; nColsToDelete++ )
{
if ( pvsColsToDelete->at(nColsToDelete) == sColNameCurr )
{
bUseCol = false;
break;
}
}
if ( bUseCol )
sFieldNamesSeperatedByCommas+= (sColNameCurr + _T(","));
}
if ( sFieldNamesSeperatedByCommas.at( int(sFieldNamesSeperatedByCommas.size()) - 1) == _T(','))
sFieldNamesSeperatedByCommas.erase( int(sFieldNamesSeperatedByCommas.size()) - 1 );
tstring sDML;
sDML = StringOps::std_sprintf(_T("insert into TempTable1 SELECT %s FROM MyOriginalTable;\n") , sFieldNamesSeperatedByCommas.c_str() );
execDML( StringOps::tstringTo_stdString(sDML).c_str() );
sDML = StringOps::std_sprintf(_T("ALTER TABLE MyOriginalTable RENAME TO MyOriginalTable_old\n") );
execDML( StringOps::tstringTo_stdString(sDML).c_str() );
sDML = StringOps::std_sprintf(_T("ALTER TABLE TempTable1 RENAME TO MyOriginalTable\n") );
execDML( StringOps::tstringTo_stdString(sDML).c_str() );
sDML = ( _T("DROP TABLE MyOriginalTable_old;") );
execDML( StringOps::tstringTo_stdString(sDML).c_str() );
execDML( StringOps::tstringTo_stdString(_T("commit transaction;")).c_str() );
}
In case anyone needs a (nearly) ready-to-use PHP function, the following is based on this answer:
/**
* Remove a column from a table.
*
* #param string $tableName The table to remove the column from.
* #param string $columnName The column to remove from the table.
*/
public function DropTableColumn($tableName, $columnName)
{
// --
// Determine all columns except the one to remove.
$columnNames = array();
$statement = $pdo->prepare("PRAGMA table_info($tableName);");
$statement->execute(array());
$rows = $statement->fetchAll(PDO::FETCH_OBJ);
$hasColumn = false;
foreach ($rows as $row)
{
if(strtolower($row->name) !== strtolower($columnName))
{
array_push($columnNames, $row->name);
}
else
{
$hasColumn = true;
}
}
// Column does not exist in table, no need to do anything.
if ( !$hasColumn ) return;
// --
// Actually execute the SQL.
$columns = implode('`,`', $columnNames);
$statement = $pdo->exec(
"CREATE TABLE `t1_backup` AS SELECT `$columns` FROM `$tableName`;
DROP TABLE `$tableName`;
ALTER TABLE `t1_backup` RENAME TO `$tableName`;");
}
In contrast to other answers, the SQL used in this approach seems to preserve the data types of the columns, whereas something like the accepted answer seems to result in all columns to be of type TEXT.
Update 1:
The SQL used has the drawback that autoincrement columns are not preserved.
Just in case if it could help someone like me.
Based on the Official website and the Accepted answer, I made a code using C# that uses System.Data.SQLite NuGet package.
This code also preserves the Primary key and Foreign key.
CODE in C#:
void RemoveColumnFromSqlite (string tableName, string columnToRemove) {
try {
var mSqliteDbConnection = new SQLiteConnection ("Data Source=db_folder\\MySqliteBasedApp.db;Version=3;Page Size=1024;");
mSqliteDbConnection.Open ();
// Reads all columns definitions from table
List<string> columnDefinition = new List<string> ();
var mSql = $"SELECT type, sql FROM sqlite_master WHERE tbl_name='{tableName}'";
var mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
string sqlScript = "";
using (mSqliteReader = mSqliteCommand.ExecuteReader ()) {
while (mSqliteReader.Read ()) {
sqlScript = mSqliteReader["sql"].ToString ();
break;
}
}
if (!string.IsNullOrEmpty (sqlScript)) {
// Gets string within first '(' and last ')' characters
int firstIndex = sqlScript.IndexOf ("(");
int lastIndex = sqlScript.LastIndexOf (")");
if (firstIndex >= 0 && lastIndex <= sqlScript.Length - 1) {
sqlScript = sqlScript.Substring (firstIndex, lastIndex - firstIndex + 1);
}
string[] scriptParts = sqlScript.Split (new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
foreach (string s in scriptParts) {
if (!s.Contains (columnToRemove)) {
columnDefinition.Add (s);
}
}
}
string columnDefinitionString = string.Join (",", columnDefinition);
// Reads all columns from table
List<string> columns = new List<string> ();
mSql = $"PRAGMA table_info({tableName})";
mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
using (mSqliteReader = mSqliteCommand.ExecuteReader ()) {
while (mSqliteReader.Read ()) columns.Add (mSqliteReader["name"].ToString ());
}
columns.Remove (columnToRemove);
string columnString = string.Join (",", columns);
mSql = "PRAGMA foreign_keys=OFF";
mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
int n = mSqliteCommand.ExecuteNonQuery ();
// Removes a column from the table
using (SQLiteTransaction tr = mSqliteDbConnection.BeginTransaction ()) {
using (SQLiteCommand cmd = mSqliteDbConnection.CreateCommand ()) {
cmd.Transaction = tr;
string query = $"CREATE TEMPORARY TABLE {tableName}_backup {columnDefinitionString}";
cmd.CommandText = query;
cmd.ExecuteNonQuery ();
cmd.CommandText = $"INSERT INTO {tableName}_backup SELECT {columnString} FROM {tableName}";
cmd.ExecuteNonQuery ();
cmd.CommandText = $"DROP TABLE {tableName}";
cmd.ExecuteNonQuery ();
cmd.CommandText = $"CREATE TABLE {tableName} {columnDefinitionString}";
cmd.ExecuteNonQuery ();
cmd.CommandText = $"INSERT INTO {tableName} SELECT {columnString} FROM {tableName}_backup;";
cmd.ExecuteNonQuery ();
cmd.CommandText = $"DROP TABLE {tableName}_backup";
cmd.ExecuteNonQuery ();
}
tr.Commit ();
}
mSql = "PRAGMA foreign_keys=ON";
mSqliteCommand = new SQLiteCommand (mSql, mSqliteDbConnection);
n = mSqliteCommand.ExecuteNonQuery ();
} catch (Exception ex) {
HandleExceptions (ex);
}
}
In Python 3.8...
Preserves primary key and column types.
Takes 3 inputs:
a sqlite cursor: db_cur,
table name: t and,
list of columns to junk: columns_to_junk
def removeColumns(db_cur, t, columns_to_junk):
# Obtain column information
sql = "PRAGMA table_info(" + t + ")"
record = query(db_cur, sql)
# Initialize two strings: one for column names + column types and one just
# for column names
cols_w_types = "("
cols = ""
# Build the strings, filtering for the column to throw out
for r in record:
if r[1] not in columns_to_junk:
if r[5] == 0:
cols_w_types += r[1] + " " + r[2] + ","
if r[5] == 1:
cols_w_types += r[1] + " " + r[2] + " PRIMARY KEY,"
cols += r[1] + ","
# Cut potentially trailing commas
if cols_w_types[-1] == ",":
cols_w_types = cols_w_types[:-1]
else:
pass
if cols[-1] == ",":
cols = cols[:-1]
else:
pass
# Execute SQL
sql = "CREATE TEMPORARY TABLE xfer " + cols_w_types + ")"
db_cur.execute(sql)
sql = "INSERT INTO xfer SELECT " + cols + " FROM " + t
db_cur.execute(sql)
sql = "DROP TABLE " + t
db_cur.execute(sql)
sql = "CREATE TABLE " + t + cols_w_types + ")"
db_cur.execute(sql)
sql = "INSERT INTO " + t + " SELECT " + cols + " FROM xfer"
db_cur.execute(sql)
You'll find a reference to a query() function. Just a helper...
Takes two inputs:
sqlite cursor db_cur and,
the query string: query
def query(db_cur, query):
r = db_cur.execute(query).fetchall()
return r
Don't forget to include a "commit()"!

How I can don't change some column under certain conditions via an SQL statement which can bind parameters

CREATE TABLE "table" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"col1" BLOB,
"col2" BLOB,
"col3" BLOB,
"coln" BLOB
);
id|col1|col2|col3|coln
1 | 1-1| 1-2| 1-3| 1-n
2 | 2-1| 2-2| 2-3| 2-n
UPDATE table SET col1=?, col2=?, col3=?, coln=? WHERE id=?;
How I can don't change some column under certain conditions just via the above SQL statement.
sqlite.
for example,
public static class Bean{
public long id;
public byte[] col1;
public byte[] col2;
public byte[] col3;
public byte[] coln;
}
PreparedStatement psUpdate = dsConn.prepareStatement("UPDATE table SET col1=?, col2=?, col3=?, coln=? WHERE id=?;");
Bean bean = new Bean();
if(null != bean.col1){
psUpdate.setBytes(1, bean.col1);//col1
}else{
psUpdate.omitColumn(1);//omit to update
}
if(null != bean.col2){
psUpdate.setBytes(2, bean.col2);//col2
}else{
psUpdate.omitColumn(2);//omit to update
}
if(null != bean.col3){
psUpdate.setBytes(3, bean.col3);//col3
}else{
psUpdate.omitColumn(3);//omit to update
}
if(null != bean.coln){
psUpdate.setBytes(4, bean.coln);//coln
}else{
psUpdate.omitColumn(4);//omit to update
}
psUpdate.setBytes(5, bean.id);//WHERE id=?
psUpdate.executeUpdate();
I haved read the document about sqlite or java. Unfortunately, it seems imposible.
You should just construct the appropriate UPDATE statement with only the columns that you actually want to update.
But if you really want to use a single statement, you could use CASE expressions to update the columns with their old value if the parameter is NULL:
UPDATE MyTable
SET col1 = CASE WHEN ?1 IS NULL THEN col1 ELSE ?1 END,
col2 = CASE WHEN ?2 IS NULL THEN col2 ELSE ?2 END,
...
WHERE id = ?5;

inserting data selected by checkboxes

I am trying a code where I want to create a table and insert data into the table.
I want to create table and insert data for only those fields which are selected by user using checkbox.
This is my jsp code:
<%# page language="java" import="java.util.*"%>
<%# page import="java.sql.*" %>
<%
try
{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String url = "jdbc:sqlserver://";
Connection con = DriverManager.getConnection(url,"sa","SQL1423#3");
Statement st=null;
String select[] = (String[]) session.getAttribute("a");
String tblname=(String)session.getAttribute("tblname");
//out.println(tblname);
//String select[] = request.getParameterValues("id");
if (select != null && select.length != 0)
{
for (int i = 0; i < select.length; i++)
{
int ch=Integer.parseInt(select[i]);
//int counter=1;
//int ID=1;
switch(ch)
{
case 1 :
String idOne=request.getParameter("idOne");
//out.println(idOne);
String idOneIns="insert into "+tblname+" (ID) values(?)";
PreparedStatement pstmt = con.prepareStatement(idOneIns);
pstmt.setString(1, idOne);
//st=con.createStatement();
int valueTwo = pstmt.executeUpdate();
out.println("Inserted");
break;
case 2 :
String ser=request.getParameter("series");
String serIns="insert into "+tblname+" (SERIES) values(?)";
PreparedStatement pst = con.prepareStatement(serIns);
pst.setString(1, ser);
int value = pst.executeUpdate();
out.println("Inserted");
break;
case 3 :
String sym=request.getParameter("symbol");
String symIns="insert into "+tblname+" (SYMBOL) values(?)";
PreparedStatement pstm = con.prepareStatement(symIns);
pstm.setString(1, sym);
int valueOne = pstm.executeUpdate();
out.println("Inserted");
break;
}//switch
}//for
}//if
st.close();
con.close();
}//try
catch(Exception e)
{
out.println(e);
}
%>
Here data is inserted in the table.But if I select all three checkboxes then data is inserted as :
Id Series Symbol
1 null null
null a null
null null b
But I want the data to be inserted as :
Id Series Symbol
1 a b
What changes I should make so that I'll get this output?
Updating the database is better put into a Controller. JSP pages is better used for displaying results (See MVC pattern)
Using dynamic SQL to determine database tables is inherently dangerous and could be targeted for attacks
Your current code loops over input elements, and for each element inserts a new row in the database table. What you want to do is assemble the tree values (from your form input) and insert only one row. E.g.
INSERT INTO table (ID, SERIES, SYMBOL) VALUES (?,?,?)