forked from qt-creator/qt-creator
Sqlite: Improve constraint support
Now you can add more than one constraint. And we added some new constraints too. Change-Id: I849d2d2ef6e44c897a65ff2bdfe8d172a345c991 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -41,16 +41,11 @@ void CreateTableSqlStatementBuilder::setTableName(Utils::SmallString &&tableName
|
||||
|
||||
void CreateTableSqlStatementBuilder::addColumn(Utils::SmallStringView columnName,
|
||||
ColumnType columnType,
|
||||
Contraint constraint,
|
||||
ForeignKey &&foreignKey)
|
||||
Constraints &&constraints)
|
||||
{
|
||||
m_sqlStatementBuilder.clear();
|
||||
|
||||
m_columns.emplace_back(Utils::SmallStringView{},
|
||||
columnName,
|
||||
columnType,
|
||||
constraint,
|
||||
std::move(foreignKey));
|
||||
m_columns.emplace_back(Utils::SmallStringView{}, columnName, columnType, std::move(constraints));
|
||||
}
|
||||
|
||||
void CreateTableSqlStatementBuilder::setColumns(const SqliteColumns &columns)
|
||||
@@ -121,30 +116,90 @@ Utils::SmallStringView actionToText(ForeignKeyAction action)
|
||||
return "";
|
||||
}
|
||||
|
||||
void appendForeignKey(Utils::SmallString &columnDefinitionString, const ForeignKey &foreignKey)
|
||||
class ContraintsVisiter
|
||||
{
|
||||
columnDefinitionString.append(" REFERENCES ");
|
||||
columnDefinitionString.append(foreignKey.table);
|
||||
public:
|
||||
ContraintsVisiter(Utils::SmallString &columnDefinitionString)
|
||||
: columnDefinitionString(columnDefinitionString)
|
||||
{}
|
||||
|
||||
if (foreignKey.column.hasContent()) {
|
||||
columnDefinitionString.append("(");
|
||||
columnDefinitionString.append(foreignKey.column);
|
||||
void operator()(const Unique &) { columnDefinitionString.append(" UNIQUE"); }
|
||||
|
||||
void operator()(const PrimaryKey &) { columnDefinitionString.append(" PRIMARY KEY"); }
|
||||
|
||||
void operator()(const ForeignKey &foreignKey)
|
||||
{
|
||||
columnDefinitionString.append(" REFERENCES ");
|
||||
columnDefinitionString.append(foreignKey.table);
|
||||
|
||||
if (foreignKey.column.hasContent()) {
|
||||
columnDefinitionString.append("(");
|
||||
columnDefinitionString.append(foreignKey.column);
|
||||
columnDefinitionString.append(")");
|
||||
}
|
||||
|
||||
if (foreignKey.updateAction != ForeignKeyAction::NoAction) {
|
||||
columnDefinitionString.append(" ON UPDATE ");
|
||||
columnDefinitionString.append(actionToText(foreignKey.updateAction));
|
||||
}
|
||||
|
||||
if (foreignKey.deleteAction != ForeignKeyAction::NoAction) {
|
||||
columnDefinitionString.append(" ON DELETE ");
|
||||
columnDefinitionString.append(actionToText(foreignKey.deleteAction));
|
||||
}
|
||||
|
||||
if (foreignKey.enforcement == Enforment::Deferred)
|
||||
columnDefinitionString.append(" DEFERRABLE INITIALLY DEFERRED");
|
||||
}
|
||||
|
||||
void operator()(const NotNull &) { columnDefinitionString.append(" NOT NULL"); }
|
||||
|
||||
void operator()(const DefaultValue &defaultValue)
|
||||
{
|
||||
columnDefinitionString.append(" DEFAULT ");
|
||||
switch (defaultValue.value.type()) {
|
||||
case Sqlite::ValueType::Integer:
|
||||
columnDefinitionString.append(
|
||||
Utils::SmallString::number(defaultValue.value.toInteger()));
|
||||
break;
|
||||
case Sqlite::ValueType::Float:
|
||||
columnDefinitionString.append(Utils::SmallString::number(defaultValue.value.toFloat()));
|
||||
break;
|
||||
case Sqlite::ValueType::String:
|
||||
columnDefinitionString.append("\"");
|
||||
columnDefinitionString.append(defaultValue.value.toStringView());
|
||||
columnDefinitionString.append("\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const DefaultExpression &defaultexpression)
|
||||
{
|
||||
columnDefinitionString.append(" DEFAULT (");
|
||||
columnDefinitionString.append(defaultexpression.expression);
|
||||
columnDefinitionString.append(")");
|
||||
}
|
||||
|
||||
if (foreignKey.updateAction != ForeignKeyAction::NoAction) {
|
||||
columnDefinitionString.append(" ON UPDATE ");
|
||||
columnDefinitionString.append(actionToText(foreignKey.updateAction));
|
||||
void operator()(const Collate &collate)
|
||||
{
|
||||
columnDefinitionString.append(" COLLATE ");
|
||||
columnDefinitionString.append(collate.collation);
|
||||
}
|
||||
|
||||
if (foreignKey.deleteAction != ForeignKeyAction::NoAction) {
|
||||
columnDefinitionString.append(" ON DELETE ");
|
||||
columnDefinitionString.append(actionToText(foreignKey.deleteAction));
|
||||
void operator()(const GeneratedAlways &generatedAlways)
|
||||
{
|
||||
columnDefinitionString.append(" GENERATED ALWAYS AS (");
|
||||
columnDefinitionString.append(generatedAlways.expression);
|
||||
columnDefinitionString.append(")");
|
||||
|
||||
if (generatedAlways.storage == Sqlite::GeneratedAlwaysStorage::Virtual)
|
||||
columnDefinitionString.append(" VIRTUAL");
|
||||
else
|
||||
columnDefinitionString.append(" STORED");
|
||||
}
|
||||
|
||||
if (foreignKey.enforcement == Enforment::Deferred)
|
||||
columnDefinitionString.append(" DEFERRABLE INITIALLY DEFERRED");
|
||||
}
|
||||
Utils::SmallString &columnDefinitionString;
|
||||
};
|
||||
} // namespace
|
||||
void CreateTableSqlStatementBuilder::bindColumnDefinitions() const
|
||||
{
|
||||
@@ -154,19 +209,10 @@ void CreateTableSqlStatementBuilder::bindColumnDefinitions() const
|
||||
for (const Column &column : m_columns) {
|
||||
Utils::SmallString columnDefinitionString = {column.name, " ", column.typeString()};
|
||||
|
||||
switch (column.constraint) {
|
||||
case Contraint::PrimaryKey:
|
||||
columnDefinitionString.append(" PRIMARY KEY");
|
||||
break;
|
||||
case Contraint::Unique:
|
||||
columnDefinitionString.append(" UNIQUE");
|
||||
break;
|
||||
case Contraint::ForeignKey:
|
||||
appendForeignKey(columnDefinitionString, column.foreignKey);
|
||||
break;
|
||||
case Contraint::NoConstraint:
|
||||
break;
|
||||
}
|
||||
ContraintsVisiter visiter{columnDefinitionString};
|
||||
|
||||
for (const Constraint &constraint : column.constraints)
|
||||
mpark::visit(visiter, constraint);
|
||||
|
||||
columnDefinitionStrings.push_back(columnDefinitionString);
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ public:
|
||||
CreateTableSqlStatementBuilder();
|
||||
|
||||
void setTableName(Utils::SmallString &&tableName);
|
||||
|
||||
void addColumn(Utils::SmallStringView columnName,
|
||||
ColumnType columnType,
|
||||
Contraint constraint = Contraint::NoConstraint,
|
||||
ForeignKey &&foreignKey = {});
|
||||
Constraints &&constraints = {});
|
||||
void setColumns(const SqliteColumns &columns);
|
||||
void setUseWithoutRowId(bool useWithoutRowId);
|
||||
void setUseIfNotExists(bool useIfNotExists);
|
||||
|
||||
@@ -27,56 +27,135 @@
|
||||
|
||||
#include "sqliteforeignkey.h"
|
||||
|
||||
#include <sqlitevalue.h>
|
||||
#include <utils/smallstring.h>
|
||||
#include <utils/variant.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace Sqlite {
|
||||
|
||||
class Unique
|
||||
{
|
||||
friend bool operator==(Unique, Unique) { return true; }
|
||||
};
|
||||
|
||||
class PrimaryKey
|
||||
{
|
||||
friend bool operator==(PrimaryKey, PrimaryKey) { return true; }
|
||||
};
|
||||
|
||||
class NotNull
|
||||
{
|
||||
friend bool operator==(NotNull, NotNull) { return true; }
|
||||
};
|
||||
|
||||
class DefaultValue
|
||||
{
|
||||
public:
|
||||
DefaultValue(long long value)
|
||||
: value(value)
|
||||
{}
|
||||
|
||||
DefaultValue(double value)
|
||||
: value(value)
|
||||
{}
|
||||
|
||||
DefaultValue(Utils::SmallStringView value)
|
||||
: value(value)
|
||||
{}
|
||||
|
||||
friend bool operator==(const DefaultValue &first, const DefaultValue &second)
|
||||
{
|
||||
return first.value == second.value;
|
||||
}
|
||||
|
||||
public:
|
||||
Sqlite::Value value;
|
||||
};
|
||||
|
||||
class DefaultExpression
|
||||
{
|
||||
public:
|
||||
DefaultExpression(Utils::SmallStringView expression)
|
||||
: expression(expression)
|
||||
{}
|
||||
|
||||
friend bool operator==(const DefaultExpression &first, const DefaultExpression &second)
|
||||
{
|
||||
return first.expression == second.expression;
|
||||
}
|
||||
|
||||
public:
|
||||
Utils::SmallString expression;
|
||||
};
|
||||
|
||||
class Collate
|
||||
{
|
||||
public:
|
||||
Collate(Utils::SmallStringView collation)
|
||||
: collation(collation)
|
||||
{}
|
||||
|
||||
friend bool operator==(const Collate &first, const Collate &second)
|
||||
{
|
||||
return first.collation == second.collation;
|
||||
}
|
||||
|
||||
public:
|
||||
Utils::SmallString collation;
|
||||
};
|
||||
|
||||
enum class GeneratedAlwaysStorage { Stored, Virtual };
|
||||
|
||||
class GeneratedAlways
|
||||
{
|
||||
public:
|
||||
GeneratedAlways(Utils::SmallStringView expression, GeneratedAlwaysStorage storage)
|
||||
: expression(expression)
|
||||
, storage(storage)
|
||||
{}
|
||||
|
||||
friend bool operator==(const GeneratedAlways &first, const GeneratedAlways &second)
|
||||
{
|
||||
return first.expression == second.expression;
|
||||
}
|
||||
|
||||
public:
|
||||
Utils::SmallString expression;
|
||||
GeneratedAlwaysStorage storage = {};
|
||||
};
|
||||
|
||||
using Constraint = Utils::variant<Unique,
|
||||
PrimaryKey,
|
||||
ForeignKey,
|
||||
NotNull,
|
||||
DefaultValue,
|
||||
DefaultExpression,
|
||||
Collate,
|
||||
GeneratedAlways>;
|
||||
using Constraints = std::vector<Constraint>;
|
||||
|
||||
class Column
|
||||
{
|
||||
public:
|
||||
Column() = default;
|
||||
|
||||
Column(Utils::SmallStringView tableName,
|
||||
Utils::SmallStringView name,
|
||||
ColumnType type = ColumnType::Numeric,
|
||||
Contraint constraint = Contraint::NoConstraint,
|
||||
ForeignKey &&foreignKey = {})
|
||||
: foreignKey(std::move(foreignKey))
|
||||
, name(name)
|
||||
, tableName(tableName)
|
||||
, type(type)
|
||||
, constraint(constraint)
|
||||
{}
|
||||
|
||||
Column(Utils::SmallStringView tableName,
|
||||
Utils::SmallStringView name,
|
||||
ColumnType type,
|
||||
Contraint constraint,
|
||||
Utils::SmallStringView foreignKeyTable,
|
||||
Utils::SmallStringView foreignKeycolumn,
|
||||
ForeignKeyAction foreignKeyUpdateAction,
|
||||
ForeignKeyAction foreignKeyDeleteAction,
|
||||
Enforment foreignKeyEnforcement)
|
||||
: foreignKey(foreignKeyTable,
|
||||
foreignKeycolumn,
|
||||
foreignKeyUpdateAction,
|
||||
foreignKeyDeleteAction,
|
||||
foreignKeyEnforcement)
|
||||
Constraints &&constraints = {})
|
||||
: constraints(std::move(constraints))
|
||||
, name(name)
|
||||
, tableName(tableName)
|
||||
, type(type)
|
||||
, constraint(constraint)
|
||||
|
||||
{}
|
||||
|
||||
void clear()
|
||||
{
|
||||
name.clear();
|
||||
type = ColumnType::Numeric;
|
||||
constraint = Contraint::NoConstraint;
|
||||
foreignKey = {};
|
||||
constraints = {};
|
||||
}
|
||||
|
||||
Utils::SmallString typeString() const
|
||||
@@ -100,16 +179,14 @@ public:
|
||||
friend bool operator==(const Column &first, const Column &second)
|
||||
{
|
||||
return first.name == second.name && first.type == second.type
|
||||
&& first.constraint
|
||||
== second.constraint /* && first.foreignKey == second.foreignKey*/;
|
||||
&& first.constraints == second.constraints && first.tableName == second.tableName;
|
||||
}
|
||||
|
||||
public:
|
||||
ForeignKey foreignKey;
|
||||
Constraints constraints;
|
||||
Utils::SmallString name;
|
||||
Utils::SmallString tableName;
|
||||
ColumnType type = ColumnType::Numeric;
|
||||
Contraint constraint = Contraint::NoConstraint;
|
||||
}; // namespace Sqlite
|
||||
|
||||
using SqliteColumns = std::vector<Column>;
|
||||
|
||||
@@ -48,7 +48,7 @@ enum class ColumnType : char
|
||||
None
|
||||
};
|
||||
|
||||
enum class Contraint : char { NoConstraint, PrimaryKey, Unique, ForeignKey };
|
||||
enum class ConstraintType : char { NoConstraint, PrimaryKey, Unique, ForeignKey };
|
||||
|
||||
enum class ForeignKeyAction : char { NoAction, Restrict, SetNull, SetDefault, Cascade };
|
||||
|
||||
|
||||
@@ -73,9 +73,9 @@ public:
|
||||
|
||||
Column &addColumn(Utils::SmallStringView name,
|
||||
ColumnType type = ColumnType::Numeric,
|
||||
Contraint constraint = Contraint::NoConstraint)
|
||||
Constraints &&constraints = {})
|
||||
{
|
||||
m_sqliteColumns.emplace_back(m_tableName, name, type, constraint);
|
||||
m_sqliteColumns.emplace_back(m_tableName, name, type, std::move(constraints));
|
||||
|
||||
return m_sqliteColumns.back();
|
||||
}
|
||||
@@ -85,17 +85,16 @@ public:
|
||||
ForeignKeyAction foreignKeyupdateAction = {},
|
||||
ForeignKeyAction foreignKeyDeleteAction = {},
|
||||
Enforment foreignKeyEnforcement = {},
|
||||
Constraints &&constraints = {},
|
||||
ColumnType type = ColumnType::Integer)
|
||||
{
|
||||
m_sqliteColumns.emplace_back(m_tableName,
|
||||
name,
|
||||
type,
|
||||
Contraint::ForeignKey,
|
||||
referencedTable.name(),
|
||||
"",
|
||||
foreignKeyupdateAction,
|
||||
foreignKeyDeleteAction,
|
||||
foreignKeyEnforcement);
|
||||
constraints.emplace_back(ForeignKey{referencedTable.name(),
|
||||
"",
|
||||
foreignKeyupdateAction,
|
||||
foreignKeyDeleteAction,
|
||||
foreignKeyEnforcement});
|
||||
|
||||
m_sqliteColumns.emplace_back(m_tableName, name, type, std::move(constraints));
|
||||
|
||||
return m_sqliteColumns.back();
|
||||
}
|
||||
@@ -104,20 +103,22 @@ public:
|
||||
const Column &referencedColumn,
|
||||
ForeignKeyAction foreignKeyupdateAction = {},
|
||||
ForeignKeyAction foreignKeyDeleteAction = {},
|
||||
Enforment foreignKeyEnforcement = {})
|
||||
Enforment foreignKeyEnforcement = {},
|
||||
Constraints &&constraints = {})
|
||||
{
|
||||
if (referencedColumn.constraint != Contraint::Unique)
|
||||
if (!constainsUniqueIndex(referencedColumn.constraints))
|
||||
throw ForeignKeyColumnIsNotUnique("Foreign column key must be unique!");
|
||||
|
||||
constraints.emplace_back(ForeignKey{referencedColumn.tableName,
|
||||
referencedColumn.name,
|
||||
foreignKeyupdateAction,
|
||||
foreignKeyDeleteAction,
|
||||
foreignKeyEnforcement});
|
||||
|
||||
m_sqliteColumns.emplace_back(m_tableName,
|
||||
name,
|
||||
referencedColumn.type,
|
||||
Contraint::ForeignKey,
|
||||
referencedColumn.tableName,
|
||||
referencedColumn.name,
|
||||
foreignKeyupdateAction,
|
||||
foreignKeyDeleteAction,
|
||||
foreignKeyEnforcement);
|
||||
std::move(constraints));
|
||||
|
||||
return m_sqliteColumns.back();
|
||||
}
|
||||
@@ -181,6 +182,16 @@ public:
|
||||
&& first.m_sqliteColumns == second.m_sqliteColumns;
|
||||
}
|
||||
|
||||
static bool constainsUniqueIndex(const Constraints &constraints)
|
||||
{
|
||||
return std::find_if(constraints.begin(),
|
||||
constraints.end(),
|
||||
[](const Constraint &constraint) {
|
||||
return Utils::holds_alternative<Unique>(constraint);
|
||||
})
|
||||
!= constraints.end();
|
||||
}
|
||||
|
||||
private:
|
||||
Utils::SmallStringVector sqliteColumnNames(const SqliteColumnConstReferences &columns)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user