mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-05-24 22:48:23 -05:00
parser: Extended support for NOT NULL and UNIQUE column constraints
This adds support for conflict actions in NOT NULL and UNIQUE column constraints. It also supports named NOT NULL and UNIQUE column constraints. With this it is now possible to edit tables which make use of these constraints without losing any information. Adding constraints like this via the UI is still not supported though. Once more constraints are handled like this, the code can be probably simplified a lot.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// A Bison parser, made by GNU Bison 3.5.1.
|
||||
// A Bison parser, made by GNU Bison 3.6.3.
|
||||
|
||||
// Locations for Bison parsers in C++
|
||||
|
||||
|
||||
+833
-778
File diff suppressed because it is too large
Load Diff
+707
-422
File diff suppressed because it is too large
Load Diff
@@ -36,8 +36,7 @@
|
||||
type = other.type;
|
||||
is_table_constraint = other.is_table_constraint;
|
||||
fully_parsed = other.fully_parsed;
|
||||
if(is_table_constraint)
|
||||
table_constraint = other.table_constraint;
|
||||
constraint = other.constraint;
|
||||
text = other.text;
|
||||
generated_constraint = other.generated_constraint;
|
||||
|
||||
@@ -66,7 +65,7 @@
|
||||
bool is_table_constraint;
|
||||
bool fully_parsed;
|
||||
|
||||
sqlb::ConstraintPtr table_constraint;
|
||||
sqlb::ConstraintPtr constraint;
|
||||
std::string text;
|
||||
sqlb::GeneratedColumnConstraint generated_constraint;
|
||||
};
|
||||
@@ -691,7 +690,7 @@ columnconstraint:
|
||||
sqlb::PrimaryKeyConstraint* pk = new sqlb::PrimaryKeyConstraint({sqlb::IndexedColumn("", false, $4)});
|
||||
pk->setName($1);
|
||||
pk->setConflictAction($5);
|
||||
$$.table_constraint = sqlb::ConstraintPtr(pk);
|
||||
$$.constraint = sqlb::ConstraintPtr(pk);
|
||||
$$.fully_parsed = true;
|
||||
}
|
||||
| optional_constraintname PRIMARY KEY optional_sort_order optional_conflictclause AUTOINCREMENT {
|
||||
@@ -701,13 +700,17 @@ columnconstraint:
|
||||
pk->setName($1);
|
||||
pk->setConflictAction($5);
|
||||
pk->setAutoIncrement(true);
|
||||
$$.table_constraint = sqlb::ConstraintPtr(pk);
|
||||
$$.constraint = sqlb::ConstraintPtr(pk);
|
||||
$$.fully_parsed = true;
|
||||
}
|
||||
| optional_constraintname NOT NULL optional_conflictclause {
|
||||
$$.type = ColumnConstraintInfo::NotNull;
|
||||
$$.is_table_constraint = false;
|
||||
$$.fully_parsed = ($1 == "" && $4 == "");
|
||||
sqlb::NotNullConstraint* nn = new sqlb::NotNullConstraint();
|
||||
nn->setName($1);
|
||||
nn->setConflictAction($4);
|
||||
$$.constraint = sqlb::ConstraintPtr(nn);
|
||||
$$.fully_parsed = true;
|
||||
}
|
||||
| optional_constraintname NULL {
|
||||
$$.type = ColumnConstraintInfo::None;
|
||||
@@ -717,7 +720,11 @@ columnconstraint:
|
||||
| optional_constraintname UNIQUE optional_conflictclause {
|
||||
$$.type = ColumnConstraintInfo::Unique;
|
||||
$$.is_table_constraint = false;
|
||||
$$.fully_parsed = ($1 == "" && $3 == "");
|
||||
sqlb::UniqueConstraint* u = new sqlb::UniqueConstraint();
|
||||
u->setName($1);
|
||||
u->setConflictAction($3);
|
||||
$$.constraint = sqlb::ConstraintPtr(u);
|
||||
$$.fully_parsed = true;
|
||||
}
|
||||
| optional_constraintname CHECK "(" expr ")" {
|
||||
$$.type = ColumnConstraintInfo::Check;
|
||||
@@ -776,7 +783,7 @@ columnconstraint:
|
||||
fk->setTable($3);
|
||||
fk->setColumns($4);
|
||||
fk->setConstraint($5);
|
||||
$$.table_constraint = sqlb::ConstraintPtr(fk);
|
||||
$$.constraint = sqlb::ConstraintPtr(fk);
|
||||
$$.fully_parsed = true;
|
||||
}
|
||||
| optional_constraintname optional_always_generated AS "(" expr ")" optional_storage_identifier { // TODO Solve shift/reduce conflict.
|
||||
@@ -809,16 +816,16 @@ columndef:
|
||||
|
||||
if(c.is_table_constraint)
|
||||
{
|
||||
if(c.table_constraint->columnList().empty())
|
||||
c.table_constraint->setColumnList({$1});
|
||||
if(c.constraint->columnList().empty())
|
||||
c.constraint->setColumnList({$1});
|
||||
else
|
||||
c.table_constraint->replaceInColumnList("", $1);
|
||||
table_constraints.insert(c.table_constraint);
|
||||
c.constraint->replaceInColumnList("", $1);
|
||||
table_constraints.insert(c.constraint);
|
||||
} else {
|
||||
if(c.type == ColumnConstraintInfo::NotNull) {
|
||||
f.setNotNull(true);
|
||||
f.setNotNull(std::dynamic_pointer_cast<sqlb::NotNullConstraint>(c.constraint));
|
||||
} else if(c.type == ColumnConstraintInfo::Unique) {
|
||||
f.setUnique(true);
|
||||
f.setUnique(std::dynamic_pointer_cast<sqlb::UniqueConstraint>(c.constraint));
|
||||
} else if(c.type == ColumnConstraintInfo::Check) {
|
||||
f.setCheck(c.text);
|
||||
} else if(c.type == ColumnConstraintInfo::Default) {
|
||||
|
||||
+25
-6
@@ -174,10 +174,29 @@ std::string UniqueConstraint::toSql() const
|
||||
if(!m_name.empty())
|
||||
result = "CONSTRAINT " + escapeIdentifier(m_name) + " ";
|
||||
|
||||
std::vector<std::string> u_columns;
|
||||
for(const auto& c : m_columns)
|
||||
u_columns.push_back(c.toString("", " "));
|
||||
result += "UNIQUE(" + joinStringVector(u_columns, ",") + ")";
|
||||
result += "UNIQUE";
|
||||
|
||||
if(m_columns.size())
|
||||
{
|
||||
std::vector<std::string> u_columns;
|
||||
for(const auto& c : m_columns)
|
||||
u_columns.push_back(c.toString("", " "));
|
||||
result += "(" + joinStringVector(u_columns, ",") + ")";
|
||||
}
|
||||
|
||||
if(!m_conflictAction.empty())
|
||||
result += " ON CONFLICT " + m_conflictAction;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string NotNullConstraint::toSql() const
|
||||
{
|
||||
std::string result;
|
||||
if(!m_name.empty())
|
||||
result = "CONSTRAINT " + escapeIdentifier(m_name) + " ";
|
||||
|
||||
result += "NOT NULL";
|
||||
|
||||
if(!m_conflictAction.empty())
|
||||
result += " ON CONFLICT " + m_conflictAction;
|
||||
@@ -258,13 +277,13 @@ std::string Field::toString(const std::string& indent, const std::string& sep) c
|
||||
{
|
||||
std::string str = indent + escapeIdentifier(m_name) + sep + m_type;
|
||||
if(m_notnull)
|
||||
str += " NOT NULL";
|
||||
str += " " + m_notnull->toSql();
|
||||
if(!m_defaultvalue.empty())
|
||||
str += " DEFAULT " + m_defaultvalue;
|
||||
if(!m_check.empty())
|
||||
str += " CHECK(" + m_check + ")";
|
||||
if(m_unique)
|
||||
str += " UNIQUE";
|
||||
str += " " + m_unique->toSql();
|
||||
if(!m_collation.empty())
|
||||
str += " COLLATE " + m_collation;
|
||||
if(!m_generated.empty())
|
||||
|
||||
+27
-10
@@ -147,6 +147,7 @@ public:
|
||||
ForeignKeyConstraintType,
|
||||
CheckConstraintType,
|
||||
GeneratedColumnConstraintType,
|
||||
NotNullConstraintType,
|
||||
|
||||
NoType = 999,
|
||||
};
|
||||
@@ -239,6 +240,22 @@ protected:
|
||||
std::string m_conflictAction;
|
||||
};
|
||||
|
||||
|
||||
class NotNullConstraint : public Constraint
|
||||
{
|
||||
public:
|
||||
void setConflictAction(const std::string& conflict) { m_conflictAction = conflict; }
|
||||
const std::string& conflictAction() const { return m_conflictAction; }
|
||||
|
||||
std::string toSql() const override;
|
||||
|
||||
ConstraintTypes type() const override { return NotNullConstraintType; }
|
||||
|
||||
protected:
|
||||
std::string m_conflictAction;
|
||||
};
|
||||
|
||||
|
||||
class PrimaryKeyConstraint : public UniqueConstraint
|
||||
{
|
||||
// Primary keys are a sort of unique constraint for us. This matches quite nicely as both can have a conflict action
|
||||
@@ -308,8 +325,6 @@ class Field
|
||||
{
|
||||
public:
|
||||
Field()
|
||||
: m_notnull(false),
|
||||
m_unique(false)
|
||||
{}
|
||||
|
||||
Field(const std::string& name,
|
||||
@@ -321,10 +336,10 @@ public:
|
||||
const std::string& collation = std::string())
|
||||
: m_name(name)
|
||||
, m_type(type)
|
||||
, m_notnull(notnull)
|
||||
, m_notnull(notnull ? std::make_shared<NotNullConstraint>() : nullptr)
|
||||
, m_check(check)
|
||||
, m_defaultvalue(defaultvalue)
|
||||
, m_unique(unique)
|
||||
, m_unique(unique ? std::make_shared<UniqueConstraint>() : nullptr)
|
||||
, m_collation(collation)
|
||||
{}
|
||||
|
||||
@@ -334,10 +349,12 @@ public:
|
||||
|
||||
void setName(const std::string& name) { m_name = name; }
|
||||
void setType(const std::string& type) { m_type = type; }
|
||||
void setNotNull(bool notnull = true) { m_notnull = notnull; }
|
||||
void setNotNull(std::shared_ptr<sqlb::NotNullConstraint> notnull) { m_notnull = notnull; }
|
||||
void setNotNull(bool notnull = true) { if(notnull) m_notnull = std::make_shared<NotNullConstraint>(); else m_notnull.reset(); }
|
||||
void setCheck(const std::string& check) { m_check = check; }
|
||||
void setDefaultValue(const std::string& defaultvalue) { m_defaultvalue = defaultvalue; }
|
||||
void setUnique(bool u) { m_unique = u; }
|
||||
void setUnique(std::shared_ptr<sqlb::UniqueConstraint> u) { m_unique = u; }
|
||||
void setUnique(bool u) { if(u) m_unique = std::make_shared<UniqueConstraint>(); else m_unique.reset(); }
|
||||
void setCollation(const std::string& collation) { m_collation = collation; }
|
||||
|
||||
bool isText() const;
|
||||
@@ -359,10 +376,10 @@ public:
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
const std::string& type() const { return m_type; }
|
||||
bool notnull() const { return m_notnull; }
|
||||
bool notnull() const { return m_notnull ? true : false; }
|
||||
const std::string& check() const { return m_check; }
|
||||
const std::string& defaultValue() const { return m_defaultvalue; }
|
||||
bool unique() const { return m_unique; }
|
||||
bool unique() const { return m_unique ? true : false; }
|
||||
const std::string& collation() const { return m_collation; }
|
||||
|
||||
const GeneratedColumnConstraint& generated() const { return m_generated; }
|
||||
@@ -372,10 +389,10 @@ public:
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_type;
|
||||
bool m_notnull;
|
||||
std::shared_ptr<NotNullConstraint> m_notnull;
|
||||
std::string m_check;
|
||||
std::string m_defaultvalue;
|
||||
bool m_unique;
|
||||
std::shared_ptr<UniqueConstraint> m_unique;
|
||||
std::string m_collation;
|
||||
GeneratedColumnConstraint m_generated;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user