grammar: Support named unique constraints

Also restructure the unique constraint handling a bit to be more
parallel to the foreign key handling.

Note that this (intentionally) introduces a bit of unnecessary
code duplication which is to be eliminated in one of the next commits.
This commit is contained in:
Martin Kleusberg
2016-08-26 14:28:40 +02:00
parent ce75ccf035
commit 5e6900ef5a
2 changed files with 64 additions and 27 deletions

View File

@@ -67,6 +67,16 @@ QString ForeignKeyClause::toSql(const FieldVector& applyOn) const
return result;
}
QString UniqueConstraint::toSql(const FieldVector& applyOn) const
{
QString result;
if(!m_name.isNull())
result += QString("CONSTRAINT %1 ").arg(escapeIdentifier(m_name));
result += QString("UNIQUE(%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;
@@ -154,24 +164,39 @@ void Table::setField(int index, FieldPtr f)
if(oldField)
{
// Unique constraints
for(int i=0;i<m_uniqueConstraints.size();++i)
UniqueMap::iterator it1 = m_uniqueConstraints.begin();
while(it1 != m_uniqueConstraints.end())
{
// Loop through all the fields mentioned in each unique constraint
FieldVector& constraint = m_uniqueConstraints[i];
for(int j=0;j<constraint.size();++j)
// Loop through all fields mentioned in a foreign key
FieldVector fields = it1.key();
bool modified = false;
for(int i=0;i<fields.size();++i)
{
// If the field that is being modified is in there update it to the new field
if(constraint[j] == oldField)
constraint[j] = f;
// If the field that is being modified is in there update it to the new field and set a flag that something has changed.
// This is used below to know when to update the map key
if(fields[i] == oldField)
{
fields[i] = f;
modified = true;
}
}
if(modified)
{
// When we need to update the map key, we insert a new constraint using the updated field vector and the old
// constraint information, and delete the old one afterwards
m_uniqueConstraints.insert(fields, it1.value());
it1 = m_uniqueConstraints.erase(it1);
} else {
++it1;
}
}
// Foreign keys
ForeignKeyMap::iterator it = m_foreignKeyClauses.begin();
while(it != m_foreignKeyClauses.end())
ForeignKeyMap::iterator it2 = m_foreignKeyClauses.begin();
while(it2 != m_foreignKeyClauses.end())
{
// Loop through all fields mentioned in a foreign key
FieldVector fields = it.key();
FieldVector fields = it2.key();
bool modified = false;
for(int i=0;i<fields.size();++i)
{
@@ -187,10 +212,10 @@ void Table::setField(int index, FieldPtr f)
{
// When we need to update the map key, we insert a new foreign key clause using the updated field vector and the old
// foreign key information, and delete the old one afterwards
m_foreignKeyClauses.insert(fields, it.value());
it = m_foreignKeyClauses.erase(it);
m_foreignKeyClauses.insert(fields, it2.value());
it2 = m_foreignKeyClauses.erase(it2);
} else {
++it;
++it2;
}
}
}
@@ -301,19 +326,21 @@ QString Table::sql() const
}
// unique constraints
foreach(FieldVector constraint, m_uniqueConstraints)
UniqueMap::const_iterator it1 = m_uniqueConstraints.constBegin();
while(it1 != m_uniqueConstraints.constEnd())
{
QStringList fieldnames = fieldVectorToFieldNames(constraint);
sql += QString(",\n\tUNIQUE(%1)").arg(fieldnames.join(","));
sql += QString(",\n\t");
sql += it1.value().toSql(it1.key());
++it1;
}
// foreign keys
ForeignKeyMap::const_iterator it = m_foreignKeyClauses.constBegin();
while(it != m_foreignKeyClauses.constEnd())
ForeignKeyMap::const_iterator it2 = m_foreignKeyClauses.constBegin();
while(it2 != m_foreignKeyClauses.constEnd())
{
sql += QString(",\n\t");
sql += it.value().toSql(it.key());
++it;
sql += it2.value().toSql(it2.key());
++it2;
}
sql += "\n)";
@@ -325,9 +352,9 @@ QString Table::sql() const
return sql + ";";
}
void Table::addUniqueConstraint(FieldVector fields)
void Table::addUniqueConstraint(FieldVector fields, UniqueConstraint unique)
{
m_uniqueConstraints.push_back(fields);
m_uniqueConstraints.insert(fields, unique);
}
void Table::addForeignKey(FieldPtr field, ForeignKeyClause fk)
@@ -507,8 +534,8 @@ Table CreateTableWalker::table()
break;
case sqlite3TokenTypes::UNIQUE:
{
if(!constraint_name.isNull())
m_bModifySupported = false;
UniqueConstraint unique;
unique.setName(constraint_name);
tc = tc->getNextSibling(); // skip UNIQUE
tc = tc->getNextSibling(); // skip LPAREN
@@ -538,7 +565,7 @@ Table CreateTableWalker::table()
if(fields.size() == 1)
fields[0]->setUnique(true);
else
tab.addUniqueConstraint(fields);
tab.addUniqueConstraint(fields, unique);
}
break;
case sqlite3TokenTypes::FOREIGN:

View File

@@ -17,10 +17,12 @@ QString escapeIdentifier(QString id);
class Field;
class ForeignKeyClause;
class UniqueConstraint;
typedef QSharedPointer<Field> FieldPtr;
typedef QVector< FieldPtr > FieldVector;
typedef QMap<FieldVector, ForeignKeyClause> ForeignKeyMap;
typedef QMap<FieldVector, UniqueConstraint> UniqueMap;
class Constraint
{
@@ -74,6 +76,14 @@ private:
QString m_override;
};
class UniqueConstraint : public Constraint
{
public:
UniqueConstraint() {}
virtual QString toSql(const FieldVector& applyOn) const;
};
class Field
{
public:
@@ -164,7 +174,7 @@ public:
bool isWithoutRowidTable() const { return m_rowidColumn != "_rowid_"; }
void clear();
void addUniqueConstraint(FieldVector fields);
void addUniqueConstraint(FieldVector fields, UniqueConstraint unique);
void addForeignKey(FieldPtr field, ForeignKeyClause fk);
void addForeignKey(FieldVector fields, ForeignKeyClause fk);
@@ -198,7 +208,7 @@ private:
QString m_name;
FieldVector m_fields;
QString m_rowidColumn;
QVector<FieldVector> m_uniqueConstraints;
UniqueMap m_uniqueConstraints;
ForeignKeyMap m_foreignKeyClauses;
};