mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 11:00:44 -06:00
grammar: Add support for CHECK table constraints
This adds support for CHECK table constraints. We already had support for CHECK column constraints and still mostly use that but with this commit there are no more hickups when encountering such a table. It also adds basic support for editing those tables: the CHECK constraint isn't removed anymore when editing a different part of the table. Example: CREATE TABLE test( a int CHECK a IN(1,2,3), -- This was supported before b int, CHECK b IN (1,2,3) -- This is added by this commit );
This commit is contained in:
@@ -168,6 +168,16 @@ QString PrimaryKeyConstraint::toSql(const FieldVector& applyOn) const
|
||||
return result;
|
||||
}
|
||||
|
||||
QString CheckConstraint::toSql(const FieldVector&) const
|
||||
{
|
||||
QString result;
|
||||
if(!m_name.isNull())
|
||||
result += QString("CONSTRAINT %1 ").arg(escapeIdentifier(m_name));
|
||||
result += QString("CHECK(%1)").arg(m_expression);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString Field::toString(const QString& indent, const QString& sep) const
|
||||
{
|
||||
QString str = indent + escapeIdentifier(m_name) + sep + m_type;
|
||||
@@ -257,6 +267,10 @@ Table& Table::operator=(const Table& rhs)
|
||||
constraint = ConstraintPtr(new UniqueConstraint(*(it.value().dynamicCast<UniqueConstraint>())));
|
||||
else if(it.value()->type() == Constraint::ConstraintTypes::ForeignKeyConstraintType)
|
||||
constraint = ConstraintPtr(new ForeignKeyClause(*(it.value().dynamicCast<ForeignKeyClause>())));
|
||||
else if(it.value()->type() == Constraint::ConstraintTypes::CheckConstraintType)
|
||||
constraint = ConstraintPtr(new CheckConstraint(*(it.value().dynamicCast<CheckConstraint>())));
|
||||
else
|
||||
qWarning() << "Unknown constraint type";
|
||||
addConstraint(key, constraint);
|
||||
}
|
||||
|
||||
@@ -429,10 +443,15 @@ QString Table::sql() const
|
||||
bool autoincrement = hasAutoIncrement();
|
||||
while(it != m_constraints.constEnd())
|
||||
{
|
||||
if((!autoincrement || it.value()->type() != Constraint::PrimaryKeyConstraintType) && !it.key().isEmpty())
|
||||
// Ignore auto increment primary key constraint
|
||||
if((!autoincrement || it.value()->type() != Constraint::PrimaryKeyConstraintType))
|
||||
{
|
||||
sql += QString(",\n\t");
|
||||
sql += it.value()->toSql(it.key());
|
||||
// Ignore all constraints without any fields, except for check constraints which don't rely on a field vector
|
||||
if(!(it.key().isEmpty() && it.value()->type() != Constraint::CheckConstraintType))
|
||||
{
|
||||
sql += QString(",\n\t");
|
||||
sql += it.value()->toSql(it.key());
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
@@ -550,18 +569,22 @@ QString identifier(antlr::RefAST ident)
|
||||
return sident;
|
||||
}
|
||||
|
||||
QString textAST(antlr::RefAST t)
|
||||
{
|
||||
// When this is called for a KEYWORDASTABLENAME token, we must take the child's content to get the actual value
|
||||
// instead of 'KEYWORDASTABLENAME' as a string. The same applies for KEYWORDASCOLUMNNAME tokens.
|
||||
if(t != antlr::nullAST && (t->getType() == sqlite3TokenTypes::KEYWORDASTABLENAME || t->getType() == sqlite3TokenTypes::KEYWORDASCOLUMNNAME))
|
||||
return t->getFirstChild()->getText().c_str();
|
||||
else
|
||||
return t->getText().c_str();
|
||||
}
|
||||
|
||||
QString concatTextAST(antlr::RefAST t, bool withspace = false)
|
||||
{
|
||||
QStringList stext;
|
||||
while(t != antlr::nullAST)
|
||||
{
|
||||
// When this is called for a KEYWORDASTABLENAME token, we must take the child's content to get the actual value
|
||||
// instead of 'KEYWORDASTABLENAME' as a string. The same applies for KEYWORDASCOLUMNNAME tokens.
|
||||
if(t != antlr::nullAST && (t->getType() == sqlite3TokenTypes::KEYWORDASTABLENAME || t->getType() == sqlite3TokenTypes::KEYWORDASCOLUMNNAME))
|
||||
stext.append(t->getFirstChild()->getText().c_str());
|
||||
else
|
||||
stext.append(t->getText().c_str());
|
||||
|
||||
stext.append(textAST(t));
|
||||
t = t->getNextSibling();
|
||||
}
|
||||
return stext.join(withspace ? " " : "");
|
||||
@@ -817,8 +840,38 @@ TablePtr CreateTableWalker::table()
|
||||
tab->addConstraint(fields, ConstraintPtr(fk));
|
||||
}
|
||||
break;
|
||||
case sqlite3TokenTypes::CHECK:
|
||||
{
|
||||
CheckConstraint* check = new CheckConstraint;
|
||||
check->setName(constraint_name);
|
||||
|
||||
tc = tc->getNextSibling(); // skip CHECK
|
||||
tc = tc->getNextSibling(); // skip LPAREN
|
||||
|
||||
int num_paren = 1;
|
||||
QString expr;
|
||||
while(tc)
|
||||
{
|
||||
if(tc->getType() == sqlite3TokenTypes::LPAREN)
|
||||
num_paren++;
|
||||
else if(tc->getType() == sqlite3TokenTypes::RPAREN)
|
||||
num_paren--;
|
||||
|
||||
if(num_paren == 0)
|
||||
break;
|
||||
|
||||
expr.append(textAST(tc));
|
||||
|
||||
tc = tc->getNextSibling();
|
||||
}
|
||||
|
||||
check->setExpression(expr);
|
||||
tab->addConstraint(FieldVector(), ConstraintPtr(check));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
qWarning() << "unknown table constraint in " << tab->name();
|
||||
tab->setFullyParsed(false);
|
||||
}
|
||||
break;
|
||||
@@ -979,6 +1032,7 @@ void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c)
|
||||
break;
|
||||
default:
|
||||
{
|
||||
qWarning() << "unknown column constraint in " << table->name() << "." << colname;
|
||||
table->setFullyParsed(false);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -108,6 +108,7 @@ public:
|
||||
PrimaryKeyConstraintType,
|
||||
UniqueConstraintType,
|
||||
ForeignKeyConstraintType,
|
||||
CheckConstraintType,
|
||||
};
|
||||
|
||||
Constraint(const QString& name = QString())
|
||||
@@ -183,6 +184,25 @@ public:
|
||||
virtual ConstraintTypes type() const { return PrimaryKeyConstraintType; }
|
||||
};
|
||||
|
||||
class CheckConstraint : public Constraint
|
||||
{
|
||||
public:
|
||||
CheckConstraint(const QString& expr = QString())
|
||||
: m_expression(expr)
|
||||
{
|
||||
}
|
||||
|
||||
void setExpression(const QString& expr) { m_expression = expr; }
|
||||
QString expression() const { return m_expression; }
|
||||
|
||||
virtual QString toSql(const FieldVector& applyOn) const;
|
||||
|
||||
virtual ConstraintTypes type() const { return CheckConstraintType; }
|
||||
|
||||
private:
|
||||
QString m_expression;
|
||||
};
|
||||
|
||||
class Field
|
||||
{
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user