grammar: Move primary key handling over to the new system

This adds support for named primary keys.

It also makes it easier to store extra information in the future as
needed for ordered primary keys.
This commit is contained in:
Martin Kleusberg
2016-09-06 15:40:16 +02:00
parent 4b79f92eaa
commit 5c17115c03
7 changed files with 107 additions and 59 deletions

View File

@@ -186,6 +186,7 @@ void DbStructureModel::reloadData()
addNode(typeToParentItem.value("browsable"), *it);
// Add field nodes
sqlb::FieldVector pk = (*it).table.primaryKey();
for(int i=0; i < (*it).table.fields().size(); ++i)
{
QTreeWidgetItem *fldItem = new QTreeWidgetItem(item);
@@ -193,7 +194,7 @@ void DbStructureModel::reloadData()
fldItem->setText(1, "field");
fldItem->setText(2, (*it).table.fields().at(i)->type());
fldItem->setText(3, (*it).table.fields().at(i)->toString(" ", " "));
if((*it).table.fields().at(i)->primaryKey())
if(pk.contains((*it).table.fields().at(i)))
fldItem->setIcon(0, QIcon(":/icons/field_key"));
else
fldItem->setIcon(0, QIcon(":/icons/field"));

View File

@@ -85,6 +85,7 @@ void EditTableDialog::populateFields()
ui->treeWidget->clear();
sqlb::FieldVector fields = m_table.fields();
sqlb::FieldVector pk = m_table.primaryKey();
foreach(sqlb::FieldPtr f, fields)
{
QTreeWidgetItem *tbitem = new QTreeWidgetItem(ui->treeWidget);
@@ -106,7 +107,7 @@ void EditTableDialog::populateFields()
ui->treeWidget->setItemWidget(tbitem, kType, typeBox);
tbitem->setCheckState(kNotNull, f->notnull() ? Qt::Checked : Qt::Unchecked);
tbitem->setCheckState(kPrimaryKey, f->primaryKey() ? Qt::Checked : Qt::Unchecked);
tbitem->setCheckState(kPrimaryKey, pk.contains(f) ? Qt::Checked : Qt::Unchecked);
tbitem->setCheckState(kAutoIncrement, f->autoIncrement() ? Qt::Checked : Qt::Unchecked);
tbitem->setCheckState(kUnique, f->unique() ? Qt::Checked : Qt::Unchecked);
@@ -237,6 +238,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column)
// When editing an exiting table, check if any foreign keys would cause trouble in case this name is edited
if(!m_bNewTable)
{
sqlb::FieldVector pk = m_table.primaryKey();
foreach(const DBBrowserObject& fkobj, pdb->objMap.values("table"))
{
QList<sqlb::ConstraintPtr> fks = fkobj.table.constraints(sqlb::FieldVector(), sqlb::Constraint::ForeignKeyConstraintType);
@@ -245,7 +247,7 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column)
QSharedPointer<sqlb::ForeignKeyClause> fk = fkptr.dynamicCast<sqlb::ForeignKeyClause>();
if(fk->table() == m_table.name())
{
if(fk->columns().contains(field->name()) || field->primaryKey())
if(fk->columns().contains(field->name()) || pk.contains(field))
{
QMessageBox::warning(this, qApp->applicationName(), tr("This column is referenced in a foreign key in table %1 and thus "
"its name cannot be changed.")
@@ -271,7 +273,12 @@ void EditTableDialog::itemChanged(QTreeWidgetItem *item, int column)
break;
case kPrimaryKey:
{
field->setPrimaryKey(item->checkState(column) == Qt::Checked);
sqlb::FieldVector& pk = m_table.primaryKeyRef();
if(item->checkState(column) == Qt::Checked)
pk.push_back(field);
else
pk.removeAll(field);
if(item->checkState(column) == Qt::Checked)
{
// this will unset any other autoincrement

View File

@@ -780,8 +780,11 @@ QString DBBrowserDB::emptyInsertStmt(const sqlb::Table& t, const QString& pk_val
QStringList vals;
QStringList fields;
foreach(sqlb::FieldPtr f, t.fields()) {
if(f->primaryKey()) {
foreach(sqlb::FieldPtr f, t.fields())
{
sqlb::ConstraintPtr pk = t.constraint(f, sqlb::Constraint::PrimaryKeyConstraintType);
if(pk)
{
fields << f->name();
if(!pk_value.isNull())
@@ -792,9 +795,7 @@ QString DBBrowserDB::emptyInsertStmt(const sqlb::Table& t, const QString& pk_val
{
QString maxval = this->max(t, f);
vals << QString::number(maxval.toLongLong() + 1);
}
else
{
} else {
vals << "NULL";
}
}

View File

@@ -426,8 +426,9 @@ QModelIndex SqliteTableModel::dittoRecord(int old_row)
sqlb::Table t = sqlb::Table::parseSQL(m_db->getObjectByName(m_sTable).getsql()).first;
sqlb::FieldVector pk = t.primaryKey();
for (int col = 0; col < t.fields().size(); ++col) {
if (!t.fields().at(col)->primaryKey()) {
if(!pk.contains(t.fields().at(col))) {
if (!firstEditedColumn)
firstEditedColumn = col + 1;

View File

@@ -77,6 +77,16 @@ QString UniqueConstraint::toSql(const FieldVector& applyOn) const
return result;
}
QString PrimaryKeyConstraint::toSql(const FieldVector& applyOn) const
{
QString result;
if(!m_name.isNull())
result += QString("CONSTRAINT %1 ").arg(escapeIdentifier(m_name));
result += QString("PRIMARY KEY(%1)").arg(fieldVectorToFieldNames(applyOn).join(","));
return result;
}
QString Field::toString(const QString& indent, const QString& sep) const
{
QString str = indent + escapeIdentifier(m_name) + sep + m_type;
@@ -193,7 +203,7 @@ void Table::setField(int index, FieldPtr f)
}
}
int Table::findField(const QString &sname)
int Table::findField(const QString &sname) const
{
for(int i = 0; i < m_fields.count(); ++i)
{
@@ -205,12 +215,13 @@ int Table::findField(const QString &sname)
int Table::findPk() const
{
for(int i = 0; i < m_fields.count(); ++i)
{
if(m_fields.at(i)->primaryKey())
return i;
}
return -1;
// TODO This is a stupid function (and always was) which should be fixed/improved
FieldVector pk = primaryKey();
if(pk.empty())
return -1;
else
return findField(pk.at(0)->name());
}
QStringList Table::fieldList() const
@@ -279,30 +290,16 @@ QString Table::sql() const
sql += fieldList().join(",\n");
// primary key
if(!hasAutoIncrement())
{
QString pk = ",\n\tPRIMARY KEY(";
bool pks_found = false;
foreach(FieldPtr f, m_fields)
{
if(f->primaryKey())
{
pk += escapeIdentifier(f->name()) + ",";
pks_found = true;
}
}
pk.chop(1);
if(pks_found)
sql += pk + ")";
}
// Constraints
ConstraintMap::const_iterator it = m_constraints.constBegin();
bool autoincrement = hasAutoIncrement();
while(it != m_constraints.constEnd())
{
sql += QString(",\n\t");
sql += it.value()->toSql(it.key());
if(!autoincrement || it.value()->type() != Constraint::PrimaryKeyConstraintType)
{
sql += QString(",\n\t");
sql += it.value()->toSql(it.key());
}
++it;
}
@@ -374,6 +371,25 @@ QList<ConstraintPtr> Table::constraints(FieldVector fields, Constraint::Constrai
}
}
FieldVector& Table::primaryKeyRef()
{
return const_cast<FieldVector&>(static_cast<const Table*>(this)->primaryKey());
}
const FieldVector& Table::primaryKey() const
{
ConstraintMap::ConstIterator it = m_constraints.constBegin();
while(it != m_constraints.constEnd())
{
if(it.value()->type() == Constraint::PrimaryKeyConstraintType)
return it.key();
++it;
}
static FieldVector emptyFieldVector;
return emptyFieldVector;
}
namespace
{
QString identifier(antlr::RefAST ident)
@@ -485,17 +501,18 @@ Table CreateTableWalker::table()
{
case sqlite3TokenTypes::PRIMARY:
{
if(!constraint_name.isNull())
m_bModifySupported = false;
PrimaryKeyConstraint* pk = new PrimaryKeyConstraint;
pk->setName(constraint_name);
tc = tc->getNextSibling()->getNextSibling(); // skip primary and key
tc = tc->getNextSibling(); // skip LPAREN
FieldVector fields;
do
{
QString col = columnname(tc);
int fieldindex = tab.findField(col);
if(fieldindex != -1)
tab.fields().at(fieldindex)->setPrimaryKey(true);
FieldPtr field = tab.field(tab.findField(col));
fields.push_back(field);
tc = tc->getNextSibling();
if(tc != antlr::nullAST
@@ -509,7 +526,7 @@ Table CreateTableWalker::table()
if(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::AUTOINCREMENT)
{
tab.fields().at(fieldindex)->setAutoIncrement(true);
field->setAutoIncrement(true);
tc = tc->getNextSibling();
}
while(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::COMMA)
@@ -517,6 +534,8 @@ Table CreateTableWalker::table()
tc = tc->getNextSibling(); // skip ident and comma
}
} while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN);
tab.addConstraint(fields, ConstraintPtr(pk));
}
break;
case sqlite3TokenTypes::UNIQUE:
@@ -738,12 +757,20 @@ void CreateTableWalker::parsecolumn(Table& table, antlr::RefAST c)
c = c->getNextSibling();
}
FieldPtr f = FieldPtr(new Field(colname, type, notnull, defaultvalue, check, primarykey, unique));
FieldPtr f = FieldPtr(new Field(colname, type, notnull, defaultvalue, check, unique));
f->setAutoIncrement(autoincrement);
table.addField(f);
if(foreignKey)
table.addConstraint(f, ConstraintPtr(foreignKey));
if(primarykey)
{
FieldVector v;
if(table.constraint(v, Constraint::PrimaryKeyConstraintType))
table.primaryKeyRef().push_back(f);
else
table.addConstraint(f, ConstraintPtr(new PrimaryKeyConstraint()));
}
}
} //namespace sqlb

View File

@@ -95,6 +95,16 @@ public:
virtual ConstraintTypes type() const { return UniqueConstraintType; }
};
class PrimaryKeyConstraint : public Constraint
{
public:
PrimaryKeyConstraint() {}
virtual QString toSql(const FieldVector& applyOn) const;
virtual ConstraintTypes type() const { return PrimaryKeyConstraintType; }
};
class Field
{
public:
@@ -103,7 +113,6 @@ public:
bool notnull = false,
const QString& defaultvalue = "",
const QString& check = "",
bool pk = false,
bool unique = false)
: m_name(name)
, m_type(type)
@@ -111,7 +120,6 @@ public:
, m_check(check)
, m_defaultvalue(defaultvalue)
, m_autoincrement(false)
, m_primaryKey(pk)
, m_unique(unique)
{}
@@ -123,7 +131,6 @@ public:
void setCheck(const QString& check) { m_check = check; }
void setDefaultValue(const QString& defaultvalue) { m_defaultvalue = defaultvalue; }
void setAutoIncrement(bool autoinc) { m_autoincrement = autoinc; }
void setPrimaryKey(bool pk) { m_primaryKey = pk; }
void setUnique(bool u) { m_unique = u; }
bool isText() const;
@@ -135,7 +142,6 @@ public:
const QString& check() const { return m_check; }
const QString& defaultValue() const { return m_defaultvalue; }
bool autoIncrement() const { return m_autoincrement; }
bool primaryKey() const { return m_primaryKey; }
bool unique() const { return m_unique; }
static QStringList Datatypes;
@@ -146,7 +152,6 @@ private:
QString m_check;
QString m_defaultvalue;
bool m_autoincrement; //! this is stored here for simplification
bool m_primaryKey;
bool m_unique;
};
@@ -193,6 +198,8 @@ public:
ConstraintPtr constraint(FieldVector fields = FieldVector(), Constraint::ConstraintTypes type = Constraint::NoType) const; //! Only returns the first constraint, if any
QList<ConstraintPtr> constraints(FieldPtr field, Constraint::ConstraintTypes type = Constraint::NoType) const;
QList<ConstraintPtr> constraints(FieldVector fields = FieldVector(), Constraint::ConstraintTypes type = Constraint::NoType) const;
FieldVector& primaryKeyRef();
const FieldVector& primaryKey() const;
/**
* @brief findField Finds a field and returns the index.
@@ -200,7 +207,7 @@ public:
* @return The field index if the field was found.
* -1 if field couldn't be found.
*/
int findField(const QString& sname);
int findField(const QString& sname) const;
int findPk() const;

View File

@@ -11,12 +11,14 @@ 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);
FieldVector pk;
pk.push_back(f);
pk.push_back(fkm);
tt.addConstraint(pk, ConstraintPtr(new PrimaryKeyConstraint()));
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
"\t`id`\tinteger,\n"
@@ -30,12 +32,12 @@ 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);
tt.addConstraint(f, ConstraintPtr(new PrimaryKeyConstraint()));
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
"\t`id`\tinteger PRIMARY KEY AUTOINCREMENT,\n"
@@ -48,12 +50,12 @@ 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);
tt.addConstraint(f, ConstraintPtr(new PrimaryKeyConstraint()));
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
"\t`id`\tinteger PRIMARY KEY AUTOINCREMENT,\n"
@@ -66,11 +68,11 @@ 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");
tt.addConstraint(f, ConstraintPtr(new PrimaryKeyConstraint()));
QCOMPARE(tt.sql(), QString("CREATE TABLE `testtable` (\n"
"\t`a`\tinteger PRIMARY KEY AUTOINCREMENT,\n"
@@ -134,8 +136,9 @@ void TestTable::parseSQL()
QVERIFY(tab.fields().at(1)->type() == "text");
QVERIFY(tab.fields().at(2)->type() == "VARCHAR(255)");
FieldVector pk = tab.primaryKey();
QVERIFY(tab.fields().at(0)->autoIncrement());
QVERIFY(tab.fields().at(0)->primaryKey());
QVERIFY(pk.size() == 1 && pk.at(0) == tab.fields().at(0));
QVERIFY(tab.fields().at(1)->notnull());
QCOMPARE(tab.fields().at(1)->defaultValue(), QString("'xxxx'"));
QCOMPARE(tab.fields().at(1)->check(), QString(""));
@@ -170,7 +173,8 @@ void TestTable::parseSQLdefaultexpr()
QCOMPARE(tab.fields().at(3)->defaultValue(), QString(""));
QCOMPARE(tab.fields().at(3)->check(), QString(""));
QVERIFY(tab.fields().at(0)->primaryKey());
sqlb::FieldVector pk = tab.primaryKey();
QVERIFY(pk.size() == 1 && pk.at(0) == tab.fields().at(0));
}
void TestTable::parseSQLMultiPk()
@@ -191,8 +195,8 @@ void TestTable::parseSQLMultiPk()
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());
sqlb::FieldVector pk = tab.primaryKey();
QVERIFY(pk.size() == 2 && pk.at(0) == tab.fields().at(0) && pk.at(1) == tab.fields().at(1));
}
void TestTable::parseSQLForeignKey()