grammar: Change the way the table constraints are stored

This changes the way we store the constraints associated with a table
from using a map to using a set. The map was mapping from the list of
field names to the constraint applied on these fields. Now the field
list is stored inside the constraint and we can store the constraints in
a simple set. This turns out to simplify the code noticeably.
This commit is contained in:
Martin Kleusberg
2019-07-28 14:40:54 +02:00
parent 9d654a19ba
commit cb694dd2c6
5 changed files with 110 additions and 166 deletions

View File

@@ -14,7 +14,6 @@
#include <algorithm>
Q_DECLARE_METATYPE(sqlb::ConstraintPtr)
Q_DECLARE_METATYPE(sqlb::StringVector)
EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& tableName, bool createTable, QWidget* parent)
: QDialog(parent),
@@ -87,29 +86,29 @@ EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier&
// Check whether the double clicked item is in the columns column
if(item->column() == kConstraintColumns)
{
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintName)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintColumns)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
// Do not allow editing the columns list of a CHECK constraint because CHECK constraints are independent of column lists
if(constraint->type() == sqlb::Constraint::CheckConstraintType)
return;
// Show the select items popup dialog
SelectItemsPopup* dialog = new SelectItemsPopup(m_table.fieldNames(), item->data(Qt::UserRole).value<sqlb::StringVector>(), this);
SelectItemsPopup* dialog = new SelectItemsPopup(m_table.fieldNames(), item->data(Qt::UserRole).value<sqlb::ConstraintPtr>()->column_list, this);
QRect item_rect = ui->tableConstraints->visualItemRect(item);
dialog->move(ui->tableConstraints->mapToGlobal(QPoint(ui->tableConstraints->x() + item_rect.x(),
ui->tableConstraints->y() + item_rect.y() + item_rect.height() / 2)));
dialog->show();
// When clicking the Apply button in the popup dialog, save the new columns list
connect(dialog, &SelectItemsPopup::accepted, [this, dialog, item, constraint]() {
connect(dialog, &SelectItemsPopup::accepted, [this, dialog, constraint]() {
// Check if column selection changed at all
sqlb::StringVector columns_before = ui->tableConstraints->item(item->row(), kConstraintColumns)->data(Qt::UserRole).value<sqlb::StringVector>();
sqlb::StringVector columns_after = dialog->selectedItems();
if(columns_before != columns_after)
sqlb::StringVector new_columns = dialog->selectedItems();
if(constraint->column_list != new_columns)
{
// Remove the constraint with the old columns and add a new one with the new columns
m_table.removeConstraint(columns_before, constraint);
m_table.addConstraint(columns_after, constraint);
m_table.removeConstraint(constraint);
constraint->column_list = new_columns;
m_table.addConstraint(constraint);
// Update the UI
populateFields();
@@ -221,15 +220,14 @@ void EditTableDialog::populateConstraints()
ui->tableConstraints->setRowCount(static_cast<int>(constraints.size()));
int row = 0;
for(const auto& pair : constraints)
for(const auto& constraint : constraints)
{
const auto& columns = pair.first;
const auto& constraint = pair.second;
const auto& columns = constraint->column_list;
// Columns
QTableWidgetItem* column = new QTableWidgetItem(QString::fromStdString(sqlb::joinStringVector(columns, ",")));
column->setData(Qt::UserRole, QVariant::fromValue<sqlb::StringVector>(pair.first)); // Remember raw data of columns of constraint
column->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
column->setData(Qt::UserRole, QVariant::fromValue<sqlb::ConstraintPtr>(constraint)); // Remember address of constraint object. This is used for modifying it later
ui->tableConstraints->setItem(row, kConstraintColumns, column);
// Type
@@ -261,12 +259,11 @@ void EditTableDialog::populateConstraints()
// Name
QTableWidgetItem* name = new QTableWidgetItem(QString::fromStdString(constraint->name()));
name->setData(Qt::UserRole, QVariant::fromValue<sqlb::ConstraintPtr>(pair.second)); // Remember address of constraint object. This is used for modifying it later
name->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
ui->tableConstraints->setItem(row, kConstraintName, name);
// SQL
QTableWidgetItem* sql = new QTableWidgetItem(QString::fromStdString(constraint->toSql(columns)));
QTableWidgetItem* sql = new QTableWidgetItem(QString::fromStdString(constraint->toSql()));
sql->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
ui->tableConstraints->setItem(row, kConstraintSql, sql);
@@ -482,7 +479,7 @@ void EditTableDialog::fieldItemChanged(QTreeWidgetItem *item, int column)
}
} else if(item->checkState(column) == Qt::Checked) {
// There is no primary key in the table yet. This means we need to add a default one.
m_table.addConstraint({field.name()}, sqlb::ConstraintPtr(new sqlb::PrimaryKeyConstraint()));
m_table.addConstraint(sqlb::ConstraintPtr(new sqlb::PrimaryKeyConstraint({field.name()})));
}
if(item->checkState(column) == Qt::Checked)
@@ -662,8 +659,7 @@ void EditTableDialog::fieldItemChanged(QTreeWidgetItem *item, int column)
void EditTableDialog::constraintItemChanged(QTableWidgetItem* item)
{
// Find modified constraint
sqlb::StringVector columns = ui->tableConstraints->item(item->row(), kConstraintColumns)->data(Qt::UserRole).value<sqlb::StringVector>();
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintName)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintColumns)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
// Which column has been modified?
switch(item->column())
@@ -674,7 +670,7 @@ void EditTableDialog::constraintItemChanged(QTableWidgetItem* item)
};
// Update SQL
ui->tableConstraints->item(item->row(), kConstraintSql)->setText(QString::fromStdString(constraint->toSql(columns)));
ui->tableConstraints->item(item->row(), kConstraintSql)->setText(QString::fromStdString(constraint->toSql()));
checkInput();
}
@@ -872,11 +868,10 @@ void EditTableDialog::removeConstraint()
// Find constraint to delete
int row = ui->tableConstraints->currentRow();
sqlb::StringVector columns = ui->tableConstraints->item(row, kConstraintColumns)->data(Qt::UserRole).value<sqlb::StringVector>();
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(row, kConstraintName)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
sqlb::ConstraintPtr constraint = ui->tableConstraints->item(row, kConstraintColumns)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();
// Remove the constraint. If there is more than one constraint with this combination of columns and constraint type, only delete the first one.
m_table.removeConstraint(columns, constraint);
m_table.removeConstraint(constraint);
ui->tableConstraints->removeRow(ui->tableConstraints->currentRow());
// Update SQL and view

View File

@@ -153,6 +153,7 @@ void ForeignKeyEditorDelegate::setModelData(QWidget* editor, QAbstractItemModel*
const QString clause = fkEditor->clauseEdit->text();
fk->setTable(table.toStdString());
fk->column_list = { field.name() };
if (!id.empty())
fk->setColumns({id});
@@ -161,7 +162,7 @@ void ForeignKeyEditorDelegate::setModelData(QWidget* editor, QAbstractItemModel*
fk->setConstraint(clause.toStdString());
}
m_table.setConstraint({field.name()}, sqlb::ConstraintPtr(fk));
m_table.setConstraint(sqlb::ConstraintPtr(fk));
}
model->setData(index, sql);

View File

@@ -160,32 +160,32 @@ void ForeignKeyClause::setFromString(const std::string& fk)
m_override = fk;
}
std::string ForeignKeyClause::toSql(const StringVector& applyOn) const
std::string ForeignKeyClause::toSql() const
{
std::string result;
if(!m_name.empty())
result = "CONSTRAINT " + escapeIdentifier(m_name) + " ";
result += "FOREIGN KEY(" + joinStringVector(escapeIdentifier(applyOn), ",") + ") REFERENCES " + this->toString();
result += "FOREIGN KEY(" + joinStringVector(escapeIdentifier(column_list), ",") + ") REFERENCES " + this->toString();
return result;
}
std::string UniqueConstraint::toSql(const StringVector& applyOn) const
std::string UniqueConstraint::toSql() const
{
std::string result;
if(!m_name.empty())
result = "CONSTRAINT " + escapeIdentifier(m_name) + " ";
result += "UNIQUE(" + joinStringVector(escapeIdentifier(applyOn), ",") + ")";
result += "UNIQUE(" + joinStringVector(escapeIdentifier(column_list), ",") + ")";
return result;
}
std::string PrimaryKeyConstraint::toSql(const StringVector& applyOn) const
std::string PrimaryKeyConstraint::toSql() const
{
std::string result;
if(!m_name.empty())
result = "CONSTRAINT " + escapeIdentifier(m_name) + " ";
result += "PRIMARY KEY(" + joinStringVector(escapeIdentifier(applyOn), ",") + ")";
result += "PRIMARY KEY(" + joinStringVector(escapeIdentifier(column_list), ",") + ")";
if(!m_conflictAction.empty())
result += " ON CONFLICT " + m_conflictAction;
@@ -193,7 +193,7 @@ std::string PrimaryKeyConstraint::toSql(const StringVector& applyOn) const
return result;
}
std::string CheckConstraint::toSql(const StringVector&) const
std::string CheckConstraint::toSql() const
{
std::string result;
if(!m_name.empty())
@@ -341,31 +341,8 @@ bool Table::operator==(const Table& rhs) const
return false;
if(fields != rhs.fields)
return false;
// We need to compare the constraint maps manually here. The reason is that the values are pointers and the default implementation
// would compare the pointers not the actual objects.
if(m_constraints.size() != rhs.m_constraints.size())
if(m_constraints != rhs.m_constraints)
return false;
for(auto it=m_constraints.cbegin();it!=m_constraints.end();++it)
{
// For each element in this map we get the list of all elements with the same key from the other map.
// Then we loop through that list and check if we find an element of the same type which produces the same SQL substring. We use this
// approach to avoid casting both objects to their actual type, then dereferencing it etc.
auto range = rhs.m_constraints.equal_range(it->first);
bool found_something = false;
for(auto jt=range.first;jt!=range.second;++jt)
{
if(it->second->type() == jt->second->type() && it->second->toSql(it->first) == jt->second->toSql(jt->first))
{
found_something = true;
break;
}
}
// If no match was found, the constraint maps aren't equal
if(!found_something)
return false;
}
return true;
}
@@ -469,21 +446,19 @@ std::string Table::sql(const std::string& schema, bool ifNotExists) const
sql += joinStringVector(fieldList(), ",\n");
// Constraints
ConstraintMap::const_iterator it = m_constraints.cbegin();
bool autoincrement = hasAutoIncrement();
while(it != m_constraints.cend())
for(const auto& it : m_constraints)
{
// Ignore auto increment primary key constraint
if((!autoincrement || it->second->type() != Constraint::PrimaryKeyConstraintType))
if((!autoincrement || it->type() != Constraint::PrimaryKeyConstraintType))
{
// Ignore all constraints without any fields, except for check constraints which don't rely on a field vector
if(!(it->first.empty() && it->second->type() != Constraint::CheckConstraintType))
if(!it->column_list.empty() || it->type() == Constraint::CheckConstraintType)
{
sql += ",\n\t";
sql += it->second->toSql(it->first);
sql += it->toSql();
}
}
++it;
}
sql += "\n)";
@@ -495,25 +470,25 @@ std::string Table::sql(const std::string& schema, bool ifNotExists) const
return sql + ";";
}
void Table::addConstraint(const StringVector& vStrFields, ConstraintPtr constraint)
void Table::addConstraint(ConstraintPtr constraint)
{
m_constraints.insert({vStrFields, constraint});
m_constraints.insert(constraint);
}
void Table::setConstraint(const StringVector& vStrFields, ConstraintPtr constraint)
void Table::setConstraint(ConstraintPtr constraint)
{
// Delete any old constraints of this type for these fields
removeConstraints(vStrFields, constraint->type());
removeConstraints(constraint->column_list, constraint->type());
// Add the new constraint to the table, effectively overwriting all old constraints for that fields/type combination
addConstraint(vStrFields, constraint);
addConstraint(constraint);
}
void Table::removeConstraint(const StringVector& vStrFields, ConstraintPtr constraint)
void Table::removeConstraint(ConstraintPtr constraint)
{
for(auto it = m_constraints.begin();it!=m_constraints.end();++it)
{
if(it->first == vStrFields && it->second->toSql(vStrFields) == constraint->toSql(vStrFields))
if((*it)->toSql() == constraint->toSql())
{
m_constraints.erase(it);
@@ -527,7 +502,7 @@ void Table::removeConstraints(const StringVector& vStrFields, Constraint::Constr
{
for(auto it = m_constraints.begin();it!=m_constraints.end();)
{
if(it->first == vStrFields && it->second->type() == type)
if((*it)->column_list == vStrFields && (*it)->type() == type)
m_constraints.erase(it++);
else
++it;
@@ -545,33 +520,16 @@ ConstraintPtr Table::constraint(const StringVector& vStrFields, Constraint::Cons
std::vector<ConstraintPtr> Table::constraints(const StringVector& vStrFields, Constraint::ConstraintTypes type) const
{
ConstraintMap::const_iterator begin, end;
if(vStrFields.empty())
{
begin = m_constraints.begin();
end = m_constraints.end();
} else {
std::tie(begin, end) = m_constraints.equal_range(vStrFields);
}
std::vector<ConstraintPtr> clist;
std::transform(begin, end, std::back_inserter(clist), [](std::pair<StringVector, ConstraintPtr> elem){return elem.second;});
if(type == Constraint::NoType)
for(const auto& it : m_constraints)
{
return clist;
} else {
std::vector<ConstraintPtr> clist_typed;
for(const ConstraintPtr& ptr : clist)
{
if(ptr->type() == type)
clist_typed.push_back(ptr);
}
return clist_typed;
if((type == Constraint::NoType || it->type() == type) && (vStrFields.empty() || it->column_list == vStrFields))
clist.push_back(it);
}
return clist;
}
void Table::setConstraints(const ConstraintMap& constraints)
void Table::setConstraints(const ConstraintSet& constraints)
{
m_constraints = constraints;
}
@@ -583,12 +541,10 @@ StringVector& Table::primaryKeyRef()
const StringVector& Table::primaryKey() const
{
auto it = m_constraints.cbegin();
while(it != m_constraints.cend())
for(const auto& it : m_constraints)
{
if(it->second->type() == Constraint::PrimaryKeyConstraintType)
return it->first;
++it;
if(it->type() == Constraint::PrimaryKeyConstraintType)
return it->column_list;
}
static StringVector emptyFieldVector;
@@ -597,18 +553,20 @@ const StringVector& Table::primaryKey() const
void Table::removeKeyFromAllConstraints(const std::string& key)
{
// First remove all constraints with exactly that one key
m_constraints.erase({key});
// Then delete all occurrences of the key in compound columns
// Update all constraints
for(auto it=m_constraints.begin();it!=m_constraints.end();)
{
if(contains(it->first, key))
// Check if they contain the old key name
if(contains((*it)->column_list, key))
{
StringVector k = it->first;
k.erase(std::remove(k.begin(), k.end(), key), k.end());
m_constraints.insert({k, it->second});
it = m_constraints.erase(it);
// If so, remove it from the column list
(*it)->column_list.erase(std::remove((*it)->column_list.begin(), (*it)->column_list.end(), key), (*it)->column_list.end());
// If the column list is empty now, remove the entire constraint. Otherwise save the updated column list
if((*it)->column_list.empty())
it = m_constraints.erase(it);
else
++it;
} else {
++it;
}
@@ -622,17 +580,10 @@ void Table::renameKeyInAllConstraints(const std::string& key, const std::string&
return;
// Find all occurrences of the key and change it to the new one
for(auto it=m_constraints.begin();it!=m_constraints.end();)
for(auto& it : m_constraints)
{
if(contains(it->first, key))
{
StringVector k = it->first;
std::replace(k.begin(), k.end(), key, to);
m_constraints.insert({k, it->second});
it = m_constraints.erase(it);
} else {
++it;
}
if(contains(it->column_list, key))
std::replace(it->column_list.begin(), it->column_list.end(), key, to);
}
}
@@ -843,13 +794,12 @@ TablePtr CreateTableWalker::table()
tc = tc->getNextSibling()->getNextSibling(); // skip primary and key
tc = tc->getNextSibling(); // skip LPAREN
StringVector fields;
do
{
antlr::RefAST indexed_column = tc->getFirstChild();
std::string col = columnname(indexed_column);
fields.push_back(col);
pk->column_list.push_back(col);
indexed_column = indexed_column->getNextSibling();
if(indexed_column != antlr::nullAST
@@ -888,7 +838,7 @@ TablePtr CreateTableWalker::table()
tc = tc->getNextSibling(); // skip RPAREN
pk->setConflictAction(parseConflictClause(tc));
tab->addConstraint(fields, ConstraintPtr(pk));
tab->addConstraint(ConstraintPtr(pk));
}
break;
case sqlite3TokenTypes::UNIQUE:
@@ -898,14 +848,13 @@ TablePtr CreateTableWalker::table()
tc = tc->getNextSibling(); // skip UNIQUE
tc = tc->getNextSibling(); // skip LPAREN
StringVector fields;
do
{
antlr::RefAST indexed_column = tc->getFirstChild();
std::string col = columnname(indexed_column);
auto field = findField(tab, col);
fields.push_back(field->name());
unique->column_list.push_back(field->name());
indexed_column = indexed_column->getNextSibling();
if(indexed_column != antlr::nullAST
@@ -933,12 +882,12 @@ TablePtr CreateTableWalker::table()
}
} while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN);
if(fields.size() == 1 && constraint_name.empty())
if(unique->column_list.size() == 1 && constraint_name.empty())
{
findField(tab, fields[0])->setUnique(true);
findField(tab, unique->column_list[0])->setUnique(true);
delete unique;
} else {
tab->addConstraint(fields, ConstraintPtr(unique));
tab->addConstraint(ConstraintPtr(unique));
}
}
break;
@@ -951,11 +900,10 @@ TablePtr CreateTableWalker::table()
tc = tc->getNextSibling(); // KEY
tc = tc->getNextSibling(); // LPAREN
StringVector fields;
do
{
std::string col = columnname(tc);
fields.push_back(findField(tab, col)->name());
fk->column_list.push_back(findField(tab, col)->name());
tc = tc->getNextSibling();
@@ -986,7 +934,7 @@ TablePtr CreateTableWalker::table()
}
fk->setConstraint(concatTextAST(tc, true));
tab->addConstraint(fields, ConstraintPtr(fk));
tab->addConstraint(ConstraintPtr(fk));
}
break;
case sqlite3TokenTypes::CHECK:
@@ -998,7 +946,7 @@ TablePtr CreateTableWalker::table()
tc = tc->getNextSibling(); // skip LPAREN
check->setExpression(concatExprAST(tc));
tab->addConstraint(StringVector(), ConstraintPtr(check));
tab->addConstraint(ConstraintPtr(check));
}
break;
default:
@@ -1093,6 +1041,7 @@ void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c)
delete primaryKey;
primaryKey = new PrimaryKeyConstraint;
primaryKey->column_list = { colname };
primaryKey->setName(constraint_name);
con = con->getNextSibling()->getNextSibling(); // skip KEY
@@ -1135,7 +1084,8 @@ void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c)
{
CheckConstraint* check_constraint = new CheckConstraint(check);
check_constraint->setName(constraint_name);
table->addConstraint({colname}, ConstraintPtr(check_constraint));
check_constraint->column_list = { colname };
table->addConstraint(ConstraintPtr(check_constraint));
check.clear();
}
}
@@ -1164,6 +1114,7 @@ void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c)
con = con->getNextSibling(); // REFERENCES
sqlb::ForeignKeyClause* foreignKey = new ForeignKeyClause;
foreignKey->column_list = { colname };
foreignKey->setTable(identifier(con));
foreignKey->setName(constraint_name);
con = con->getNextSibling(); // identifier
@@ -1210,7 +1161,7 @@ void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c)
table->fields.push_back(f);
for(sqlb::ForeignKeyClause* fk : foreignKeys)
table->addConstraint({f.name()}, ConstraintPtr(fk));
table->addConstraint(ConstraintPtr(fk));
if(primaryKey)
{
StringVector v;
@@ -1222,7 +1173,7 @@ void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c)
// don't need another one.
delete primaryKey;
} else {
table->addConstraint({f.name()}, ConstraintPtr(primaryKey));
table->addConstraint(ConstraintPtr(primaryKey));
}
}
}

View File

@@ -4,10 +4,10 @@
#include <algorithm>
#include <memory>
#include <unordered_map>
#include <string>
#include <vector>
#include <cctype>
#include <set>
template<typename C, typename E>
bool contains(const C& container, E element)
@@ -37,18 +37,6 @@ using StringVector = std::vector<std::string>;
StringVector escapeIdentifier(StringVector ids);
std::string joinStringVector(const StringVector& vec, const std::string& delim);
struct StringVectorHash
{
size_t operator()(const StringVector& key) const
{
// This is taken from Boost
size_t seed = 0;
for(const std::string& s : key)
seed ^= std::hash<std::string>{}(s) + 0x9e3779b9 + (seed << 6) + ( seed >> 2);
return seed;
}
};
class Object;
class Table;
class Index;
@@ -66,7 +54,7 @@ using TriggerPtr = std::shared_ptr<Trigger>;
using ConstraintPtr = std::shared_ptr<Constraint>;
using FieldVector = std::vector<Field>;
using IndexedColumnVector = std::vector<IndexedColumn>;
using ConstraintMap = std::unordered_multimap<StringVector, ConstraintPtr, StringVectorHash>;
using ConstraintSet = std::set<ConstraintPtr>;
using FieldInfoList = std::vector<FieldInfo>;
struct FieldInfo
@@ -138,8 +126,9 @@ public:
CheckConstraintType,
};
explicit Constraint(const std::string& name = std::string())
: m_name(name)
explicit Constraint(const StringVector& columns = {}, const std::string& name = std::string())
: column_list(columns),
m_name(name)
{
}
virtual ~Constraint() {}
@@ -149,7 +138,9 @@ public:
void setName(const std::string& name) { m_name = name; }
const std::string& name() const { return m_name; }
virtual std::string toSql(const StringVector& applyOn) const = 0;
virtual std::string toSql() const = 0;
StringVector column_list;
protected:
std::string m_name;
@@ -178,7 +169,7 @@ public:
void setConstraint(const std::string& constraint) { m_constraint = constraint; }
const std::string& constraint() const { return m_constraint; }
std::string toSql(const StringVector& applyOn) const override;
std::string toSql() const override;
ConstraintTypes type() const override { return ForeignKeyConstraintType; }
@@ -193,9 +184,11 @@ private:
class UniqueConstraint : public Constraint
{
public:
UniqueConstraint() {}
explicit UniqueConstraint(const StringVector& columns = {}) :
Constraint (columns)
{}
std::string toSql(const StringVector& applyOn) const override;
std::string toSql() const override;
ConstraintTypes type() const override { return UniqueConstraintType; }
};
@@ -203,12 +196,14 @@ public:
class PrimaryKeyConstraint : public Constraint
{
public:
PrimaryKeyConstraint() {}
explicit PrimaryKeyConstraint(const StringVector& columns = {}) :
Constraint (columns)
{}
void setConflictAction(const std::string& conflict) { m_conflictAction = conflict; }
const std::string& conflictAction() const { return m_conflictAction; }
std::string toSql(const StringVector& applyOn) const override;
std::string toSql() const override;
ConstraintTypes type() const override { return PrimaryKeyConstraintType; }
@@ -227,7 +222,7 @@ public:
void setExpression(const std::string& expr) { m_expression = expr; }
const std::string& expression() const { return m_expression; }
std::string toSql(const StringVector& applyOn) const override;
std::string toSql() const override;
ConstraintTypes type() const override { return CheckConstraintType; }
@@ -335,14 +330,14 @@ public:
FieldInfoList fieldInformation() const override;
void addConstraint(const StringVector& vStrFields, ConstraintPtr constraint);
void setConstraint(const StringVector& vStrFields, ConstraintPtr constraint);
void removeConstraint(const StringVector& vStrFields, ConstraintPtr constraint);
void addConstraint(ConstraintPtr constraint);
void setConstraint(ConstraintPtr constraint);
void removeConstraint(ConstraintPtr constraint);
void removeConstraints(const StringVector& vStrFields = StringVector(), Constraint::ConstraintTypes type = Constraint::NoType);
ConstraintPtr constraint(const StringVector& vStrFields = StringVector(), Constraint::ConstraintTypes type = Constraint::NoType) const; //! Only returns the first constraint, if any
std::vector<ConstraintPtr> constraints(const StringVector& vStrFields = StringVector(), Constraint::ConstraintTypes type = Constraint::NoType) const;
ConstraintMap allConstraints() const { return m_constraints; }
void setConstraints(const ConstraintMap& constraints);
ConstraintSet allConstraints() const { return m_constraints; }
void setConstraints(const ConstraintSet& constraints);
StringVector& primaryKeyRef();
const StringVector& primaryKey() const;
void removeKeyFromAllConstraints(const std::string& key);
@@ -360,7 +355,7 @@ private:
private:
bool m_withoutRowid;
ConstraintMap m_constraints;
ConstraintSet m_constraints;
std::string m_virtual;
};

View File

@@ -29,7 +29,7 @@ void TestTable::sqlOutput()
tt.fields.push_back(f);
tt.fields.emplace_back("car", "text");
tt.fields.push_back(fkm);
tt.addConstraint({f.name(), fkm.name()}, ConstraintPtr(new PrimaryKeyConstraint()));
tt.addConstraint(ConstraintPtr(new PrimaryKeyConstraint({f.name(), fkm.name()})));
QCOMPARE(tt.sql(), "CREATE TABLE \"testtable\" (\n"
"\t\"id\"\tinteger,\n"
@@ -47,7 +47,7 @@ void TestTable::sqlGraveAccentOutput()
tt.fields.push_back(f);
tt.fields.emplace_back("car", "text");
tt.fields.push_back(fkm);
tt.addConstraint({f.name(), fkm.name()}, ConstraintPtr(new PrimaryKeyConstraint()));
tt.addConstraint(ConstraintPtr(new PrimaryKeyConstraint({f.name(), fkm.name()})));
sqlb::setIdentifierQuoting(sqlb::GraveAccents);
QCOMPARE(tt.sql(), "CREATE TABLE `testtable` (\n"
@@ -69,7 +69,7 @@ void TestTable::sqlSquareBracketsOutput()
tt.fields.push_back(f);
tt.fields.emplace_back("car", "text");
tt.fields.push_back(fkm);
tt.addConstraint({f.name(), fkm.name()}, ConstraintPtr(new PrimaryKeyConstraint()));
tt.addConstraint(ConstraintPtr(new PrimaryKeyConstraint({f.name(), fkm.name()})));
sqlb::setIdentifierQuoting(sqlb::SquareBrackets);
QCOMPARE(tt.sql(), "CREATE TABLE [testtable] (\n"
@@ -91,7 +91,7 @@ void TestTable::autoincrement()
tt.fields.push_back(f);
tt.fields.emplace_back("car", "text");
tt.fields.push_back(fkm);
tt.addConstraint({f.name()}, ConstraintPtr(new PrimaryKeyConstraint()));
tt.addConstraint(ConstraintPtr(new PrimaryKeyConstraint({f.name()})));
QCOMPARE(tt.sql(), "CREATE TABLE \"testtable\" (\n"
"\t\"id\"\tinteger PRIMARY KEY AUTOINCREMENT,\n"
@@ -109,7 +109,7 @@ void TestTable::notnull()
tt.fields.push_back(f);
tt.fields.emplace_back("car", "text", true);
tt.fields.push_back(fkm);
tt.addConstraint({f.name()}, ConstraintPtr(new PrimaryKeyConstraint()));
tt.addConstraint(ConstraintPtr(new PrimaryKeyConstraint({f.name()})));
QCOMPARE(tt.sql(), "CREATE TABLE \"testtable\" (\n"
"\t\"id\"\tinteger PRIMARY KEY AUTOINCREMENT,\n"
@@ -126,7 +126,7 @@ void TestTable::withoutRowid()
tt.fields.push_back(f);
tt.fields.emplace_back("b", "integer");
tt.setWithoutRowidTable(true);
tt.addConstraint({f.name()}, ConstraintPtr(new PrimaryKeyConstraint()));
tt.addConstraint(ConstraintPtr(new PrimaryKeyConstraint({f.name()})));
QCOMPARE(tt.sql(), "CREATE TABLE \"testtable\" (\n"
"\t\"a\"\tinteger PRIMARY KEY AUTOINCREMENT,\n"
@@ -139,7 +139,9 @@ void TestTable::foreignKeys()
Table tt("testtable");
Field f("a", "integer");
tt.fields.push_back(f);
tt.addConstraint({f.name()}, sqlb::ConstraintPtr(new sqlb::ForeignKeyClause("b", sqlb::StringVector{"c"})));
sqlb::ConstraintPtr fk = sqlb::ConstraintPtr(new sqlb::ForeignKeyClause("b", sqlb::StringVector{"c"}));
fk->column_list = {f.name()};
tt.addConstraint(fk);
QCOMPARE(tt.sql(), "CREATE TABLE \"testtable\" (\n"
"\t\"a\"\tinteger,\n"
@@ -157,7 +159,7 @@ void TestTable::uniqueConstraint()
tt.fields.push_back(f1);
tt.fields.push_back(f2);
tt.fields.push_back(f3);
tt.addConstraint({f2.name(), f3.name()}, sqlb::ConstraintPtr(new sqlb::UniqueConstraint()));
tt.addConstraint(sqlb::ConstraintPtr(new sqlb::UniqueConstraint({f2.name(), f3.name()})));
QCOMPARE(tt.sql(), "CREATE TABLE \"testtable\" (\n"
"\t\"a\"\tinteger UNIQUE,\n"