I have the following code that's supposed to bind a value to a prepare statement:
QSqlQuery query(db);
query.setForwardOnly(true);
query.prepare("SELECT Entry.* FROM Entry WHERE body LIKE ?;");
query.addBindValue(QVariant("%" + name + "%"));
query.exec();
tDebug("%s", query.executedQuery().toUtf8().data());
For instance, if name was "thing", then the query should execute the statement SELECT Entry.* FROM Entry WHERE body LIKE "%thing%", but it executes SELECT Entry.* FROM Entry WHERE body LIKE ?, almost as if the bound value was ignored. Named placeholders have the same problem.
I tried this on an equivalent example and appropriately testing the result of "prepare" and "exec", which both return a Boolean. I tested that the values are bounded ok after exec with:
QList<QVariant> list = query.boundValues().values();
for (int i = 0; i < list.size(); ++i)
qDebug() << i << ": " << list.at(i).toString();
I tested that I got the expected result with
while (query.next())
qDebug()<<"result = "<<query.value(0);
Indeed, the executedQuery did not contain the bounded values, but the comment in Qt is somewhat vague on this:
"If a prepared query with placeholders is executed on a DBMS that does not support it, the preparation of this query is emulated.The placeholders in the original query are replaced with their bound values to form a new query. This function returns the modified query. It is mostly useful for debugging purposes."
So I assume that for postgresql (what I have) and SQLite, the executedQuery does return the original with placeholders and not bounded values.
Related
SHA256 sha256;
std::string s = sha256(pass.toStdString());
QString myquery = "declare #identifier nvarchar(100) = NEWID()\
declare #user_name nvarchar(50) = '"+user+"'\
declare #pass_word nvarchar(100) = '"+QString::fromStdString(s)+"'\
declare #hint nvarchar(50) = '"+hint+"'\
if NOT exists(select * from user_table where (userid=#identifier or username = #user_name))\
insert into user_table (username,password,password_salt,userid) values(#user_name,#pass_word,#hint,#identifier)";
qDebug()<<myquery;
openSqlConnection();
QSqlQuery q3;
q3.exec(myquery);
After executing this query, I should see result which will be (1 row(s) affected) if executed successfully.
If username already exists in the database, the result will be
Command(s) completed successfully.
To see the result from the select I use q3.next().
How do I know that my query has been executed successfully?
How do I know that my query has been executed successfully?
You have a number of options here. You could check either:
The returned value of QSqlQuery::exec
Returns true and sets the query state to active if the query was successful; otherwise returns false.
for example:
if (!q3.exec(myquery))
\\ show error
The status of the query, i.e. QSqlQuery::isActive
An active QSqlQuery is one that has been exec()'d successfully but not yet finished with.
for example:
q3.exec(myquery);
if (!q3.isActive())
\\ show error
The type of QSqlQuery::lastError
QSqlError::NoError 0 No error occurred.
for example:
q3.exec(myquery);
if (q3.lastError().type() != QSqlError::NoError)
\\ show error
If you pass the check (the one you have selected), you could process your query further, e.g. to see if the username already exists and how many rows have been affected.
I am trying to fetch some records from MySQL database by using a prepared statement by using QSqlQuery as:
QString username=ui->textEdit_password->toPlainText();
QString password=ui->textEdit_password->toPlainText();
QSqlQuery query;
query.prepare("SELECT * FROM login_access WHERE username=? AND password=?");
query.addBindValue(username);
query.addBindValue(password);
query.exec();`
When i run :
std::string q_str1=query.executedQuery().toUtf8().constData();
std::cout<<"Query : "<<q_str1<<"\n";
It outputs : Query : SELECT * FROM login_access WHERE username=? AND password=? where the "?" has not been replaced and the query returns nothing since the "?" character is compared to the database records.
On running the query: SELECT * FROM login_access, the query returns all the database records in the login_access table.
I have also tried replacing the "?" with placeholders ":uname",":pass" and changed query.addBindValue(username); to query.bindValue(":uname",username);, and done same with password field.
I am running QtCreator 4.4.1
Thanks.
Use query.bindValue( ...) because this sets the placeholder value.
I tested executedQuery() on one of my SQL statements with placeholders and it returned a string with just the placeholders, not the values. The documentation does say that in most cases it the same string as lastQuery().
http://doc.qt.io/qt-5/qsqlquery.html#executedQuery
You have confirmed that your SQL statement without the where clause works so the next stage is to check you are binding what you think you are binding. To do this use boundValue(const QString placeholder) to find out if the placehold value is being bound.
It might also be useful to check the query has run OK.
So, after your query.exec you should put the following (assuming these are your placeholders) just to check these things:
qDebug() << query.lasterError();
qDebug() << query.boundValue(":uname");
qDebug() << query.boundValue(":pass");
I configured my firebird database to autoincrement the primary key of the table.
CREATE GENERATOR GEN_CHANNEL_PARAMETER_SET_ID;
SET GENERATOR GEN_CHANNEL_PARAMETER_SET_ID TO 0;
CREATE TRIGGER CHANNEL_PARAMETER_SETS_BI FOR CHANNEL_PARAMETER_SETS
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
if (NEW.CHANNEL_PARAMETER_SET_ID is NULL) then NEW.CHANNEL_PARAMETER_SET_ID = GEN_ID(GEN_CHANNEL_PARAMETER_SET_ID, 1);
END
Now, in my C++ program using IBPP I have the following problem:
When inserting a dataset into an new row of this table I know all values in my C++ program exept the new primary key because the database creates it. How can I retrieve this key form the database?
Maybe someone else inserted an entry too - just a moment after I inserted one. So retrieve the PK with the highest value could create an error. How can I handle this?
Adopting Amir Rahimi Farahani's answer I found the following solution for my problem:
I use a generator:
CREATE GENERATOR GEN_CHANNEL_PARAMETER_SET_ID;
SET GENERATOR GEN_CHANNEL_PARAMETER_SET_ID TO 0;
and the following C++/IBPP/SQL code:
// SQL statement
m_DbStatement->Execute(
"SELECT NEXT VALUE FOR gen_channel_parameter_set_id FROM rdb$database"
);
// Retrieve Data
IBPP::Row ibppRow;
int64_t channelParameterSetId;
m_DbStatement->Fetch(ibppRow);
ibppRow->Get (1, channelParameterSetId);
// SQL statement
m_DbStatement->Prepare(
"INSERT INTO channel_parameter_sets "
"(channel_parameter_set_id, ...) "
"VALUES (?, ...) "
);
// Set variables
m_DbStatement->Set (1, channelParameterSetId);
...
...
// Execute
m_DbStatement->Execute ();
m_DbTransaction->CommitRetain ();
It is possible to generate and use the new id before inserting the new record:
SELECT NEXT VALUE FOR GEN_CHANNEL_PARAMETER_SET_ID FROM rdb$database
You now know the value for new primary key.
Update:
IBPP supports RETURNING too:
// SQL statement
m_DbStatement->Prepare(
"INSERT INTO channel_parameter_sets "
"(...) VALUES (...) RETURNING channel_parameter_set_id"
);
// Execute
m_DbStatement->Execute ();
m_DbTransaction->CommitRetain ();
// Get the generated id
m_DbStatement->Get (1, channelParameterSetId);
...
To retrieve the value of the generated key (or any other column) you can use INSERT ... RETURNING ....
For example:
INSERT INTO myTable (x, y, z) VALUES (1, 2, 3) RETURNING ID
Also a lot of drivers provide extra features to support RETURNING, but I don't know IBPP.
Note that from the perspective of a driver the use of RETURNING will make the insert act like an executable stored procedure; some drivers might require you to execute it in a specific way.
Using Qt, I have to connect to a database and list column's types and names from a table. I have two constraints:
1 The database type must not be a problem (This has to work on PostgreSQL, SQL Server, MySQL, ...)
2 When I looked on the internet, I found solutions that work but only if there are one or more reocrd into the table. And I have to get column's type and name with or without record into this database.
I searched a lot on the internet but I didn't find any solutions.
I am looking for an answer in Qt/C++ or using a query that can do that.
Thanks for help !
QSqlDriver::record() takes a table name and returns a QSqlRecord, from which you can fetch the fields using QSqlRecord::field().
So, given a QSqlDatabase db,
fetch the driver with db.driver(),
fetch the list of tables with db.tables(),
fetch the a QSqlRecord for each table from driver->record(tableName), and
fetch the number of fields with record.count() and the name and type with record.field(x)
According to the previous answers, I make the implementation as below.It can work well, hope it can help you.
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSLITE", "demo_conn"); //create a db connection
QString strDBPath = "db_path";
db.setDatabaseName(strDBPath); //set the db file
QSqlRecord record = db.record("table_name"); //get the record of the certain table
int n = record.count();
for(int i = 0; i < n; i++)
{
QString strField = record.fieldName(i);
}
}
QSqlDatabase::removeDatabase("demo_conn"); //remove the db connection
Getting column names and types is a database-specific operation. But you can have a single C++ function that will use the correct sql query according to the QSqlDriver you currently use:
QStringlist getColumnNames()
{
QString sql;
if (db.driverName.contains("QOCI", Qt::CaseInsensitive))
{
sql = ...
}
else if (db.driverName.contains("QPSQL", Qt::CaseInsensitive))
{
sql = ...
}
else
{
qCritical() << "unsupported db";
return QStringlist();
}
QSqlQuery res = db.exec(sql);
...
// getting names from db-specific sql query results
}
I don't know of any existing mechanism in Qt which allows that (though it might exist - maybe by using QSqlTableModel). If noone else knows of such a thing, I would just do the following:
Create data classes to store the information you require, e.g. a class TableInfo which stores a list of ColumnInfo objects which have a name and a type.
Create an interface e.g. ITableInfoReader which has a pure virtual TableInfo* retrieveTableInfo( const QString& tableName ) method.
Create one subclass of ITableInfoReader for every database you want to support. This allows doing queries which are only supported on one or a subset of all databases.
Create a TableInfoReaderFactory class which allows creation of the appropriate ITableInfoReader subclass dependent on the used database
This allows you to have your main code independent from the database, by using only the ITableInfoReader interface.
Example:
Input:
database: The QSqlDatabase which is used for executing queries
tableName: The name of the table to retrieve information about
ITableInfoReader* tableInfoReader =
_tableInfoReaderFactory.createTableReader( database );
QList< ColumnInfo* > columnInfos = tableInfoReader->retrieveTableInfo( tableName );
foreach( ColumnInfo* columnInfo, columnInfos )
{
qDebug() << columnInfo.name() << columnInfo.type();
}
I found the solution. You just have to call the record function from QSqlDatabase. You have an empty record but you can still read column types and names.
I've written a SQL query based on Qt assistant and it says that you can use the prepare() method instead of exec() then you can pass your parameter by the help of two methods called :
bindvalue() and addbindvalue()
Here is an snippet code of my problem :
Query->prepare("SELECT ID , Row , Col FROM sometable WHERE Row = :row AND Col = :col");
Query->bindValue(":row" , __Row);
Query->bindValue(":col" ,__Col);
Query->exec();
qDebug("%s" , Query->executedQuery().toStdString().c_str());
output :
SELECT ID , Row , Col FROM sometable WHERE Row = ? AND Col = ?
and also I've used another suggested way :
Query->prepare("SELECT ID , Row , Col FROM sometable WHERE Row = :row AND Col = :col");
Query->addBindValue(0 , __Row);
Query->addBindValue(1 ,__Col);
Query->exec();
qDebug("%s" , Query->executedQuery().toStdString().c_str());
output :
SELECT ID , Row , Col FROM sometable WHERE Row = ? AND Col = ?
but when I use exec() normally it works perfectly and will replace the corresponding values instead of "?".
is there any explanation about that? or should I use the ordinary exec()?
Is the exec() call failing ?
Because it may just be ok what you're seeing, as ,depending on which sql server you're using, the binding could be done by the server ( e.g. Oracle ).
According to Qt docs, executedQuery: "In most cases this function returns the same string as lastQuery(). If a prepared query with placeholders is executed on a DBMS that does not support it, the preparation of this query is emulated". So, I guess, if the server supports binding values the preparation won't be emulated so you'd just see the query without the placeholders being replaced by real values.
This is just a guess but from http://qt.nokia.com/doc/4.6/qsqlquery.html I read the following:
Warning: You must load the SQL driver and open the connection before a QSqlQuery is created. Also, the connection must remain open while the query exists; otherwise, the behavior of QSqlQuery is undefined.
Is the connection open in your case?
You can try this if you want to how your query constructed with prepared statements:
qDebug("%s" , Query.lastQuery().toStdString().c_str());