mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 02:50:46 -06:00
Foreign keys used to be stored along with the column information even though it's more or less a table constraint. However, as we only support single column foreign keys it was easier to store it inside that single column. Now with multi-column foreign keys coming, a mechanism has been introduced to store those multi-column foreign keys in the table data. This lead to two different storing places for foreign key information: inside the field for one-column foreign keys and inside the table for multi-column foreign keys. This commit deletes the foreign key storage inside fields and changes all code to use the table storage.
328 lines
11 KiB
C++
328 lines
11 KiB
C++
#include "testsqlobjects.h"
|
|
#include "../sqlitetypes.h"
|
|
|
|
#include <QtTest/QtTest>
|
|
|
|
QTEST_APPLESS_MAIN(TestTable)
|
|
|
|
using namespace sqlb;
|
|
|
|
void TestTable::sqlOutput()
|
|
{
|
|
Table tt("testtable");
|
|
FieldPtr f = FieldPtr(new Field("id", "integer"));
|
|
f->setPrimaryKey(true);
|
|
FieldPtr fkm = FieldPtr(new Field("km", "integer", false, "", "km > 1000"));
|
|
fkm->setPrimaryKey(true);
|
|
tt.addField(f);
|
|
tt.addField(FieldPtr(new Field("car", "text")));
|
|
tt.addField(fkm);
|
|
|
|
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
|
|
"\t`id`\tinteger,\n"
|
|
"\t`car`\ttext,\n"
|
|
"\t`km`\tinteger CHECK(km > 1000),\n"
|
|
"\tPRIMARY KEY(`id`,`km`)\n"
|
|
");"));
|
|
}
|
|
|
|
void TestTable::autoincrement()
|
|
{
|
|
Table tt("testtable");
|
|
FieldPtr f = FieldPtr(new Field("id", "integer"));
|
|
f->setPrimaryKey(true);
|
|
f->setAutoIncrement(true);
|
|
FieldPtr fkm = FieldPtr(new Field("km", "integer"));
|
|
tt.addField(f);
|
|
tt.addField(FieldPtr(new Field("car", "text")));
|
|
tt.addField(fkm);
|
|
|
|
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
|
|
"\t`id`\tinteger PRIMARY KEY AUTOINCREMENT,\n"
|
|
"\t`car`\ttext,\n"
|
|
"\t`km`\tinteger\n"
|
|
");"));
|
|
}
|
|
|
|
void TestTable::notnull()
|
|
{
|
|
Table tt("testtable");
|
|
FieldPtr f = FieldPtr(new Field("id", "integer"));
|
|
f->setPrimaryKey(true);
|
|
f->setAutoIncrement(true);
|
|
FieldPtr fkm = FieldPtr(new Field("km", "integer"));
|
|
tt.addField(f);
|
|
tt.addField(FieldPtr(new Field("car", "text", true)));
|
|
tt.addField(fkm);
|
|
|
|
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
|
|
"\t`id`\tinteger PRIMARY KEY AUTOINCREMENT,\n"
|
|
"\t`car`\ttext NOT NULL,\n"
|
|
"\t`km`\tinteger\n"
|
|
");"));
|
|
}
|
|
|
|
void TestTable::withoutRowid()
|
|
{
|
|
Table tt("testtable");
|
|
FieldPtr f = FieldPtr(new Field("a", "integer"));
|
|
f->setPrimaryKey(true);
|
|
f->setAutoIncrement(true);
|
|
tt.addField(f);
|
|
tt.addField(FieldPtr(new Field("b", "integer")));
|
|
tt.setRowidColumn("a");
|
|
|
|
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
|
|
"\t`a`\tinteger PRIMARY KEY AUTOINCREMENT,\n"
|
|
"\t`b`\tinteger\n"
|
|
") WITHOUT ROWID;"));
|
|
}
|
|
|
|
void TestTable::foreignKeys()
|
|
{
|
|
Table tt("testtable");
|
|
FieldPtr f = FieldPtr(new Field("a", "integer"));
|
|
tt.addField(f);
|
|
tt.addForeignKey(f, sqlb::ForeignKeyClause("b", QStringList("c")));
|
|
|
|
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
|
|
"\t`a`\tinteger,\n"
|
|
"\tFOREIGN KEY(`a`) REFERENCES `b`(`c`)\n"
|
|
");"));
|
|
}
|
|
|
|
void TestTable::uniqueConstraint()
|
|
{
|
|
Table tt("testtable");
|
|
FieldPtr f1 = FieldPtr(new Field("a", "integer"));
|
|
FieldPtr f2 = FieldPtr(new Field("b", "integer"));
|
|
FieldPtr f3 = FieldPtr(new Field("c", "integer"));
|
|
f1->setUnique(true);
|
|
FieldVector uc;
|
|
uc.push_back(f2);
|
|
uc.push_back(f3);
|
|
tt.addField(f1);
|
|
tt.addField(f2);
|
|
tt.addField(f3);
|
|
tt.addUniqueConstraint(uc);
|
|
|
|
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
|
|
"\t`a`\tinteger UNIQUE,\n"
|
|
"\t`b`\tinteger,\n"
|
|
"\t`c`\tinteger,\n"
|
|
"\tUNIQUE(`b`,`c`)\n"
|
|
");"));
|
|
}
|
|
|
|
void TestTable::parseSQL()
|
|
{
|
|
QString sSQL = "create TABLE hero (\n"
|
|
"\tid integer PRIMARY KEY AUTOINCREMENT,\n"
|
|
"\tname text NOT NULL DEFAULT 'xxxx',\n"
|
|
"\tinfo VARCHAR(255) CHECK (info == 'x')\n"
|
|
");";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QVERIFY(tab.name() == "hero");
|
|
QVERIFY(tab.rowidColumn() == "_rowid_");
|
|
QVERIFY(tab.fields().at(0)->name() == "id");
|
|
QVERIFY(tab.fields().at(1)->name() == "name");
|
|
QVERIFY(tab.fields().at(2)->name() == "info");
|
|
|
|
QVERIFY(tab.fields().at(0)->type() == "integer");
|
|
QVERIFY(tab.fields().at(1)->type() == "text");
|
|
QVERIFY(tab.fields().at(2)->type() == "VARCHAR(255)");
|
|
|
|
QVERIFY(tab.fields().at(0)->autoIncrement());
|
|
QVERIFY(tab.fields().at(0)->primaryKey());
|
|
QVERIFY(tab.fields().at(1)->notnull());
|
|
QCOMPARE(tab.fields().at(1)->defaultValue(), QString("'xxxx'"));
|
|
QCOMPARE(tab.fields().at(1)->check(), QString(""));
|
|
QCOMPARE(tab.fields().at(2)->check(), QString("info == 'x'"));
|
|
}
|
|
|
|
void TestTable::parseSQLdefaultexpr()
|
|
{
|
|
QString sSQL = "CREATE TABLE chtest(\n"
|
|
"id integer primary key,\n"
|
|
"dumpytext text default('axa') CHECK(dumpytext == \"aa\"),\n"
|
|
"date datetime default CURRENT_TIMESTAMP,"
|
|
"zoi integer)";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QCOMPARE(tab.name(), QString("chtest"));
|
|
QCOMPARE(tab.fields().at(0)->name(), QString("id"));
|
|
QCOMPARE(tab.fields().at(1)->name(), QString("dumpytext"));
|
|
QCOMPARE(tab.fields().at(2)->name(), QString("date"));
|
|
QCOMPARE(tab.fields().at(3)->name(), QString("zoi"));
|
|
|
|
QCOMPARE(tab.fields().at(0)->type(), QString("integer"));
|
|
QCOMPARE(tab.fields().at(1)->type(), QString("text"));
|
|
QCOMPARE(tab.fields().at(2)->type(), QString("datetime"));
|
|
QCOMPARE(tab.fields().at(3)->type(), QString("integer"));
|
|
|
|
QCOMPARE(tab.fields().at(1)->defaultValue(), QString("('axa')"));
|
|
QCOMPARE(tab.fields().at(1)->check(), QString("dumpytext == \"aa\""));
|
|
QCOMPARE(tab.fields().at(2)->defaultValue(), QString("CURRENT_TIMESTAMP"));
|
|
QCOMPARE(tab.fields().at(2)->check(), QString(""));
|
|
QCOMPARE(tab.fields().at(3)->defaultValue(), QString(""));
|
|
QCOMPARE(tab.fields().at(3)->check(), QString(""));
|
|
|
|
QVERIFY(tab.fields().at(0)->primaryKey());
|
|
}
|
|
|
|
void TestTable::parseSQLMultiPk()
|
|
{
|
|
QString sSQL = "CREATE TABLE hero (\n"
|
|
"\tid1 integer,\n"
|
|
"\tid2 integer,\n"
|
|
"\tnonpkfield blob,\n"
|
|
"PRIMARY KEY(`id1`,`id2`)\n"
|
|
");";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QVERIFY(tab.name() == "hero");
|
|
QVERIFY(tab.fields().at(0)->name() == "id1");
|
|
QVERIFY(tab.fields().at(1)->name() == "id2");
|
|
|
|
QVERIFY(tab.fields().at(0)->type() == "integer");
|
|
QVERIFY(tab.fields().at(1)->type() == "integer");
|
|
|
|
QVERIFY(tab.fields().at(0)->primaryKey());
|
|
QVERIFY(tab.fields().at(1)->primaryKey());
|
|
}
|
|
|
|
void TestTable::parseSQLForeignKey()
|
|
{
|
|
QString sSQL = "CREATE TABLE grammar_test(id, test, FOREIGN KEY(test) REFERENCES other_table);";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QVERIFY(tab.name() == "grammar_test");
|
|
QVERIFY(tab.fields().at(0)->name() == "id");
|
|
QVERIFY(tab.fields().at(1)->name() == "test");
|
|
}
|
|
|
|
void TestTable::parseSQLSingleQuotes()
|
|
{
|
|
QString sSQL = "CREATE TABLE 'test'('id','test');";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QVERIFY(tab.name() == "test");
|
|
QVERIFY(tab.fields().at(0)->name() == "id");
|
|
QVERIFY(tab.fields().at(1)->name() == "test");
|
|
}
|
|
|
|
void TestTable::parseSQLKeywordInIdentifier()
|
|
{
|
|
QString sSQL = "CREATE TABLE deffered(key integer primary key, if text);";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QVERIFY(tab.name() == "deffered");
|
|
QVERIFY(tab.fields().at(0)->name() == "key");
|
|
QVERIFY(tab.fields().at(1)->name() == "if");
|
|
}
|
|
|
|
void TestTable::parseSQLWithoutRowid()
|
|
{
|
|
QString sSQL = "CREATE TABLE test(a integer primary key, b integer) WITHOUT ROWID;";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QVERIFY(tab.fields().at(tab.findPk())->name() == "a");
|
|
QVERIFY(tab.rowidColumn() == "a");
|
|
}
|
|
|
|
void TestTable::parseNonASCIIChars()
|
|
{
|
|
QString sSQL = "CREATE TABLE `lösung` ("
|
|
"`Fieldöäüß` INTEGER,"
|
|
"PRIMARY KEY(`Fieldöäüß`)"
|
|
");";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
|
|
QVERIFY(tab.name() == "lösung");
|
|
QVERIFY(tab.fields().at(0)->name() == "Fieldöäüß");
|
|
}
|
|
|
|
void TestTable::parseSQLEscapedQuotes()
|
|
{
|
|
QString sSql = "CREATE TABLE double_quotes(a text default 'a''a');";
|
|
|
|
Table tab = Table::parseSQL(sSql).first;
|
|
|
|
QCOMPARE(tab.name(), QString("double_quotes"));
|
|
QCOMPARE(tab.fields().at(0)->name(), QString("a"));
|
|
QCOMPARE(tab.fields().at(0)->defaultValue(), QString("'a''a'"));
|
|
}
|
|
|
|
void TestTable::parseSQLForeignKeys()
|
|
{
|
|
QString sql = "CREATE TABLE foreign_key_test(a int, b int, foreign key (a) references x, foreign key (b) references w(y,z) on delete set null);";
|
|
|
|
Table tab = Table::parseSQL(sql).first;
|
|
|
|
QCOMPARE(tab.name(), QString("foreign_key_test"));
|
|
QCOMPARE(tab.fields().at(0)->name(), QString("a"));
|
|
QCOMPARE(tab.fields().at(0)->type(), QString("int"));
|
|
QCOMPARE(tab.foreignKey(tab.fields().at(0)).table(), QString("x"));
|
|
QCOMPARE(tab.fields().at(1)->name(), QString("b"));
|
|
QCOMPARE(tab.fields().at(1)->type(), QString("int"));
|
|
QCOMPARE(tab.foreignKey(tab.fields().at(1)).toString(), QString("`w`(`y`,`z`) on delete set null"));
|
|
}
|
|
|
|
void TestTable::parseSQLCheckConstraint()
|
|
{
|
|
QString sql = "CREATE TABLE a (`b` text CHECK(`b`='A' or `b`='B'));";
|
|
|
|
Table tab = Table::parseSQL(sql).first;
|
|
|
|
QCOMPARE(tab.name(), QString("a"));
|
|
QCOMPARE(tab.fields().at(0)->name(), QString("b"));
|
|
QCOMPARE(tab.fields().at(0)->type(), QString("text"));
|
|
QCOMPARE(tab.fields().at(0)->check(), QString("`b` = 'A' or `b` = 'B'"));
|
|
}
|
|
|
|
void TestTable::createTableWithIn()
|
|
{
|
|
QString sSQL = "CREATE TABLE not_working("
|
|
"_id PRIMARY KEY NOT NULL,"
|
|
"value NVARCHAR(5) CHECK (value IN ('a', 'b', 'c'))"
|
|
");";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
QVERIFY(tab.name() == "not_working");
|
|
|
|
QVERIFY(tab.fields().at(1)->check() == "value IN ( 'a' , 'b' , 'c' )");
|
|
}
|
|
|
|
void TestTable::createTableWithNotLikeConstraint()
|
|
{
|
|
QString sSQL = "CREATE TABLE hopefully_working(\n"
|
|
"value TEXT CONSTRAINT 'value' CHECK(value NOT LIKE 'prefix%'),\n"
|
|
"value2 TEXT CONSTRAINT 'value' CHECK(value2 NOT MATCH 'prefix%'),\n"
|
|
"value3 TEXT CONSTRAINT 'value' CHECK(value3 NOT REGEXP 'prefix%'),\n"
|
|
"value4 TEXT CONSTRAINT 'value' CHECK(value4 NOT GLOB 'prefix%'),\n"
|
|
"value5 INTEGER CONSTRAINT 'value' CHECK(value5 BETWEEN 1+4 AND 100 OR 200),\n"
|
|
"value6 INTEGER CONSTRAINT 'value' CHECK(value6 NOT BETWEEN 1 AND 100),\n"
|
|
"value7 INTEGER CONSTRAINT 'value' CHECK(NOT EXISTS (1))\n"
|
|
");";
|
|
|
|
Table tab = Table::parseSQL(sSQL).first;
|
|
QVERIFY(tab.name() == "hopefully_working");
|
|
|
|
QVERIFY(tab.fields().at(0)->check() == "value NOT LIKE 'prefix%'");
|
|
QVERIFY(tab.fields().at(1)->check() == "value2 NOT MATCH 'prefix%'");
|
|
QVERIFY(tab.fields().at(2)->check() == "value3 NOT REGEXP 'prefix%'");
|
|
QVERIFY(tab.fields().at(3)->check() == "value4 NOT GLOB 'prefix%'");
|
|
QVERIFY(tab.fields().at(4)->check() == "value5 BETWEEN 1 + 4 AND 100 OR 200");
|
|
QVERIFY(tab.fields().at(5)->check() == "value6 NOT BETWEEN 1 AND 100");
|
|
QVERIFY(tab.fields().at(6)->check() == "NOT EXISTS ( 1 )");
|
|
}
|