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...).
This commit is contained in:
Martin Kleusberg
2015-06-21 22:09:36 +02:00
parent 3f29599ce8
commit 0e18e36aa9
4 changed files with 127 additions and 15 deletions

View File

@@ -115,7 +115,7 @@ void EditTableDialog::populateFields()
tbitem->setText(kDefault, f->defaultValue());
tbitem->setText(kCheck, f->check());
tbitem->setText(kForeignKey, f->foreignKey());
tbitem->setText(kForeignKey, f->foreignKey().toString());
ui->treeWidget->addTopLevelItem(tbitem);
}
@@ -374,7 +374,9 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column)
callRenameColumn = true;
break;
case kForeignKey:
field->setForeignKey(item->text(column));
sqlb::ForeignKeyClause fk;
fk.setFromString(item->text(column));
field->setForeignKey(fk);
if(!m_bNewTable)
callRenameColumn = true;
break;

View File

@@ -9,6 +9,41 @@ namespace sqlb {
QStringList Field::Datatypes = QStringList() << "INTEGER" << "TEXT" << "BLOB" << "REAL" << "NUMERIC";
bool ForeignKeyClause::isSet() const
{
return m_override.size() || m_table.size();
}
QString ForeignKeyClause::toString() const
{
if(!isSet())
return QString();
if(m_override.size())
return m_override;
QString result = "`" + m_table + "`";
if(m_columns.size())
{
result += "(";
foreach(const QString& column, m_columns)
result += "`" + column + "`,";
result.chop(1); // Remove last comma
result += ")";
}
if(m_constraint.size())
result += " " + m_constraint;
return result;
}
void ForeignKeyClause::setFromString(const QString& fk)
{
m_override = fk;
}
QString Field::toString(const QString& indent, const QString& sep) const
{
QString str = indent + '`' + m_name + '`' + sep + m_type;
@@ -194,8 +229,8 @@ QString Table::sql() const
// foreign keys
foreach(FieldPtr f, m_fields)
{
if(!f->foreignKey().isEmpty())
sql += QString(",\n\tFOREIGN KEY(`%1`) REFERENCES %2").arg(f->name()).arg(f->foreignKey());
if(f->foreignKey().isSet())
sql += QString(",\n\tFOREIGN KEY(`%1`) REFERENCES %2").arg(f->name()).arg(f->foreignKey().toString());
}
sql += "\n)";
@@ -372,6 +407,8 @@ Table CreateTableWalker::table()
break;
case sqlite3TokenTypes::FOREIGN:
{
sqlb::ForeignKeyClause fk;
tc = tc->getNextSibling(); // FOREIGN
tc = tc->getNextSibling(); // KEY
tc = tc->getNextSibling(); // LPAREN
@@ -386,7 +423,28 @@ Table CreateTableWalker::table()
tc = tc->getNextSibling(); // RPAREN
tc = tc->getNextSibling(); // REFERENCES
tab.fields().at(tab.findField(column_name))->setForeignKey(concatTextAST(tc, true));
fk.setTable(identifier(tc));
tc = tc->getNextSibling(); // identifier
if(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::LPAREN)
{
tc = tc->getNextSibling(); // LPAREN
QStringList fk_cols;
while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN)
{
if(tc->getType() != sqlite3TokenTypes::COMMA)
fk_cols.push_back(identifier(tc));
tc = tc->getNextSibling();
}
fk.setColumns(fk_cols);
tc = tc->getNextSibling(); // RPAREN
}
fk.setConstraint(concatTextAST(tc, true));
tab.fields().at(tab.findField(column_name))->setForeignKey(fk);
}
break;
default:
@@ -423,7 +481,7 @@ void CreateTableWalker::parsecolumn(FieldPtr& f, antlr::RefAST c)
bool unique = false;
QString defaultvalue;
QString check;
QString foreignKey;
sqlb::ForeignKeyClause foreignKey;
colname = columnname(c);
c = c->getNextSibling(); //type?
@@ -491,7 +549,27 @@ void CreateTableWalker::parsecolumn(FieldPtr& f, antlr::RefAST c)
case sqlite3TokenTypes::REFERENCES:
{
con = con->getNextSibling(); // REFERENCES
foreignKey = concatTextAST(con, true);
foreignKey.setTable(identifier(con));
con = con->getNextSibling(); // identifier
if(con != antlr::nullAST && con->getType() == sqlite3TokenTypes::LPAREN)
{
con = con->getNextSibling(); // LPAREN
QStringList fk_cols;
while(con != antlr::nullAST && con->getType() != sqlite3TokenTypes::RPAREN)
{
if(con->getType() != sqlite3TokenTypes::COMMA)
fk_cols.push_back(identifier(con));
con = con->getNextSibling();
}
foreignKey.setColumns(fk_cols);
con = con->getNextSibling(); // RPAREN
}
foreignKey.setConstraint(concatTextAST(con, true));
}
break;
default:

View File

@@ -12,6 +12,38 @@
namespace sqlb {
class ForeignKeyClause
{
public:
ForeignKeyClause(const QString& table = QString(), const QStringList& columns = QStringList(), const QString& constraint = QString())
: m_table(table),
m_columns(columns),
m_constraint(constraint),
m_override(QString())
{
}
bool isSet() const;
QString toString() const;
void setFromString(const QString& fk);
void setTable(const QString& table) { m_override = QString(); m_table = table; }
const QString& table() const { return m_table; }
void setColumns(const QStringList& columns) { m_columns = columns; }
const QStringList& columns() const { return m_columns; }
void setConstraint(const QString& constraint) { m_constraint = constraint; }
const QString& constraint() const { return m_constraint; }
private:
QString m_table;
QStringList m_columns;
QString m_constraint;
QString m_override;
};
class Field
{
public:
@@ -42,7 +74,7 @@ public:
void setAutoIncrement(bool autoinc) { m_autoincrement = autoinc; }
void setPrimaryKey(bool pk) { m_primaryKey = pk; }
void setUnique(bool u) { m_unique = u; }
void setForeignKey(const QString& key) { m_foreignKey = key; }
void setForeignKey(const ForeignKeyClause& key) { m_foreignKey = key; }
bool isText() const;
bool isInteger() const;
@@ -55,7 +87,7 @@ public:
bool autoIncrement() const { return m_autoincrement; }
bool primaryKey() const { return m_primaryKey; }
bool unique() const { return m_unique; }
const QString& foreignKey() const { return m_foreignKey; }
const ForeignKeyClause& foreignKey() const { return m_foreignKey; }
static QStringList Datatypes;
private:
@@ -64,7 +96,7 @@ private:
bool m_notnull;
QString m_check;
QString m_defaultvalue;
QString m_foreignKey; // Even though this information is a table constraint easier for accessing and processing to store it here
ForeignKeyClause m_foreignKey; // Even though this information is a table constraint it's easier for accessing and processing to store it here
bool m_autoincrement; //! this is stored here for simplification
bool m_primaryKey;
bool m_unique;

View File

@@ -82,12 +82,12 @@ void TestTable::foreignKeys()
{
Table tt("testtable");
FieldPtr f = FieldPtr(new Field("a", "integer"));
f->setForeignKey("b(c)");
f->setForeignKey(sqlb::ForeignKeyClause("b", QStringList("c")));
tt.addField(f);
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
"\t`a`\tinteger,\n"
"\tFOREIGN KEY(`a`) REFERENCES b(c)\n"
"\tFOREIGN KEY(`a`) REFERENCES `b`(`c`)\n"
");"));
}
@@ -241,17 +241,17 @@ void TestTable::parseSQLEscapedQuotes()
void TestTable::parseSQLForeignKeys()
{
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);";
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.fields().at(0)->foreignKey(), QString("x"));
QCOMPARE(tab.fields().at(0)->foreignKey().table(), QString("x"));
QCOMPARE(tab.fields().at(1)->name(), QString("b"));
QCOMPARE(tab.fields().at(1)->type(), QString("int"));
QCOMPARE(tab.fields().at(1)->foreignKey(), QString("w ( z ) on delete set null"));
QCOMPARE(tab.fields().at(1)->foreignKey().toString(), QString("`w`(`y`,`z`) on delete set null"));
}
void TestTable::parseSQLCheckConstraint()