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