Close #21 Support for ODBC (MSSQL)

Add ODBC QSQL Database driver

Column "public" in public_keys table is a keyword in
MSSQL. Double quote it.
Double quotes are invalid on MySQL in non-ANSI mode.
Force ANSI mode for MySQL/MariaDB databases.

MSSQL also has a maximum VARCHAR of 8000

Schema updates are not performed for each new database.
New databases are immediately created conforming to schema:7
Schema 5 and 6 get updated to 7.
Schemas < 5 never have been released officially. (pre 2.0.0)

Switch "Database name" label to "DSN" for ODBC databases.
This commit is contained in:
Christian Hohnstaedt 2020-01-09 20:36:00 +01:00
parent fb8370f9a4
commit 953d26c419
7 changed files with 49 additions and 41 deletions

View File

@ -162,7 +162,7 @@ xca-portable-$(VERSION): xca$(SUFFIX).signed do.doc do.lang do.misc
$(patsubst %,"$(QTDIR)/translations/qt_%.qm", de es pl pt ru fr sk it ja) \
"$(TOPDIR)"/COPYRIGHT "${TOPDIR}/../sql/"*.dll $@
cp "$(QTDIR)/plugins/platforms/qwindows.dll" $@/platforms
cp $(patsubst %,"$(QTDIR)/plugins/sqldrivers/%.dll", qsqlite qsqlmysql qsqlpsql) $@/sqldrivers
cp $(patsubst %,"$(QTDIR)/plugins/sqldrivers/%.dll", qsqlite qsqlmysql qsqlpsql qsqlodbc) $@/sqldrivers
xca-portable.zip: xca-portable-$(VERSION).zip

View File

@ -487,7 +487,7 @@ QSqlError pki_key::insertSqlData()
}
q.finish();
SQL_PREPARE(q, "INSERT INTO public_keys (item, type, hash, len, public) "
SQL_PREPARE(q, "INSERT INTO public_keys (item, type, hash, len, \"public\") "
"VALUES (?, ?, ?, ?, ?)");
q.bindValue(0, sqlItemId);
q.bindValue(1, getKeyTypeString());

View File

@ -137,7 +137,7 @@
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<widget class="QLabel" name="dbName_label">
<property name="text">
<string>Database name</string>
</property>

View File

@ -25,14 +25,17 @@
QSqlError MainWindow::initSqlDB()
{
QStringList schemas[7];
#define MAX_SCHEMAS 7
#define SCHEMA_VERSION "7"
QStringList schemas[MAX_SCHEMAS];
#include "database_schema.cpp"
XSqlQuery q;
QSqlDatabase db = QSqlDatabase::database();
QStringList tables;
unsigned int i;
if (!db.isOpen())
return QSqlError();
@ -40,7 +43,10 @@ QSqlError MainWindow::initSqlDB()
if (!TransBegin())
return db.lastError();
for (i = XSqlQuery::schemaVersion(); i < ARRAY_SIZE(schemas); i++) {
for (;;) {
unsigned int i = XSqlQuery::schemaVersion();
if (i >= ARRAY_SIZE(schemas))
break;
foreach(QString sql, schemas[i]) {
qDebug("EXEC[%d]: '%s'", i, CCHAR(sql));
if (!q.exec(sql) || q.lastError().isValid()) {
@ -49,6 +55,7 @@ QSqlError MainWindow::initSqlDB()
}
}
}
TransCommit();
return QSqlError();
}

View File

@ -27,7 +27,7 @@ DbMap OpenDb::getDatabases()
databases["QPSQL7"] = "PostgreSQL";
databases["QMYSQL3"] = "MySQL / MariaDB";
//databases["QODBC3"] = "Open Database Connectivity (ODBC)";
databases["QODBC3"] = "Open Database Connectivity (ODBC)";
foreach (QString driver, databases.keys()) {
if (!list.contains(driver))
@ -50,6 +50,14 @@ bool OpenDb::hasRemoteDrivers()
return getDatabases().size() > 0;
}
void OpenDb::driver_selected()
{
if (getDbType() == "QODBC3")
dbName_label->setText("DSN");
else
dbName_label->setText(tr("Database name"));
}
DbMap OpenDb::splitRemoteDbName(QString db)
{
static const char * const names[NUM_PARAM] =
@ -125,13 +133,13 @@ OpenDb::OpenDb(QWidget *parent, QString db)
sqlite = false;
show_connection_settings = true;
}
driver_selected();
connect(dbType, SIGNAL(currentIndexChanged(int)),
this, SLOT(driver_selected()));
}
QString OpenDb::getDbType() const
{
qDebug() << "OpenDb::getDbType: "
<< dbType->itemData(dbType->currentIndex()).toString();
return sqlite ? hasSqLite() ? QString("QSQLITE") : QString("") :
dbType->itemData(dbType->currentIndex()).toString();
}
@ -219,6 +227,10 @@ bool OpenDb::_openDatabase(QString connName, QString pass) const
if (!hasTrans) {
XCA_WARN(tr("The database driver does not support transactions. This may happen if the client and server have different versions. Continue with care."));
}
/* This is MySQL specific. Execute it always, because
* dbType() could return "ODBC" but connect to MariaDB
*/
XSqlQuery q("SET SESSION SQL_MODE='ANSI'");
return true;
}
XSqlQuery::clearTablePrefix();

View File

@ -39,6 +39,7 @@ class OpenDb: public QDialog, public Ui::OpenDb
public slots:
int exec();
void driver_selected();
};
#endif

View File

@ -4,10 +4,10 @@
* Collisions are of course possible.
*
* All binaries are stored Base64 encoded in a column of type
* " B64_BLOB " It is defined here as "VARCHAR(10000)"
* " B64_BLOB " It is defined here as "VARCHAR(8000)"
*/
#define B64_BLOB "VARCHAR(10000)"
#define B64_BLOB "VARCHAR(8000)"
/*
* The B64(DER(something)) function means DER encode something
@ -30,8 +30,8 @@
<< "CREATE TABLE settings ("
"key_ CHAR(20) UNIQUE, " /* mySql does not like "key" or "option" */
"value VARCHAR(1024))"
<< "INSERT INTO settings (key_, value) VALUES ('schema', '1')"
"value " B64_BLOB ")"
<< "INSERT INTO settings (key_, value) VALUES ('schema', '" SCHEMA_VERSION "')"
/*
* All items (keys, tokens, requests, certs, crls, templates)
@ -45,7 +45,9 @@
"type INTEGER, " /* enum pki_type */
"source INTEGER, " /* enum pki_source */
"date " DB_DATE ", " /* Time of insertion (creation/import) */
"comment VARCHAR(2048))"
"comment VARCHAR(2048), "
"stamp INTEGER NOT NULL DEFAULT 0, " /* indicate concurrent access */
"del SMALLINT NOT NULL DEFAULT 0)"
/*
* Storage of public keys. Private keys and tokens also store
@ -56,7 +58,7 @@
"type CHAR(4), " /* RSA DSA EC (as text) */
"hash INTEGER, " /* 32 bit hash */
"len INTEGER, " /* key size in bits */
"public " B64_BLOB ", " /* B64(DER(public key)) */
"\"public\" " B64_BLOB ", " /* B64(DER(public key)) */
"FOREIGN KEY (item) REFERENCES items (id))"
/*
@ -185,15 +187,11 @@
"template " B64_BLOB ", " /* The base64 encoded template */
"FOREIGN KEY (item) REFERENCES items (id))"
;
/* Schema Version 2: Views added to quickly load the data */
schemas[1]
/* Views */
<< "CREATE VIEW view_public_keys AS SELECT "
"items.id, items.name, items.type AS item_type, items.date, "
"items.source, items.comment, "
"public_keys.type as key_type, public_keys.len, public_keys.public, "
"public_keys.type as key_type, public_keys.len, public_keys.\"public\", "
"private_keys.ownPass, "
"tokens.card_manufacturer, tokens.card_serial, tokens.card_model, "
"tokens.card_label, tokens.slot_label, tokens.object_id "
@ -236,12 +234,10 @@
"templates.version, templates.template "
"FROM templates LEFT JOIN items ON templates.item = items.id"
<< "CREATE VIEW view_private AS SELECT "
"name, private FROM private_keys JOIN items ON "
"items.id = private_keys.item"
<< "UPDATE settings SET value='2' WHERE key_='schema'"
;
/* Schema Version 3: Add indexes over hashes and primary, foreign keys */
schemas[2]
<< "CREATE INDEX i_settings_key_ ON settings (key_)"
<< "CREATE INDEX i_items_id ON items (id)"
@ -269,9 +265,14 @@
<< "CREATE INDEX i_crls_issuer ON crls (issuer)"
<< "CREATE INDEX i_revocations_caId_serial ON revocations (caId, serial)"
<< "CREATE INDEX i_templates_item ON templates (item)"
<< "UPDATE settings SET value='3' WHERE key_='schema'"
<< "CREATE INDEX i_items_stamp ON items (stamp)"
;
/* Schema Version 2: Views added to quickly load the data */
/* Schema Version 3: Add indexes over hashes and primary, foreign keys */
/* Schema Version 4: Add private key view to extract a private key with:
mysql: mysql -sNp -u xca xca_msq -e
or sqlite: sqlite3 ~/sqlxdb.xdb
@ -281,21 +282,8 @@
* First mysql/psql will ask for a password and then OpenSSL will ask for
* the database password.
*/
schemas[3]
<< "CREATE VIEW view_private AS SELECT "
"name, private FROM private_keys JOIN items ON "
"items.id = private_keys.item"
<< "UPDATE settings SET value='4' WHERE key_='schema'"
;
schemas[4]
<< "ALTER TABLE items ADD stamp INTEGER NOT NULL DEFAULT 0"
<< "CREATE INDEX i_items_stamp ON items (stamp)"
<< "UPDATE settings SET value='5' WHERE key_='schema'"
;
/* Extend settings value size from 1024 to B64_BLOB
/* Schema Version 5: Extend settings value size from 1024 to B64_BLOB
* SQLite does not support "ALTER TABLE settings MODIFY ..."
*/
schemas[5]