Invalid Argument to getUInt64 when retrieving LAST_INSERT_ID() - c++

I have added a record to my table which auto-increments the primary key. I am having no luck retrieving this new value. The MySQL documents say to use the SELECT LAST_INSERT_ID(); in a query. I have done this, but can't retrieve the results.
According the the metadata of the result set, the data type is BIGINT and the column name is LAST_INSERT_ID(). The C++ connector has a getUInt64() for the result set, which I assume is the correct method to use.
The ResultSet class declaration contains the following:
virtual uint64_t getUInt64(uint32_t columnIndex) const = 0;
virtual uint64_t getUInt64(const std::string& columnLabel) const = 0;
The documentation does not state whether the columnIndex is zero based or one based. I tried both and get sql::InvalidArgumentException for both cases.
Using the result set metadata, I retrieved the column name and passed it directly to the getUInt64 method and still receive the sql::InvalidArgumentException. This not a good indication (when the returned column name doesn't work when fetching the data).
Here is my code fragment:
std::string query_text;
query_text = "SELECT LAST_INSERT_ID();";
boost::shared_ptr<sql::Statement> query(m_db_connection->createStatement());
boost::shared_ptr<sql::ResultSet> query_results(query->executeQuery(query_text));
long id_value = 0;
if (query_results)
{
ResultSetMetaData p_metadata = NULL;
p_metadata = query_results->getMetaData();
unsigned int columns = 0;
columns = p_metadata->getColumnCount();
std::string column_label;
std::string column_name;
std::string column_type;
for (i = 0; i < columns; ++i)
{
column_label = p_metadata->getColumnLabel(i);
column_name = p_metadata->getColumnName(i);
column_type = p_metadata->getColumnTypeName(i);
wxLogDebug("Column label: \"%s\"\nColumn name: \"%s\"\nColumn type: \"%s\"\n",
column_label.c_str(),
column_name.c_str(),
column_type.c_str());
}
unsigned int column_index = 0;
column_index = query_results->findColumn(column_name);
// The value of column_index is 1 (one).
// All of the following will generate sql::InvalidArgumentException
id_value = query_results->getUInt64(column_index);
id_value = query_results->getUInt64(column_name);
id_value = query_results->getUInt64(0);
id_value = query_results->getUInt64(1);
id_record.set_record_id(id_value);
}
Here is the debug output (from wxLogDebug):
10:50:58: Column label: "LAST_INSERT_ID()"
Column name: "LAST_INSERT_ID()"
Column type: "BIGINT"
My Question: How do I retrieve the LAST_INSERT_ID() using the MySQL C++ Connector?
Do I need to use a prepared statement instead?
I am using MySQL Connector C++ 1.0.5 on Windows Vista and Windows XP with Visual Studio 9 (2008).

Resolved.
I inserted a query_results->next() before retrieving the data and that worked.

Related

Custom CRecordset class does not call DoFieldExchange() when useMultiRowFetch is specified

I've implemented a custom CRecordset class, and have code similar to the following:
ASSERT(prs->GetRowsetSize() == 25);
while (!prs->IsEOF())
{
for (int i = 1; i <= prs->GetRowsFetched(); i++)
{
prs->SetRowsetCursorPosition((WORD)i);
// Inspecting data here...
}
prs->MoveNext();
}
prs->Close();
Apparently, when using multi-row fetch, CRecordset does not call my DoFieldExchange override as it does when not using multi-row fetch, and that is by design. And so my data isn't automatically populated. So the question is how do I get the data?
The answer appears to be by calling GetFieldValue(). But I get an Invalid cursor position error when I do! (GetFieldValue() works fine when I'm not using multi-row fetch.)
Below is a streamlined version of my recordset class. In addition, #EylM was good enough to create a sample in the answers below that he says does work for him. However, when I copied his code exactly and just changed what was needed to connect to and query my database, I still get an Invalid cursor position when I call GetFieldValue().
I don't know what else could be different. I see he's using MySQL where I'm using SQL Server. But surely CRecordset works with SQL Server. I've also tried all the available SQL Server ODBC drivers, but the result is always the same.
class CRS : public CRecordset
{
public:
// Data variables
int m_nId;
TCHAR m_szName[CUSTOMER_NAME_MAXLENGTH + 1];
// Bulk data variables
int* m_pnIds;
long* m_pnIdLengths;
LPTSTR m_pszNames;
long* m_pnNameLengths;
// Constructor
CRS(CDatabase* pDatabase = NULL)
: CRecordset(pDatabase)
{
m_nFields = 2;
m_nId = 0;
m_szName[0] = '\0';
m_pnIds = NULL;
m_pnIdLengths = NULL;
m_pszNames = NULL;
m_pnNameLengths = NULL;
}
CString GetDefaultSQL()
{
return CCustomerData::m_szTableName;
}
// This method is never called when
// CRecordset::useMultiRowFetch is specified!
void DoFieldExchange(CFieldExchange* pFX)
{
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Int(pFX, _T("Id"), m_nId);
RFX_Text(pFX, _T("Name"), m_szName, CUSTOMER_NAME_MAXLENGTH);
}
// This method is called several times
void DoBulkFieldExchange(CFieldExchange* pFX)
{
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Int_Bulk(pFX, _T("Id"), &m_pnIds, &m_pnIdLengths);
RFX_Text_Bulk(pFX, _T("Name"), &m_pszNames, &m_pnNameLengths, (CUSTOMER_NAME_MAXLENGTH + 1) * 2);
}
};
UPDATE:
Spending more time on this, I have been able to write code that reads the data directly from the rowset data (in my case, from m_pnIds, m_pnIdLengths, m_pszNames and m_pnNameLengths). Perhaps that's the approach I need to take.
But the question still stands. Why can't I use GetFieldValue() on a SQL Server database? And what is the point of SetRowsetCursorPosition()?
From documentation of CRecordset::DoFieldExchange:
When bulk row fetching is not implemented, the framework calls this
member function to automatically exchange data between the field data
members of your recordset object and the corresponding columns of the
current record on the data source.
DoFieldExchange is called only if CRecordset::useMultiRowFetch is not specified in the Open function.
Looking at MFC code CRecordset::BindFieldsToColumns, dbcore.cpp using VS 2019 (14.22.27905):
// Binding depends on fetch type
if (m_dwOptions & useMultiRowFetch)
DoBulkFieldExchange(&fx);
else
DoFieldExchange(&fx);
Sounds like that behaviour your are getting is by design.
Edit:
Here is working example for multi row fetch. The thing that did the trick is CRecordset::useExtendedFetch in the opening flags.
Database:
I used MySQL with a simple table with 2 columns. Here is the creation script.
CREATE TABLE `categories` (
`CatID` int(11) NOT NULL,
`Category` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`CatID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
MFC:
CMultiRowSet.h
class CMultiRowSet : public CRecordset
{
public:
CMultiRowSet(CDatabase* pDB);
virtual void DoBulkFieldExchange(CFieldExchange* pFX);
// Field/Param Data
// field data members
long* m_rgID;
LPSTR m_rgName;
// pointers for the lengths
// of the field data
long* m_rgIDLengths;
long* m_rgNameLengths;
};
CMultiRowSet.cpp
void CMultiRowSet::DoBulkFieldExchange(CFieldExchange* pFX)
{
// call the Bulk RFX functions
// for field data members
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Long_Bulk(pFX, _T("[CatID]"),
&m_rgID, &m_rgIDLengths);
RFX_Text_Bulk(pFX, _T("[Category]"),
&m_rgName, &m_rgNameLengths, 30);
}
Usage:
CDatabase database;
CString sCatID, sCategory;
TRY
{
CString connStr = (_T("Driver={MySQL ODBC 8.0 Unicode Driver};Server=localhost;Database=XXXX;User=XXX; Password=XXXX; Option = 3;"));
// Open the database
database.OpenEx(connStr,CDatabase::noOdbcDialog);
// Allocate the recordset
CMultiRowSet recset(&database);
// Execute the query
// make sure you use CRecordset::useExtendedFetch.
recset.Open(CRecordset::forwardOnly, _T("SELECT CatID, Category FROM Categories"), CRecordset::readOnly|CRecordset::useMultiRowFetch|CRecordset::useExtendedFetch);
// Loop through each record
while (!recset.IsEOF())
{
// The default `GetRowsetSize` is 25. I have 4 rows in my database.
// GetRowsFetched returns 4 in my case.
for (int rowCount = 1; rowCount <= (int)recset.GetRowsFetched(); rowCount++)
{
recset.SetRowsetCursorPosition(rowCount);
// Copy each column into a variable
recset.GetFieldValue(_T("CatID"), sCatID);
recset.GetFieldValue(_T("Category"), sCategory);
}
// goto next record
recset.MoveNext();
}
recset.Close();
// Close the database
database.Close();
}
CATCH(CDBException, e)
{
// If a database exception occured, show error msg
AfxMessageBox(_T("Database error: ") + e->m_strError);
}
END_CATCH;

Poco c++ How to read "text" data type from PostgreSQL DB?

I am executing a database (PostgreSQL) query.
Сomment column has "text" data type.
SELECT Comment FROM table WHERE id = 1;
RecordSet result = query->execute("");
bool more = result .moveFirst();
while (more)
{
std::string comment = result["Comment"].convert<std::string>());
or
std::string comment = result["Comment"].extract<std::string>());
more = result.moveNext();
}
I get an exception
Poco::SQL::UnknownTypeException
How can I read a field without changing the database data type?
I figured out
Poco::SQL::MetaColumn::ColumnDataType type = result.columnType("Comment");
type is Poco::SQL::MetaColumn::ColumnDataType::FDT_CLOB
Poco::SQL::CLOB comment = response["Comment"].extract<Poco::SQL::CLOB>();
Look the same https://github.com/pocoproject/poco/issues/2566 if POCO version < 1.9.1

Issue in getting a VARCHAR2 type column using getString() in OCCI

I have to read a column of VARCHAR2 type from Oracle table in my CPP code. When I tried to get the VARCHAR2 as a string using getString() of OCCI call, the code fails.
In the below code, I am concerned in getting table column COST_VALUE of VARCHAR2<50>
Code:
string sqlStmt = "SELECT \”REJECTED COST\”, APPROVED_COST, COST_VALUE FROM COST_TABLE where PART_NUM= 'PN4879-1'";
stmt = conn->createStatement(sqlStmt);
ResultSet *rset = stmt->executeQuery();
double dRejCost = 0;
double dAppCost = 0;
if(rset->next())
{
dRejCost = rset->getNumber(1);
dAppCost = rset->getNumber(2);
string strCost = rset->getString(3);
}
stmt->closeResultSet(rset);
conn->terminateStatement(stmt);
Error:
When I debugged I was able to see the value (from COST_TABLE column) being fetched in the string. But after the line (string strCost = rset->getString(3);) is executed, the application failed without any exception.
Please help with your expertise.

Update function broken in C++ Azure Mobile Library

I am trying to update a record in one of my Azure Mobile tables using the “update” function in the azure mobile C++ header. But I get an exception. Below is what my code looks like:
void DBUtils::DBQuestion::UpdateQuestionInTable(std::shared_ptr<azure::mobile::table> table)
{
auto obj = json::value::object();
obj[U("id")] = json::value::string(ID);
obj[U("QuestionText")] = json::value::string(QuestionText);
obj[U("AnswerLatitude")] = json::value::number(AnswerLatitude);
obj[U("AnswerLongitude")] = json::value::number(AnswerLongitude);
table->update(obj);
}
I have verified that the ID above is a valid one actually present in the table. A similar insert operation (which doesn’t specify the ID field) actually succeeds:
void DBUtils::DBQuestion::InsertIntoTable(std::shared_ptr<azure::mobile::table> table)
{
auto obj = json::value::object();
obj[U("QuestionText")] = json::value::string(QuestionText);
obj[U("AnswerLatitude")] = json::value::number(AnswerLatitude);
obj[U("AnswerLongitude")] = json::value::number(AnswerLongitude);
table->insert(obj);
}
What am I doing wrong?
Azure Mobile recently updated its table schema so that the Id field is now a string, which gets filled in by the server with a Guid value if the client doesn’t set it.
This change has introduced a bug in the C++ library. As a workaround, you can try calling the other overload for update, the one that takes the ID string and the object.
void DBUtils::DBQuestion::UpdateQuestionInTable(utility::string_t id, std::shared_ptr<azure::mobile::table> table)
{
auto obj = json::value::object();
obj[U("QuestionText")] = json::value::string(QuestionText);
obj[U("AnswerLatitude")] = json::value::number(AnswerLatitude);
obj[U("AnswerLongitude")] = json::value::number(AnswerLongitude);
ID = id;
table->update(ID, obj);
}

How to get VARCHAR2(n) limit using OCCI?

I have a table with multiple VARCHAR2(n) columns ('n' differs for each column). I access the table from a C++ application via OCCI library. Is there a way to get this limit value (n) for a column using the OCCI library?
That can be done using MetaData. Here is the sample:
MetaData metaData = connection->getMetaData((utext*)L"\"TableName\"", MetaData::PTYPE_TABLE);
vector<MetaData> metaDataVector = metaData.getVector(MetaData::ATTR_LIST_COLUMNS);
for (UINT i = 0; i < (UINT)metaDataVector.size(); i++)
{
if(metaDataVector[i].getInt(MetaData::ATTR_DATA_TYPE) == OCCI_SQLT_CHR)
columnInfo.size = metaDataVector[i].getInt(MetaData::ATTR_DATA_SIZE);
}