mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-23 20:39:57 -06:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user