I don't have problem inserting UTF-8 (korean character in my case) using direct sql command in PGAdmin or psql console. But when I need to insert values in C++ by using libpq library, I got the encoding error and I couldn't resolve it until now.
I have tested DB connection, etc and works well. So I will just share the insertion code only :
/* INSERT demo */
res = PQexec(conn,
"insert into db_test values('testval', '군포지사','N00225','영동선','0500','E',13.67,14.18);");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
std::cout << "Insert into table failed: " << PQresultErrorMessage(res)
<< std::endl;
}
else
std::cout << "inserted" << std::endl;
PQclear(res);
Below is the error :
Insert into table failed: ERROR: invalid byte sequence for encoding "UTF8": 0xb1
I have managed to solve it, sorry for my lack of understanding in encoding processing. I just need to convert ANSI to UTF-8, below is my working code. If there's a better solution, please post your answer.
int AnsiToUTF8(char* szSrc, char* strDest, int destSize)
{
WCHAR szUnicode[255];
char szUTF8code[255];
int nUnicodeSize = MultiByteToWideChar(CP_ACP, 0, szSrc, (int)strlen(szSrc), szUnicode, sizeof(szUnicode));
int nUTF8codeSize = WideCharToMultiByte(CP_UTF8, 0, szUnicode, nUnicodeSize, szUTF8code, sizeof(szUTF8code), NULL, NULL);
assert(destSize > nUTF8codeSize);
memcpy(strDest, szUTF8code, nUTF8codeSize);
strDest[nUTF8codeSize] = 0;
return nUTF8codeSize;
}
/* INSERT demo */
char queryUtf8[100];
std::string queryAnsi = "insert into db_test values('testval', '군포지사','N00225','영동선','0500','E',13.67,14.18);";
AnsiToUTF8(&queryAnsi[0u], queryUtf8, 100);
res = PQexec(conn, queryUtf8);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
std::cout << "Insert into table failed: " << PQresultErrorMessage(res)
<< std::endl;
}
else
std::cout << "inserted" << std::endl;
PQclear(res);
The proper solution is to set the client_encoding parameter to the correct value (the encoding your client uses). You can do that when connecting to the database:
PGconn *conn;
conn = PQconnectdb("user=carl password=secret client_encoding=LATIN1");
Related
We are using Oracle 12c database and CentOS7 with OCCI to connect. We are trying to insert a char array into the database, but this char array has a NUL character in the middle. When we use the statement->setString function the update is successful however once it sees the NUL char it only puts NUL chars there after. See this example code and it's output.
Example Code using setString:
static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
Environment *env = Environment::createEnvironment();
Connection *conn = env->createConnection(user, pwd, connectionString);
Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = :1 WHERE customer_id = :2");
stmt->setString(1, std::string("GEO\0RGE ", 20));
stmt->setInt(2, 10);
stmt->setString(1, std::string(adrs_first_name, sizeof(adrs_first_name)));
oracle::occi::Statement::Status status = stmt->execute();
conn->terminateStatement(stmt);
conn->commit();
}
Accessing the Database After the Update:
SELECT first_name FROM my_customers WHERE customer_id = 10;
GEO
SELECT rawtohex(first_name) FROM my_customers WHERE customer_id = 10;
47454F0000000000000000000000000000000000
However I would have expected it to be
47454F0047452032322020202020202020202020
So I tried using oracle::occi::Bytes-- this errors out with
ORA - 12899: value too large for column "MAIN_USER"."MY_CUSTOMERS"."FIRST_NAME" (actual : 40, maximum : 20)
Example Code using setBytes:
static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
Environment *env = Environment::createEnvironment();
Connection *conn = env->createConnection(user, pwd, connectionString);
Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = :1 WHERE customer_id = :2");
std::string s("GEO\0RGE ", 20);
oracle::occi::Bytes bytes((unsigned char *)s.c_str(), 20, 0, env);
stmt->setBytes(1, bytes);
stmt->setInt(2, 10);
try
{
oracle::occi::Statement::Status status = stmt->execute();
}
catch (oracle::occi::SQLException &e)
{
std::cout << "Error " << e.getErrorCode() << ": " << e.what() << std::endl;
}
conn->terminateStatement(stmt);
conn->commit();
}
Output:
Error 12899 : ORA - 12899 : value too large for column "MAIN_USER"."MY_CUSTOMERS"."FIRST_NAME" (actual : 40, maximum : 20)
So I tried sending in half the bytes by changing the 2nd parameter of the oracle::occi::Bytes constructor to 10, and it succeeded however after reading the value from the database I realized it is a string representation of the hex value of the chars. So my question at this point is why is Oracle12c putting the hex value as a string when I pass oracle::occi:Bytes.
Example Code using half the actual length and setBytes:
static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
Environment *env = Environment::createEnvironment();
Connection *conn = env->createConnection(user, pwd, connectionString);
Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = :1 WHERE customer_id = :2");
std::string s("GEO\0RGE ", 20);
oracle::occi::Bytes bytes((unsigned char *)s.c_str(), 10, 0, env);
stmt->setBytes(1, bytes);
stmt->setInt(2, 10);
try
{
oracle::occi::Statement::Status status = stmt->execute();
}
catch (oracle::occi::SQLException &e)
{
std::cout << "Error " << e.getErrorCode() << ": " << e.what() << std::endl;
}
conn->terminateStatement(stmt);
conn->commit();
}
Accessing the Database After the Update :
SELECT first_name FROM my_customers WHERE customer_id = 10;
47454F00524745202020
NOTE : This query did not wrap with rawtohex-- this is the actual char array value in the database.
Here is the table definition:
DESCRIBE MAIN_USER.MY_CUSTOMERS
Name Null Type
------------------------------ ---- --------------
CUSTOMER_ID NUMBER(10)
FIRST_NAME CHAR(20 CHAR)
Here is our Oracle instance information :
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Advanced Analytics and Real Application Testing options
We are using Oracle occi client 12.1 64bit
For those that may stumble upon this, I ended up talking with Oracle Support and they told me there wasn't a way to do what I wanted (as expected) however the Byte workaround got us closer and we were able to take the hex string being passed in by the setBytes and turn it into raw data and then convert that raw data to a varchar2 which ended up "working" -- however I don't know what implications we will run into later, but so far it seems to work.
Code:
static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
Environment *env = Environment::createEnvironment();
Connection *conn = env->createConnection(user, pwd, connectionString);
Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = utl_raw.cast_to_varchar2(hextoraw(:1)) WHERE customer_id = :2");
std::string s("GEO\0RGE ", 20);
oracle::occi::Bytes bytes((unsigned char *)s.c_str(), 20, 0, env);
stmt->setBytes(1, bytes);
stmt->setInt(2, 10);
try
{
oracle::occi::Statement::Status status = stmt->execute();
}
catch (oracle::occi::SQLException &e)
{
std::cout << "Error " << e.getErrorCode() << ": " << e.what() << std::endl;
}
conn->terminateStatement(stmt);
conn->commit();
}
Then checking the database:
SELECT rawtohex(first_name) FROM my_customers WHERE customer_id = 10;
47454F0052474520202020202020202020202020
So it seems to work
I've got a fairly simple c++ application that uses ODBC to connect to a SQL Server instance that uses a stored procedure to populate.
I use the lines of a wstring object to build a query that is then passed through to the stored procedure. Everything works fine if I run it a single time - however, I want to be able to loop through a quite extensive amount of code (3000+ excel rows) and as I do it the cursor error mentioned in the title occurs.
This is the stored procedure:
GO
CREATE PROCEDURE s_addHistorical
#Symbol nchar(10),#Date datetime,
#Open decimal(8,2),#Close decimal(8,2),#MinPrice decimal(8,2),
#MaxPrice decimal(8,2),#Volume int
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
MERGE HistoricalStock WITH (UPDLOCK) AS myTarget
USING (SELECT #Symbol AS Symbol,
#Date AS Date, #Open AS [Open], #Close AS [Close],
#MinPrice AS MinPrice, #MaxPrice AS MaxPrice,#Volume AS Volume) AS mySource
ON mySource.Symbol = myTarget.Symbol AND mySource.Date = myTarget.Date
WHEN MATCHED
THEN UPDATE
SET [Open] = mySource.[Open], [Close] = mySource.[Close],
MinPrice = mySource.MinPrice, MaxPrice = mySource.MaxPrice, Volume = mySource.Volume
WHEN NOT MATCHED
THEN
INSERT(Symbol,Date,[Open],[Close],MinPrice,MaxPrice,Volume)
VALUES(#Symbol,#Date,#Open,#Close,#MinPrice,#MaxPrice,#Volume);
COMMIT
GO
And this is the connector:
#include "stdafx.h"
#include "database_con.h"
////////////////////////////////////////////////////////////////////////
// Show errors from the SQLHANDLE
void database_con::show_error(unsigned int handletype, const SQLHANDLE& handle)
{
SQLWCHAR sqlstate[1024];
SQLWCHAR message[1024];
if (SQL_SUCCESS == SQLGetDiagRec(handletype, handle, 1, sqlstate, NULL, message, 1024, NULL))
wcout << "Message: " << message << "\nSQLSTATE: " << sqlstate << endl;
}
////////////////////////////////////////////////////////////////////////
// Builds the stored procedure query.
std::wstring database_con::buildQuery(vector<std::wstring> input)
{
std::cout << "Building the query" << std::endl;
std::wstringstream builder;
builder << L"EXEC sp_addHistorical " << "#Symbol='" << L"" << input.at(0) << "'," <<
"#Date=" << (wstring)L"" << input.at(1) << "," <<
"#Open=" << (wstring)L"" << input.at(2) << "," <<
"#Close=" << (wstring)L"" << input.at(3) << "," <<
"#MaxPrice=" << (wstring)L"" << input.at(4) << "," <<
"#MinPrice=" << (wstring)L"" << input.at(5) << "," <<
"#Volume=" << (wstring)L"" << input.at(6) << ";";
return builder.str();
}
////////////////////////////////////////////////////////////////////////
// Adds a substring of the string before the delimiter to a vector<wstring> that is returned.
std::vector<wstring> database_con::parseData(wstring line, char delim) {
size_t pos = 0;
std::vector<std::wstring> vOut;
while ((pos = line.find(delim)) != std::string::npos) {
vOut.push_back(line.substr(0, pos));
line.erase(0, pos + 2);
}
vOut.push_back(line.substr(0, pos));
return vOut;
}
std::wstring database_con::StringToWString(const std::string& s)
{
std::wstring temp(s.length(), L' ');
std::copy(s.begin(), s.end(), temp.begin());
return temp;
}
database_con::database_con(std::string historical){
/*
Set up the handlers
*/
/* Allocate an environment handle */
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
/* We want ODBC 3 support */
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
/* Allocate a connection handle */
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
/* Connect to the DSN */
SQLDriverConnectW(dbc, NULL, L"DRIVER={SQL Server};SERVER=ERA-PC-STUART\\JBK_DB;DATABASE=master;UID=geo;PWD=kalle123;", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
/* Check for success */
if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt))
{
show_error(SQL_HANDLE_DBC, dbc);
std::cout << "Failed to connect";
}
std::wstringstream stream(StringToWString(historical));
std::wstring line;
while (std::getline(stream, line)) {
vector<wstring> vHistorical = parseData(L"" + line, ',');
std::wstring SQL = buildQuery(vHistorical);
if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS)) {
show_error(SQL_HANDLE_STMT, stmt);
}
}
}
database_con::~database_con() {
}
I've been looking around at google and on SO, but I can't seem to find any questions that are usable in my current question. Most of them seem to be revolving around stored procedures sending back some sort of extracts, whereas my SP is simply inserting / updating.
Any sort of help would be greatly appriciated. :)
Anyone?
You need to call SQLCloseCursor to release the Cursor.
Change your code to:
while (std::getline(stream, line)) {
vector<wstring> vHistorical = parseData(L"" + line, ',');
std::wstring SQL = buildQuery(vHistorical);
if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS)) {
show_error(SQL_HANDLE_STMT, stmt);
}
// Close Cursor before next iteration starts:
SQLRETURN closeCursRet = SQLCLoseCursor(stmt);
if(!SQL_SUCCEEDED(closeCursRet))
{
// maybe add some handling for the case that closing failed.
}
}
See: https://msdn.microsoft.com/en-us/library/ms709301%28v=vs.85%29.aspx
I try to attach in memory db to an existing db connection via c++ api like this:
void InMemoryDbtest(){
sqlite3 *db;
int rc = sqlite3_open("E:/VS_workspace/ConsoleSQLITE/Debug/TPCH.sqlite", &db);
if (rc){
cout << "nie moge otworzyc bazy danych ";}
else{
cout << "otwarcie bazy ---- udalo sie\n";}
char *err;
sqlite3_exec(db, "ATTACH DATABASE 'file::memory:?cache=shared' as pom",0,0,&err);
cout << "error" << err << endl;
string q = "create table pom.bla as select n_name from NATION ";
sqlite3_exec(db, q.c_str(), 0, 0, &err);
cout << "error" << err << endl;
}
I get 2 error messages"
unable to open database: file:memory
and
unknown database pom
Any ideas? When i try to attach db in sqlite console everything works just fine
Thank you for your suggestions the solution is:
first : set SQLITE_USE_URI flag in sqlite3.c to 1 instead of 0 (just using SQLITE_OPEN_URI flag with sqlite3_open_v2() wsn't enough)
#ifndef SQLITE_USE_URI
# define SQLITE_USE_URI 1
#endif
then you can use uri's with all sqlite3 functions.
proper uri for windows is:
int rc2 = sqlite3_open("file:///E:/VS_workspace/ConsoleSQLITE/Debug/TPCH.sqlite", &db);
I am using SQLite3 header files in my C++ program and trying to create a table and insert data onto it, it works fine on a regular input.
It shows error when I use it in a C++ loop with changing variables.
I am using the database to insert my reading from RS-232.
Here is my code:
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
std::string sql_str;
std::ostringstream temp;
std::string command;
/* Open database */
rc = sqlite3_open("test_1.db", &db);
if (rc){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else{
fprintf(stderr, "Opened database successfully\n");
}
std::string str;
std::ostringstream oss;
oss << id_count; // stornig the primary id int values into a string
str = "INSERT INTO M_DATA (ID, DETAILS) VALUES(";
str += oss.str(); //copying the int primary id
str += ", '";
std::string str_t1(szBuffer); //Copying character aray to a string
str += str_t1; //concatening the string
str += "');";
//printing what the database takes
//output_file << std::endl << str << std::endl;
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0'; // don't forget the terminating 0
sql = writable;
output_file << std::endl << "## SQL COMMAND : " << sql << "#" << std::endl;
// don't forget to free the string after finished using it
delete[] writable;
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if (rc != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
output_file << std::endl << "** SQL ERROR : " << zErrMsg << "*" << std::endl;
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Records created successfully\n");
}
// _sleep(3000);
sqlite3_close(db);
My issue is I have a szBuffer which changes everytime, and I have to insert it as a new entry into the table.
Is there a way to increment the Primary Key and store my string into it?
The sz buffer at a single line will give data like: For Ex:
szBuffer : ersion = 1 [SPI]: MinorVersion = 2 [SPI]: Real Time
= 1434260351 [SPI]: SR # = SBB-ST1000090
The SQL command in the string I pass is like this:
SQL COMMAND : INSERT INTO M_DATA (ID, DETAILS) VALUES(9,
'ersion = 1 [SPI]: MinorVersion = 2 [SPI]: Real Time = 1434260351
[SPI]: SR # = SBB-ST1000090');
The Error which I get is like:
SQL ERROR : near "¸”_": syntax error
I am not sure if I am doing this right or wrong.
Can we use the insert statement in a loop? Am I passing the string the right way? (It looks correct to me when I print it out.)
But why do I get an error?
Is there any better way to enter my data?
I am very new to this so I tried search the internet, but no one is doing it the way I did it.
Please help.
Many Thanks.
(Almost) never build a SQL statement via string concatenation. Use a prepared statement and bind the parameter values.
// Prepare the statement
sqlite3_stmt* stmt;
int result = sqlite3_prepare_v2(db, "INSERT INTO M_DATA (ID, DETAILS) VALUES(?, ?);", -1, &stmt, nullptr);
// TODO: Handle when result != SQLITE_OK
while(/* whatever you wanted to loop on */)
{
// Bind in the parameter values
result = sqlite3_bind_int(stmt, 1, id_count);
// TODO: Handle when result != SQLITE_OK
result = sqlite3_bind_text(stmt, 2, szBuffer, -1, SQLITE_STATIC);
// TODO: Handle when result != SQLITE_OK
// Invoke the statement
result = sqlite3_step(stmt);
// TODO: Handle when result != SQLITE_OK
// Reset the statement to allow binding variables on the next iteration
result = sqlite3_reset(stmt);
}
// Release the statement
sqlite3_finalize(stmt);
I am trying to create a database in c++ using sqlite3 lib.. I am getting error sqlite3_prepare_v2'
was not declared in this scope as shown in logcat.
log file
..\src\Test.cpp: In function 'int main(int, const char**)':
..\src\Test.cpp:21:85: error: 'sqlite3_prepare_v2' was not declared in this scope
..\src\Test.cpp:30:13: error: variable 'sqlite3 in' has initializer but incomplete type
..\src\Test.cpp:30:30: error: invalid use of incomplete type 'sqlite3 {aka struct sqlite3}'
..\src\/sqlite3.h:73:16: error: forward declaration of 'sqlite3 {aka struct sqlite3}'
Here is my code
#include <iostream>
using namespace std;
#include "sqlite3.h"
int main (int argc, const char * argv[]) {
sqlite3 *db;
sqlite3_open("test.db", & db);
string createQuery = "CREATE TABLE IF NOT EXISTS items (busid INTEGER PRIMARY KEY, ipaddr TEXT, time TEXT NOT NULL DEFAULT (NOW()));";
sqlite3_stmt *createStmt;
cout << "Creating Table Statement" << endl;
sqlite3_prepare_v2(db, createQuery.c_str(), createQuery.size(), &createStmt, NULL);
cout << "Stepping Table Statement" << endl;
if (sqlite3_step(createStmt) != SQLITE_DONE) cout << "Didn't Create Table!" << endl;
string insertQuery = "INSERT INTO items (time, ipaddr) VALUES ('test', '192.168.1.1');"; // WORKS!
sqlite3_stmt *insertStmt;
cout << "Creating Insert Statement" << endl;
sqlite3_prepare(db, insertQuery.c_str(), insertQuery.size(), &insertStmt, NULL);
cout << "Stepping Insert Statement" << endl;
if (sqlite3_step(insertStmt) != SQLITE_DONE) cout << "Didn't Insert Item!" << endl;
cout << "Success!" << endl;
return 0;
}
please help me out. thanks.....
#include <sqlite3.h>
should contain sqlite3_prepare_v2 and struct sqlite3. Make sure you're including the right sqlite3.h file.
Also in sqlite3_prepare_v2 the 3rd arg can be (and should be in your case) -1 so the sql is read to the first null terminator.
Working bare-metal sample using sqlite 3.7.11:
#include <sqlite3.h>
int test()
{
sqlite3* pDb = NULL;
sqlite3_stmt* query = NULL;
int ret = 0;
do // avoid nested if's
{
// initialize engine
if (SQLITE_OK != (ret = sqlite3_initialize()))
{
printf("Failed to initialize library: %d\n", ret);
break;
}
// open connection to a DB
if (SQLITE_OK != (ret = sqlite3_open_v2("test.db", &pDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)))
{
printf("Failed to open conn: %d\n", ret);
break;
}
// prepare the statement
if (SQLITE_OK != (ret = sqlite3_prepare_v2(pDb, "SELECT 2012", -1, &query, NULL)))
{
printf("Failed to prepare insert: %d, %s\n", ret, sqlite3_errmsg(pDb));
break;
}
// step to 1st row of data
if (SQLITE_ROW != (ret = sqlite3_step(query))) // see documentation, this can return more values as success
{
printf("Failed to step: %d, %s\n", ret, sqlite3_errmsg(pDb));
break;
}
// ... and print the value of column 0 (expect 2012 here)
printf("Value from sqlite: %s", sqlite3_column_text(query, 0));
} while (false);
// cleanup
if (NULL != query) sqlite3_finalize(query);
if (NULL != pDb) sqlite3_close(pDb);
sqlite3_shutdown();
return ret;
}
Hope this helps
Guys , creating database using sqlite3 in c/c++, here I'm using follwing steps...
1) Firstly you include MinGw file .
2) Add header file sqlite3.h, sqlite3.c in your src folder.
3) Add libr folder , in libr here include these file
mysqlite.h, shell.c, sqlite3.c, sqlite3.h, sqlite3ext.h
After then start your coding...
#include <iostream>
using namespace std;
#include "sqlite3.h"
int main (int argc, const char * argv[]) {
sqlite3 *db;
sqlite3_open("test1.db", & db);
string createQuery = "CREATE TABLE IF NOT EXISTS items (userid INTEGER PRIMARY KEY, ipaddr
TEXT,username TEXT,useradd TEXT,userphone INTEGER,age INTEGER, "
"time TEXT NOT NULL DEFAULT
(NOW()));";
sqlite3_stmt *createStmt;
cout << "Creating Table Statement" << endl;
sqlite3_prepare(db, createQuery.c_str(), createQuery.size(), &createStmt, NULL);
cout << "Stepping Table Statement" << endl;
if (sqlite3_step(createStmt) != SQLITE_DONE) cout << "Didn't Create Table!" << endl;
string insertQuery = "INSERT INTO items (time, ipaddr,username,useradd,userphone,age)
VALUES ('7:30', '192.187.27.55','vivekanand','kolkatta','04456823948',74);"; // WORKS!
sqlite3_stmt *insertStmt;
cout << "Creating Insert Statement" << endl;
sqlite3_prepare(db, insertQuery.c_str(), insertQuery.size(), &insertStmt, NULL);
cout << "Stepping Insert Statement" << endl;
if (sqlite3_step(insertStmt) != SQLITE_DONE) cout << "Didn't Insert Item!" << endl;
return 0;
}
go through this link. I am not sure. It might help you out.
I think their is no sqlite3_prepare_v2 in sqlite3.h lib, so try this.. sqlite3_prepare_v2 can be replaced by sqlite3_prepare, but more care is needed, because it changes the semantics of subsequent calls slightly.