Skip to content

Commit 0e18e36

Browse files
committed
Parse foreign key clauses instead of just treating them as a big string
This changes the SQL grammar parser so that it parses foreign key clauses instead of just reading to the end of the clause when encoutering one. This allows using the information inside the clause later in a more effective way. However, as of now this isn't used yet. This commit only attempts to imitate the old behaviour using the new approach (and might fail doing so, causing new errors...).
1 parent 3f29599 commit 0e18e36

File tree

4 files changed

+127
-15
lines changed

4 files changed

+127
-15
lines changed

src/EditTableDialog.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ void EditTableDialog::populateFields()
115115
tbitem->setText(kDefault, f->defaultValue());
116116

117117
tbitem->setText(kCheck, f->check());
118-
tbitem->setText(kForeignKey, f->foreignKey());
118+
tbitem->setText(kForeignKey, f->foreignKey().toString());
119119
ui->treeWidget->addTopLevelItem(tbitem);
120120
}
121121

@@ -374,7 +374,9 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column)
374374
callRenameColumn = true;
375375
break;
376376
case kForeignKey:
377-
field->setForeignKey(item->text(column));
377+
sqlb::ForeignKeyClause fk;
378+
fk.setFromString(item->text(column));
379+
field->setForeignKey(fk);
378380
if(!m_bNewTable)
379381
callRenameColumn = true;
380382
break;

src/sqlitetypes.cpp

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,41 @@ namespace sqlb {
99

1010
QStringList Field::Datatypes = QStringList() << "INTEGER" << "TEXT" << "BLOB" << "REAL" << "NUMERIC";
1111

12+
bool ForeignKeyClause::isSet() const
13+
{
14+
return m_override.size() || m_table.size();
15+
}
16+
17+
QString ForeignKeyClause::toString() const
18+
{
19+
if(!isSet())
20+
return QString();
21+
22+
if(m_override.size())
23+
return m_override;
24+
25+
QString result = "`" + m_table + "`";
26+
27+
if(m_columns.size())
28+
{
29+
result += "(";
30+
foreach(const QString& column, m_columns)
31+
result += "`" + column + "`,";
32+
result.chop(1); // Remove last comma
33+
result += ")";
34+
}
35+
36+
if(m_constraint.size())
37+
result += " " + m_constraint;
38+
39+
return result;
40+
}
41+
42+
void ForeignKeyClause::setFromString(const QString& fk)
43+
{
44+
m_override = fk;
45+
}
46+
1247
QString Field::toString(const QString& indent, const QString& sep) const
1348
{
1449
QString str = indent + '`' + m_name + '`' + sep + m_type;
@@ -194,8 +229,8 @@ QString Table::sql() const
194229
// foreign keys
195230
foreach(FieldPtr f, m_fields)
196231
{
197-
if(!f->foreignKey().isEmpty())
198-
sql += QString(",\n\tFOREIGN KEY(`%1`) REFERENCES %2").arg(f->name()).arg(f->foreignKey());
232+
if(f->foreignKey().isSet())
233+
sql += QString(",\n\tFOREIGN KEY(`%1`) REFERENCES %2").arg(f->name()).arg(f->foreignKey().toString());
199234
}
200235

201236
sql += "\n)";
@@ -372,6 +407,8 @@ Table CreateTableWalker::table()
372407
break;
373408
case sqlite3TokenTypes::FOREIGN:
374409
{
410+
sqlb::ForeignKeyClause fk;
411+
375412
tc = tc->getNextSibling(); // FOREIGN
376413
tc = tc->getNextSibling(); // KEY
377414
tc = tc->getNextSibling(); // LPAREN
@@ -386,7 +423,28 @@ Table CreateTableWalker::table()
386423
tc = tc->getNextSibling(); // RPAREN
387424
tc = tc->getNextSibling(); // REFERENCES
388425

389-
tab.fields().at(tab.findField(column_name))->setForeignKey(concatTextAST(tc, true));
426+
fk.setTable(identifier(tc));
427+
tc = tc->getNextSibling(); // identifier
428+
429+
if(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::LPAREN)
430+
{
431+
tc = tc->getNextSibling(); // LPAREN
432+
433+
QStringList fk_cols;
434+
while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN)
435+
{
436+
if(tc->getType() != sqlite3TokenTypes::COMMA)
437+
fk_cols.push_back(identifier(tc));
438+
tc = tc->getNextSibling();
439+
}
440+
fk.setColumns(fk_cols);
441+
442+
tc = tc->getNextSibling(); // RPAREN
443+
}
444+
445+
fk.setConstraint(concatTextAST(tc, true));
446+
447+
tab.fields().at(tab.findField(column_name))->setForeignKey(fk);
390448
}
391449
break;
392450
default:
@@ -423,7 +481,7 @@ void CreateTableWalker::parsecolumn(FieldPtr& f, antlr::RefAST c)
423481
bool unique = false;
424482
QString defaultvalue;
425483
QString check;
426-
QString foreignKey;
484+
sqlb::ForeignKeyClause foreignKey;
427485

428486
colname = columnname(c);
429487
c = c->getNextSibling(); //type?
@@ -491,7 +549,27 @@ void CreateTableWalker::parsecolumn(FieldPtr& f, antlr::RefAST c)
491549
case sqlite3TokenTypes::REFERENCES:
492550
{
493551
con = con->getNextSibling(); // REFERENCES
494-
foreignKey = concatTextAST(con, true);
552+
553+
foreignKey.setTable(identifier(con));
554+
con = con->getNextSibling(); // identifier
555+
556+
if(con != antlr::nullAST && con->getType() == sqlite3TokenTypes::LPAREN)
557+
{
558+
con = con->getNextSibling(); // LPAREN
559+
560+
QStringList fk_cols;
561+
while(con != antlr::nullAST && con->getType() != sqlite3TokenTypes::RPAREN)
562+
{
563+
if(con->getType() != sqlite3TokenTypes::COMMA)
564+
fk_cols.push_back(identifier(con));
565+
con = con->getNextSibling();
566+
}
567+
foreignKey.setColumns(fk_cols);
568+
569+
con = con->getNextSibling(); // RPAREN
570+
}
571+
572+
foreignKey.setConstraint(concatTextAST(con, true));
495573
}
496574
break;
497575
default:

src/sqlitetypes.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,38 @@
1212

1313
namespace sqlb {
1414

15+
class ForeignKeyClause
16+
{
17+
public:
18+
ForeignKeyClause(const QString& table = QString(), const QStringList& columns = QStringList(), const QString& constraint = QString())
19+
: m_table(table),
20+
m_columns(columns),
21+
m_constraint(constraint),
22+
m_override(QString())
23+
{
24+
}
25+
26+
bool isSet() const;
27+
QString toString() const;
28+
void setFromString(const QString& fk);
29+
30+
void setTable(const QString& table) { m_override = QString(); m_table = table; }
31+
const QString& table() const { return m_table; }
32+
33+
void setColumns(const QStringList& columns) { m_columns = columns; }
34+
const QStringList& columns() const { return m_columns; }
35+
36+
void setConstraint(const QString& constraint) { m_constraint = constraint; }
37+
const QString& constraint() const { return m_constraint; }
38+
39+
private:
40+
QString m_table;
41+
QStringList m_columns;
42+
QString m_constraint;
43+
44+
QString m_override;
45+
};
46+
1547
class Field
1648
{
1749
public:
@@ -42,7 +74,7 @@ class Field
4274
void setAutoIncrement(bool autoinc) { m_autoincrement = autoinc; }
4375
void setPrimaryKey(bool pk) { m_primaryKey = pk; }
4476
void setUnique(bool u) { m_unique = u; }
45-
void setForeignKey(const QString& key) { m_foreignKey = key; }
77+
void setForeignKey(const ForeignKeyClause& key) { m_foreignKey = key; }
4678

4779
bool isText() const;
4880
bool isInteger() const;
@@ -55,7 +87,7 @@ class Field
5587
bool autoIncrement() const { return m_autoincrement; }
5688
bool primaryKey() const { return m_primaryKey; }
5789
bool unique() const { return m_unique; }
58-
const QString& foreignKey() const { return m_foreignKey; }
90+
const ForeignKeyClause& foreignKey() const { return m_foreignKey; }
5991

6092
static QStringList Datatypes;
6193
private:
@@ -64,7 +96,7 @@ class Field
6496
bool m_notnull;
6597
QString m_check;
6698
QString m_defaultvalue;
67-
QString m_foreignKey; // Even though this information is a table constraint easier for accessing and processing to store it here
99+
ForeignKeyClause m_foreignKey; // Even though this information is a table constraint it's easier for accessing and processing to store it here
68100
bool m_autoincrement; //! this is stored here for simplification
69101
bool m_primaryKey;
70102
bool m_unique;

src/tests/testsqlobjects.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ void TestTable::foreignKeys()
8282
{
8383
Table tt("testtable");
8484
FieldPtr f = FieldPtr(new Field("a", "integer"));
85-
f->setForeignKey("b(c)");
85+
f->setForeignKey(sqlb::ForeignKeyClause("b", QStringList("c")));
8686
tt.addField(f);
8787

8888
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
8989
"\t`a`\tinteger,\n"
90-
"\tFOREIGN KEY(`a`) REFERENCES b(c)\n"
90+
"\tFOREIGN KEY(`a`) REFERENCES `b`(`c`)\n"
9191
");"));
9292
}
9393

@@ -241,17 +241,17 @@ void TestTable::parseSQLEscapedQuotes()
241241

242242
void TestTable::parseSQLForeignKeys()
243243
{
244-
QString sql = "CREATE TABLE foreign_key_test(a int, b int, foreign key (a) references x, foreign key (b) references w(z) on delete set null);";
244+
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);";
245245

246246
Table tab = Table::parseSQL(sql).first;
247247

248248
QCOMPARE(tab.name(), QString("foreign_key_test"));
249249
QCOMPARE(tab.fields().at(0)->name(), QString("a"));
250250
QCOMPARE(tab.fields().at(0)->type(), QString("int"));
251-
QCOMPARE(tab.fields().at(0)->foreignKey(), QString("x"));
251+
QCOMPARE(tab.fields().at(0)->foreignKey().table(), QString("x"));
252252
QCOMPARE(tab.fields().at(1)->name(), QString("b"));
253253
QCOMPARE(tab.fields().at(1)->type(), QString("int"));
254-
QCOMPARE(tab.fields().at(1)->foreignKey(), QString("w ( z ) on delete set null"));
254+
QCOMPARE(tab.fields().at(1)->foreignKey().toString(), QString("`w`(`y`,`z`) on delete set null"));
255255
}
256256

257257
void TestTable::parseSQLCheckConstraint()

0 commit comments

Comments
 (0)