Can't retrieve identity field after insert in sql database - c++

Using C++ Qt framework and sql server 2008 I've been trying to insert a record into a table with an identity field and retrieve the identity value. Below is a simplified code sample which actually does do the insert but just doesn't retreive the identity. The identity returned from query.value(0) is an invalid QVariant.
QSqlQuery query(*pConn);
query.prepare("insert into [VH_MANUFACTURER] values ('sRRS test man code','RRS type','RRS logo file',1,'RRS SEO para','RRS description','RS');"
"select SCOPE_IDENTITY();");
if(query.exec())
{
if(query.next())
{
QVariant identity = query.value(0);
int id=identity.toInt();
}
}
I've tried using select ##identity instead of scope_identity with no improvement and also QSqlQuery .lastInsertId() which also returns an invalid QVariant, see below.
bool bFeature = pConn->driver()->hasFeature(QSqlDriver::LastInsertId);
QSqlQuery query(*pConn);
query.prepare("insert into [VH_MANUFACTURER] ([MFG_NAME],[MFG_TYPE],[MFG_LOGO],[MFG_ACTIVE],[MFG_SEO_CONTENT],[MFG_DESCRI],[MFG_CAPMANCODE]) values ('sRRS test man code','RRS type','RRS logo file',1,'RRS SEO para','RRS description','RS')");
if(query.exec())
{
QVariant id=query.lastInsertId();
}
hasFeature returns true, so the driver is supposed to support what I'm trying to do.
Just to test the sql , I ran the sql directly through Sql Server Management Studio and it inserts as expected and returns the identity value correctly.

Finally found a work around using OUTPUT clause in sql. I don't exactly know why the other methods I tried don't work. There is a sql server bug associated with this feature but that doesn't explain why it worked in ssms but not in c++ Qt code. The sample below show's the work around. Here's the reference I used to solve this.
bool bFeature = pConn->driver()->hasFeature(QSqlDriver::LastInsertId);
QSqlQuery query(*pConn);
query.prepare("insert into [VH_MANUFACTURER] ([MFG_NAME],[MFG_TYPE],[MFG_LOGO],[MFG_ACTIVE],[MFG_SEO_CONTENT],[MFG_DESCRI],[MFG_CAPMANCODE]) OUTPUT INSERTED.MFG_ID values ('sRRS test man code','RRS type','RRS logo file',1,'RRS SEO para','RRS description','RS')");
if(query.exec())
{
if(query.next())
{
QVariant id=query.value("MFG_ID");
}
}

Related

oledb: INSERT with OUTPUT statement generates DB_E_ERRORSOCCURRED error

I'm creating a small c++ library to access MS SQL Server database using OLE DB (with cpp wrapper provided by atldbcli).
While SELECT or INSERT statements alone works fine I've encountered problem with SQL query like:
INSERT INTO testTable(col_int, col_str)
OUTPUT INSERTED.autincPK --output primary key value, required for further processing
VALUES (43, 'test str')
Produce error:
DB_E_ERRORSOCCURRED Multiple-step OLE DB operation generated errors. Check each OLE DB status value, if available. No work was done.
I'm unable to find more error info.
Query executes without any errors in database. Error message says 'No work was done' but new row is created in table (autocommit is enabled).
The relevant code looks like:
#include <atldbcli.h>
int execSQL(CSession *pSession, const std::string& sql)
{
CCommand<CDynamicAccessor, CRowset, CMultipleResults> command;
CDBPropSet rgPropertySet[1] = {};
rgPropertySet[0].SetGUID(DBPROPSET_ROWSET);
rgPropertySet[0].AddProperty(DBPROP_CANSCROLLBACKWARDS, true);
rgPropertySet[0].AddProperty(DBPROP_CANFETCHBACKWARDS, true);
rgPropertySet[0].AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT);
rgPropertySet[0].AddProperty(DBPROP_IMultipleResults, true);
}
HRESULT hr = command.Open(*pSession, sql.c_str(), rgPropertySet, &m_rowsAffected, DBGUID_DEFAULT, false, 1ul);
if (FAILED(hr)){
//error handling
}
return 0;

How to get item attributes via api?

We are using cloud Dynamics 365 Business Central and trying to get items with all attributes via OData.
In Microsoft documentation we found this endpoint:
api.businesscentral.dynamics.com/v1.0[our tenant id]/Sandbox/ODataV4/Company('CRONUS%20DE')/items
But unfortunately, the response does not contain item attributes and values, such as Farbe, Tiefe, etc.
Next, we tried to add new Web Services. But some of this endpoints return empty values and some of them (7506, 7507, 7508, 7510) don't work and return:
No HTTP resource was found that matches the request URI
Objects 7500, 7501, 7503 contain information about attributes. But non of them (7500 - 7510) does not contain a relation between Item, Attributes, and Values.
Maybe there is another way to get items with their attribute values? We also tried to research microsoft graph but not successful.
i am having similar troubles with this. i find the dynamics api to be exceptionally unintuitive and difficult to use. the furthest i have been able to get has been to go into the api settings for dynamics and uncover the tables for a few item attributes (i believe that the table numbers are as those below:
7500 - Item Attribute
7501 - Item Attribute Value
7502 - Item Attribute Translation
7504 - Item Attribute Value Selection
7505 - Item Attribute Value Mapping
i cannot comment on why 7503 is missing.
using 7500 as an example, when you uncover the table, the system provides a resulting endpoint (unfortunately, they always promote OData, and the outdated SOAP resource; i can't figure out why they have such a vendetta against the simple and easy-to-use REST endpoint).
https://api.businesscentral.dynamics.com/v2.0/<TENANT_ID>/<ENVIRONMENT_NAME>/ODataV4/Company('COMPANY_IDENTIFIER')/ItemAttributes
using this endpoint, you can get a listing of the attribute types themselves (e.g. let's say you've defined an attribute called 'BaseColor', you should get a result here for the name of the attribute 'BaseColor', its ID, its type, etc.),
with the ItemAttributeValues endpoint, you should get the actual attribute values that are in existence (e.g. for some item, you happened to set its 'BaseColor' attribute to 'Blue', you should get a response for this attribute value with a attribute type of 'BaseColor', a value, as 'Blue' along with the entity's ID, etc).
yet, when it comes to any instantiated attribute values for items, i can't figure out how to get the association of the attributes with those items. i expect that the "item attribute value mapping" option would be something along the lines of a item_id - attribute_id pair so that for any item in question, one could query the attributes list with something like a filter. but as you said, upon uncovering some of these elements, their respective endpoints return nothing. you get to the point where you say 'OH...AWSOME! there is a value-item mapping. that makes sense, and i can definitely use that'. a few moments later, the API spits in your face with an error, or craps on you by returning something you don't expect like an empty data set.
this api is a constant uphill battle, and totally riddled with landmines. a complete blow-me-up-pain-in-the-arse.
EDIT: 2021-06-09
i've looked into this some more. i was able to set up an export package for the various tables in question, specifically 7500, 7501, and 7505. the magical table was 7505 as it is the relationship between an attribute value and the item with which it is associated. exporting the package to excel results in good data. yet, when trying to expose this information in the OData resource, something strange happens:
in web services, i try to open up page 7505 which populates the object name as ItemAttributeValueMapping and i set the service name to 'ItemAttributeValueMapping'. This is normal.
the system complains when i fail to specify that the object type is a page. so, i go back in the line and set the selection to "Page"
when i tab through to publish the change, the object name automatically changes to 'ItemAttributeValueTranslations'.
EDIT: 2021-06-15
After a lot of fiddling about, i finally reached a point where i decided that the only way to address this was to write an al query which would expose the appropriate value-item mapping information. there is a page which provides some source code for doing this:
github AL-Code-Samples
to get something out of the API, i had to use microsoft visual studio code. there are a few good videos on how to get this up and running to get a test app working for your business central instance (i used eric hougaar's videos: Getting started with Business Central AL Development).
when you have set up your app to connect to your instance by inserting your tenant and entering your credentials, you can modify the source code as below to create a query in your system.
query 50102 "<YOUR_QUERY_NAME>"
{
QueryType = API;
APIPublisher = '<YOUR_NAME>';
APIGroup = '<YOUR_APP_GROUP>';
APIVersion = 'v1.0';
Caption = '<YOUR CAPTION>';
EntityName = '<YOUR_QUERY_NAME>';
EntitySetName = '<YOUR_API_ENDPOINT_NAME>';
elements
{
dataitem(Item; Item)
{
column(No_; "No.")
{
}
column(Description; Description)
{
}
column(Unit_Cost; "Unit Cost")
{
}
dataitem(Item_Attribute_Value_Mapping; "Item Attribute Value Mapping")
{
DataItemLink = "No." = Item."No.";
column(Item_Attribute_ID; "Item Attribute ID")
{
}
column(Item_Attribute_Value_ID; "Item Attribute Value ID")
{
}
dataitem(QueryElement6; "Item Attribute")
{
DataItemLink = ID = Item_Attribute_Value_Mapping."Item Attribute ID";
column(Name; Name)
{
}
dataItem(Queryelement10; "Item Attribute Value")
{
DataItemLink = "Attribute ID" = Item_Attribute_Value_Mapping."Item Attribute ID",
ID = Item_Attribute_Value_Mapping."Item Attribute Value ID";
column(Value; Value)
{
}
column(Numeric_Value; "Numeric Value")
{
}
}
}
}
}
}
}
once this code gets successfully uploaded to your server and returning a page (you have to wait for it), you can then use specified query number to expose the data in the API by going to business central's "web services" and adding a 'Query' to item 50102 (or whatever number you use). the endpoint will automatically be populated and you can use it to send you back the necessary JSON which will show a product, with its attribute values.
hope that helps.
You should try with below endpoint:
/v2.0/tenant_id/enviornment_name/ODataV4/Company(company_id)/Items

not positioned on a valid record

Im New in Qt
I have problem in my app.
This is my code:
voidDialog::on_tableView_activated(constQModelIndex&index)
{
QString valu=ui->tableView->model()->data(index).toString();
QSqlQuery qryview;
qryview.prepare("SELECT* FROM employ where nomper='"+valu+"' or prenomper='"+valu+"'");
ui->lineEdit_2->setText(qryview.value(0).toString());
ui->lineEdit_3->setText(qryview.value(1).toString());
}
I want to recuperate my data in QLineEdit.
When I use SQLite, there is no problem.
But when I use MySQL no result shows in lineEdit.
qsqlquery::value: not positioned on a valid record.
The problem is that you are trying to retrieve the row without initializing it. The right way to do this is by using query.next(), below is the code example:
QString valu=ui->tableView->model()->data(index).toString();
QSqlQuery qryview;
qryview.prepare("SELECT* FROM employ where nomper='"+valu+"' or prenomper='"+valu+"'");
qryview.exec();
while(qryview.next()) {
ui->lineEdit_2->setText(qryview.value(0).toString());
ui->lineEdit_3->setText(qryview.value(1).toString());
}
This should work, if not try to print the lastQueryError and see if query is executed coreectly.

Qt and SQLite, error when executing query, without error message

I am using Qt 5.4 and SQLite to to database operations, all other requests work, but this one doesn't seem to work and gives no error.
QSqlQuery query(Globals::db);
QString cmd = "Update VideoFile set isVisible = 0 WHERE Id =1;";
if(query.prepare(cmd))
qDebug("Prepare success..."); //<-- prints out
if (!query.exec()) {
qDebug("Error occurred querying.");
qDebug("%s.", qPrintable(Globals::db.lastError().text())); //<<-- prints out blank
}
Tried so far:
o) Database: exists!
o) Query Prepare() yields True
o) When executing the same Statement in SQLite Browser. Works!
i read about a problem here:, that some databases have a certain delay.
"Portability note: Some databases choose to delay preparing a query until it is executed the first time. In this case, preparing a syntactically wrong query succeeds, but every consecutive exec() will fail." But apparently, the query is not syntactically wrong.

Correct way to use QVariant when extracting data from a database

I have recently begun working with QT and I'm having a small issue. I cant seem to understand how QVariant works. I've been going through the help and looking online but it is just not sinking in. In my project i am trying to populate a combo box with a list of manufacturers from a database. I have opened the database and pulled out the entries but I rename them all manValue. Now I understand why this is happening, the problem is I do not understand how to properly use QVariant to get the result I want. Originally i thought "manValue" was going to be the identifier for the string that held the actual value from the database but instead it reads the value from the database and makes sure it is not null, and then renames it. I already tried making a string before I assign the QVariant with any properties and assigning it the text i received from the database, and then inserting that string where manValue is but still no luck. Any help would be greatly appreciated. Sorry, I know this is going to be simple I'm just a noob and the help docs often confuse me.
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setHostName("LOCALHOST\\TestERPServer");
db.setDatabaseName("TestERPConnection");
if (db.open())
{
QMessageBox::information(this,"Connected","Connection to the Database was Established\n"
"\nStatus: Connected");
QSqlQuery mfrQry;
if (mfrQry.exec("SELECT * FROM erp_data.manufacturers;"))
{
if (mfrQry.value(1) == "")
{
QMessageBox::information(this,"No Connection","Nothing in the Manufacturer Database\n"
"\nError: " + db.lastError().text());
}
else
{
while (mfrQry.next())
{
ui->mfrComboBox->addItem("manValue",QVariant(mfrQry.value(1)));
}
}
}
else
{
QMessageBox::information(this,"No Connection","Connection to the Manufacturer Database could not be Established\n"
"\nError: " + db.lastError().text());
}
}
else
{
QMessageBox::information(this,"No Connection","Connection to the Database could not be Established\n"
"\nError: " + db.lastError().text());
}
The first problem in the provided code has to do with the way you manipulate the QSqlQuery.
Successfully executed SQL statements set the query's state to active
so that isActive() returns true. Otherwise the query's state is set to
inactive. In either case, when executing a new SQL statement, the
query is positioned on an invalid record. An active query must be
navigated to a valid record before values can be retrieved.
In order to move to a valid record you have to use one of the following:
next()
previous()
first()
last()
seek()
Once you are on a valid record you have to use the value() function in order to take the column you want. The returned value is QVariant so you need to convert to the desired type using one of the many toSomething functions QVariant provides.
So in your case the code should look like this:
QSqlQuery mfrQry;
if (mfrQry.exec("SELECT * FROM erp_data.manufacturers;"))
{
// This will loop through all records returned by the query
while (mfrQry.next()) {
// mfrQry.value(COLID) returns a QVariant containing the data of the current
// record in column COLID.
// Using toString we convert it to String
QString stringValue = mfrQry.value(COLID).toString();
// Now handle the QString the way you want...
}
}