Qt 5.x, ODBC (dBase, dbf), Windows, encodings - c++

The database is a set of dbf files in Windows-1251 codepage (1C 7.7, if you need to know). How do I get a UTF-8 string as a result? Somehow manages to see the desired text in console only, only with a locale: IBM 866
Code example:
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setDatabaseName("Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=/path/to/database");
db.open(); // Ok
QSqlQuery query = QSqlQuery("select descr from SC84", db);
query.next(); // Ok
// "╩єчютэюх цхыхчю"
qDebug(query.value(0).toByteArray().data());
// if setup locale ...
QTextCodec::setCodecForLocale(QTextCodec::codecForName("IBM 866"));
// "Кузовное железо" (i was looking for this russian words!)
qDebug(query.value(0).toByteArray().data());
// "╩єчютэюх цхыхчю"
QMessageBox::information(this, "test as is", query.value(0).toString());
// "тХй╤Ф╤З╤О╤В╤Н╤О╤Е ╤Ж╤Е╤Л╤Е╤З╤О" — what was wrong?
QMessageBox::information(this, "test from DOS-866",
QTextCodec::codecForName("IBM 866")->toUnicode(query.value(0).toByteArray().data()));
I understand that there is somewhere an implicit conversion. But cannot find a solution!
PS The question is closed. It was hard to guess that the reason recoding "cp1251 as cp866". The problem is that ignored the connection settings in the ODBC DSN string. Attempts to solve with the option "DataCodePage=ANSI" had failed, the option was discarded. But setting this value in the registry under\SOFTWARE\Microsoft\Jet\4.0\Engines\xBase solved the problem immediately...

In the query results cp1251 encoded as cp866, mangling text. Setup DataCodePage=ANSI" DSN ODBC is ignored. So you need to set the value of the ANSI in registry \SOFTWARE\Microsoft\Jet\4.0\Engines\xBase.

Related

ODBC Connection via CDatabase fails with connection string from previous succesfull connection

I'm a bit clueless at the moment what the problem is with this code.
It trows a exception of type CDBException when trying to connect with the given ODBC Connect String and never comes back from .OpenEx - the lower message boxes never display.
There is a message from the ODBC Driver that states that the DSN isnt specified and that no default driver is found.
The Message Box that shows the connect string gives a good looking string - as it should, coming from a working connection
ODBC;DSN=DBTEST;Trusted_Connection=Yes;APP=XXX;WSID=NEUROMANCER;DATABASE=master;
Same problem when using SA/Password for the connection
ODBC;DSN=DBTEST_PW;UID=SA;PWD=password;APP=XXX;WSID=NEUROMANCER;DATABASE=master;
After reading other posts I added "DRIVER=" to the beginning and "SERVER=localhost;" to the end but that changed nothing. Resulting tested connection strings where
DRIVER=ODBC;DSN=DBTEST;Trusted_Connection=Yes;APP=XXX;WSID=NEUROMANCER;DATABASE=master;
DRIVER=ODBC;DSN=DBTEST;Trusted_Connection=Yes;APP=XXX;WSID=NEUROMANCER;DATABASE=master;SERVER=localhost
DRIVER=ODBC;DSN=DBTEST_PW;UID=SA;PWD=password;APP=XXX;WSID=NEUROMANCER;DATABASE=master;
DRIVER=ODBC;DSN=DBTEST_PW;UID=SA;PWD=password;APP=XXX;WSID=NEUROMANCER;DATABASE=master;SERVER=localhost
I also made sure to use the 32bit ODBC Admin tool to generate the system DSNs for the 32bit application.
If I replace "CDatabase::NoOdbcDialog" with "0" it opens the ODBC-Dialog again as if informations necessary to open the connection are missing.
db.OpenEx(NULL, CDatabase::forceOdbcDialog);
CString cstmp;
if (db.IsOpen()) {
cstmp = db.GetConnect();
db.Close();
}
MessageBox(cstmp, _T(""), MB_OK);
if (db.OpenEx(cstmp, CDatabase::noOdbcDialog )) { //Exception here CDBException at 0x004FDD9
MessageBox(_T("Connection success!"), _T("Connection"), MB_OK);
db.Close();
}
else {
MessageBox(_T("Connection failed!"), _T("Connection"), MB_OK);
}
Can anyone tell me where my problem is?

Cannot make connection to SQL Server via ODBC

In the past, I have used ADO to access SQL Server, the connection string for the ADO connection object is:
Provider=sqloledb;Data Source=MYPC;Integrated Security=SSPI;
where MYPC is the name of my computer, and SQL Server is installed on my computer as the default instance.
The above connection string works well.
However, now it is said that ADO is outdated and ODBC is recommended again by Microsoft (see https://blogs.msdn.microsoft.com/sqlnativeclient/2011/08/29/microsoft-is-aligning-with-odbc-for-native-relational-data-access/ ), so I have to modify my code to use ODBC instead.
So I changed the connection to SQL Server to the following code:
CDatabase Database;
// Provider=sqloledb;Data Source=MYPC;Integrated Security=SSPI;
Database.OpenEx(_T("Driver = {SQL Native Client}; Server = MYPC; Trusted_Connection = yes;"), CDatabase::noOdbcDialog);
Database.ExecuteSQL(_T("create database [MyDB2019] on primary (name=[MyDB2019_File],filename='F:\\MyDB2019.mdf')"));
Database.Close();
However, this code does not work. After executing Database.OpenEx, there will be a CDBException indicating
Data source name not found and no default driver specified.
Why?
Note: I am using Visual C++ 2008 and ADO
The error/exception is pointing to the problem (albeit it seems a bit generic at first).
"Data source name not found and no default driver specified"
Data source name not found --> You did not specify a DSN, you do not
want to use a DSN, ignore this part
.
and no default driver specified --> You intend to use a driver, so this part of the error most likely applies to you.
The connection string appears syntactically correct: "Driver={Driver Name}...", so the next
step is to check whether the driver you try to use, named SQL Native Client, exists
on your machine.
"SQL Native Client" was/is the driver name for SQL Server 2005.
From SQL2008 the driver name got a version descriptor.
Driver={SQL Native Client} ,sql2005
Driver={SQL Server Native Client 10.0} ,sql2008
Driver={SQL Server Native Client 11.0} ,sql2012 and later
Driver={ODBC Driver 11 for SQL Server} ,sql2012 and later, odbc appears in the name
Driver={ODBC Driver 13 for SQL Server}
Driver={ODBC Driver 17 for SQL Server}
An easy way to find the installed 'SQL Native' and 'ODBC Driver for SQL Server' drivers on your machine, would be to run the ODBC Data Source Administrator. Type Odbc data sources in your search box or odbcad32.exe in the command/address bar (for 64bit: %windir%\system32\odbcad32.exe)
When in ODBC Data Source Administrator, switch to the Drivers tab and there you will find all the available/installed drivers, at your disposal.
Here is a powershell script that opens a connection to localhost using odbc. Adjust accordingly to your installed driver(s)
cls
$conn = new-object system.data.odbc.odbcconnection
$conn.connectionstring =
# all these installed on my pc and working
#"Driver={SQL Server Native Client 11.0};Server=localhost; Database=master;Trusted_Connection=yes;"
#"Driver={SQL Server};Server=localhost; Database=master;Trusted_Connection=yes;"
#"Driver={ODBC Driver 11 for SQL Server};Server=localhost; Database=master;Trusted_Connection=yes;"
"Driver={ODBC Driver 17 for SQL Server};Server=localhost; Database=master;Trusted_Connection=yes;"
$conn.Open()
$conn.State
$conn.Close();
..and an mfc button
void CMFCDBTestDlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
CDatabase database;
CString connectionstring = _T("Driver={SQL Server Native Client 10.0};Server=localhost;Trusted_Connection=yes;");
CString messagetext;
TRY{
//database.Open(NULL, FALSE, FALSE, connectionstring, FALSE); //ok
database.OpenEx(connectionstring, CDatabase::noOdbcDialog); //ok
if (database.IsOpen()){
messagetext = _T("open, database:") + database.GetDatabaseName();
database.Close();
}
}CATCH(CDBException, e) {
messagetext = _T("Database error: ")+e->m_strError;
}
END_CATCH;
AfxMessageBox(messagetext, 0, 0);
}
Create a file with udl extension. Example: test.udl
Double click the file. Use the GUI to connect to your database. Once done, open the UDL file using notepad and extract the connection string.
See https://www.dmxzone.com/go/312/how-to-generate-an-ado-connection-string/
The problem comes from the extra spaces in the connection string, after removing extra spaces from:
"Driver = {SQL Native Client}; Server = MYPC; Trusted_Connection = yes;"
to(and change the driver name as well):
"Driver={SQL Server Native Client 10.0};Server=MYPC;Trusted_Connection=yes;"
The connection will succeed.

SQLITE_CANTOPEN - windows c++

I am trying to create an Sqlite database in the selected folder but can`t do this.
Operating System - Windows, editor - Visual studio, progect encoding is unicode. Encoding of input string with database connection unknown. Language - C++
The problem:
I can't create database when I use following connection string: file:C:/Users/Public/Desktop/testDb.sqlite
I am always getting error 14 (SQLITE_CANTOPEN).
I use following function to create database:
CSqliteManager::CSqliteManager(const char* dbName)
{
db = nullptr;
int rc = sqlite3_open(dbName, &db);
if (rc != SQLITE_OK)
{
db = nullptr;
}
}
How should me change connection string to create database fo the sollowing pass:
C://Users/Public/Desctop/MyBD.sqlite
You can't use URI style filenames with sqlite3_open() as they're disabled by default (I'm assuming you didn't enable them globally). Instead you need to use sqlite3_open_v2() with the appropriate option (SQLITE_OPEN_URI). Details and more details.
(Or just not use a URI, of course)

C++ Qt sql lite database connection issue

The following code is used by me to connect to a database using Qt IDE. And if it successfully connects to the database Connected to db is printed on a label. But the issue is if I even given a wrong database path it returns Connected to db on the label how can i correct this issue?
QSqlDatabase mydb= QSqlDatabase::addDatabase("QSQLITE");
mydb.setDatabaseName("x");
if(!mydb.open()){
ui->label->setText("Failed to open the db");
}
else{
ui->label->setText("Connected to db");
}
Even though in the above code i put "x" which is not a valid database path I get "Connected to db' in the label when I run the program!
How can i correct this issue?
Qt uses as SQLite backend uses the sqlite library, so you will use one of the functions to open the database:
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
int sqlite3_open16(
const void *filename, /* Database filename (UTF-16) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
int sqlite3_open_v2(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb, /* OUT: SQLite db handle */
int flags, /* Flags */
const char *zVfs /* Name of VFS module to use */
);
According to the docs:
These routines open an SQLite database file as specified by the
filename argument. The filename argument is interpreted as UTF-8 for
sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
order for sqlite3_open16(). A database connection handle is usually
returned in *ppDb, even if an error occurs. The only exception is that
if SQLite is unable to allocate memory to hold the sqlite3 object, a
NULL will be written into *ppDb instead of a pointer to the sqlite3
object. If the database is opened (and/or created) successfully, then
SQLITE_OK is returned. Otherwise an error code is returned. The
sqlite3_errmsg() or sqlite3_errmsg16() routines can be used to obtain
an English language description of the error following a failure of
any of the sqlite3_open() routines.
From which we conclude that if the database does not exist this will create it, it will only generate the error in creating it if there are problems to allocate memory.

Why it connected continuously whether the path is correct or wrong

I've written the following code to connect with sqlite database file .
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("c://database.db");
if(!db.open())
ui->label->setText(ui->label->text() + "Failed to connect to database");
else
ui->label->setText(ui->label->text() + "Connected.");
When change the correct path another wrong path (ex: c://datafile.db or c:database.db), it remain prints Connected. text.
Notice: If the database file does not exists, or the correct path changed to another wrong path, it will create an empty database file into the wrong path.
Is there is any problem in my code to prints Connected all time?
If you are using SQLite then it will always attempt to create the database file if it can. This may not be what you want -- for example, if you need to use data from a pre-existing database. What you should do is verify that the file exists on disk before trying to open.
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
QFile file ("c:/database.db");
if (file.exists()) {
if(!db.open()) {
ui->label->setText(ui->label->text() + "Failed to connect to database");
}
else {
ui->label->setText(ui->label->text() + "Connected.");
}
}
else {
ui->table->setText("Database does not exist!");
}
Note that the forward slash / works for any platform, and is less confusing than having to escape with the \ character. To have better portability you should probably replace the hard coded C:/ with something from QDir::root() or QDir::drives().
I can't spot it in the documentation, but some mailing list threads suggest that QSqlDatabase::open will always return true because it will create an empty database if there is no database at the given path. Note, as others have pointed out, that C://database.db is likely to be a valid path - most C++ libraries accept '/' as a path separator on Windows. It is also fairly common for libraries to convert paths you give them to 'canonical paths', which will usually involve cleaning up things like extra path separators and missing path separators after a drive name.