forked from qt-creator/qt-creator
Sqlite: Add foreign key support
It is still only support references in columns but so far it is enough. Change-Id: Iebb4866cf738d651270e54357b5e4a2837f05417 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -39,13 +39,18 @@ void CreateTableSqlStatementBuilder::setTableName(Utils::SmallString &&tableName
|
||||
this->m_tableName = std::move(tableName);
|
||||
}
|
||||
|
||||
void CreateTableSqlStatementBuilder::addColumn(Utils::SmallString &&columnName,
|
||||
void CreateTableSqlStatementBuilder::addColumn(Utils::SmallStringView columnName,
|
||||
ColumnType columnType,
|
||||
Contraint constraint)
|
||||
Contraint constraint,
|
||||
ForeignKey &&foreignKey)
|
||||
{
|
||||
m_sqlStatementBuilder.clear();
|
||||
|
||||
m_columns.emplace_back(std::move(columnName), columnType, constraint);
|
||||
m_columns.emplace_back(Utils::SmallStringView{},
|
||||
columnName,
|
||||
columnType,
|
||||
constraint,
|
||||
std::move(foreignKey));
|
||||
}
|
||||
|
||||
void CreateTableSqlStatementBuilder::setColumns(const SqliteColumns &columns)
|
||||
@@ -97,17 +102,70 @@ bool CreateTableSqlStatementBuilder::isValid() const
|
||||
return m_tableName.hasContent() && !m_columns.empty();
|
||||
}
|
||||
|
||||
namespace {
|
||||
Utils::SmallStringView actionToText(ForeignKeyAction action)
|
||||
{
|
||||
switch (action) {
|
||||
case ForeignKeyAction::NoAction:
|
||||
return "NO ACTION";
|
||||
case ForeignKeyAction::Restrict:
|
||||
return "RESTRICT";
|
||||
case ForeignKeyAction::SetNull:
|
||||
return "SET NULL";
|
||||
case ForeignKeyAction::SetDefault:
|
||||
return "SET DEFAULT";
|
||||
case ForeignKeyAction::Cascade:
|
||||
return "CASCADE";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void appendForeignKey(Utils::SmallString &columnDefinitionString, 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");
|
||||
}
|
||||
} // namespace
|
||||
void CreateTableSqlStatementBuilder::bindColumnDefinitions() const
|
||||
{
|
||||
Utils::SmallStringVector columnDefinitionStrings;
|
||||
columnDefinitionStrings.reserve(m_columns.size());
|
||||
|
||||
for (const Column &columns : m_columns) {
|
||||
Utils::SmallString columnDefinitionString = {columns.name(), " ", columns.typeString()};
|
||||
for (const Column &column : m_columns) {
|
||||
Utils::SmallString columnDefinitionString = {column.name, " ", column.typeString()};
|
||||
|
||||
switch (columns.constraint()) {
|
||||
case Contraint::PrimaryKey: columnDefinitionString.append(" PRIMARY KEY"); break;
|
||||
case Contraint::Unique: columnDefinitionString.append(" UNIQUE"); break;
|
||||
case Contraint::NoConstraint: break;
|
||||
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;
|
||||
}
|
||||
|
||||
columnDefinitionStrings.push_back(columnDefinitionString);
|
||||
|
||||
@@ -36,9 +36,10 @@ public:
|
||||
CreateTableSqlStatementBuilder();
|
||||
|
||||
void setTableName(Utils::SmallString &&tableName);
|
||||
void addColumn(Utils::SmallString &&columnName,
|
||||
void addColumn(Utils::SmallStringView columnName,
|
||||
ColumnType columnType,
|
||||
Contraint constraint = Contraint::NoConstraint);
|
||||
Contraint constraint = Contraint::NoConstraint,
|
||||
ForeignKey &&foreignKey = {});
|
||||
void setColumns(const SqliteColumns &columns);
|
||||
void setUseWithoutRowId(bool useWithoutRowId);
|
||||
void setUseIfNotExists(bool useIfNotExists);
|
||||
|
||||
@@ -28,6 +28,7 @@ HEADERS += \
|
||||
$$PWD/sqlitedatabasebackend.h \
|
||||
$$PWD/sqlitedatabaseinterface.h \
|
||||
$$PWD/sqliteexception.h \
|
||||
$$PWD/sqliteforeignkey.h \
|
||||
$$PWD/sqliteglobal.h \
|
||||
$$PWD/sqlitereadstatement.h \
|
||||
$$PWD/sqlitereadwritestatement.h \
|
||||
@@ -45,7 +46,8 @@ HEADERS += \
|
||||
$$PWD/sqlitebasestatement.h
|
||||
|
||||
DEFINES += SQLITE_THREADSAFE=2 SQLITE_ENABLE_FTS4 SQLITE_ENABLE_FTS3_PARENTHESIS \
|
||||
SQLITE_ENABLE_UNLOCK_NOTIFY SQLITE_ENABLE_COLUMN_METADATA SQLITE_ENABLE_JSON1
|
||||
SQLITE_ENABLE_UNLOCK_NOTIFY SQLITE_ENABLE_COLUMN_METADATA SQLITE_ENABLE_JSON1 \
|
||||
SQLITE_DEFAULT_FOREIGN_KEYS=1
|
||||
|
||||
OTHER_FILES += README.md
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sqliteglobal.h"
|
||||
#include "sqliteforeignkey.h"
|
||||
|
||||
#include <utils/smallstring.h>
|
||||
|
||||
@@ -38,59 +38,60 @@ class Column
|
||||
public:
|
||||
Column() = default;
|
||||
|
||||
Column(Utils::SmallString &&name,
|
||||
Column(Utils::SmallStringView tableName,
|
||||
Utils::SmallStringView name,
|
||||
ColumnType type = ColumnType::Numeric,
|
||||
Contraint constraint = Contraint::NoConstraint)
|
||||
: m_name(std::move(name)),
|
||||
m_type(type),
|
||||
m_constraint(constraint)
|
||||
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)
|
||||
, name(name)
|
||||
, tableName(tableName)
|
||||
, type(type)
|
||||
, constraint(constraint)
|
||||
|
||||
{}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_name.clear();
|
||||
m_type = ColumnType::Numeric;
|
||||
m_constraint = Contraint::NoConstraint;
|
||||
}
|
||||
|
||||
void setName(Utils::SmallString &&newName)
|
||||
{
|
||||
m_name = newName;
|
||||
}
|
||||
|
||||
const Utils::SmallString &name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void setType(ColumnType newType)
|
||||
{
|
||||
m_type = newType;
|
||||
}
|
||||
|
||||
ColumnType type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void setContraint(Contraint constraint)
|
||||
{
|
||||
m_constraint = constraint;
|
||||
}
|
||||
|
||||
Contraint constraint() const
|
||||
{
|
||||
return m_constraint;
|
||||
name.clear();
|
||||
type = ColumnType::Numeric;
|
||||
constraint = Contraint::NoConstraint;
|
||||
foreignKey = {};
|
||||
}
|
||||
|
||||
Utils::SmallString typeString() const
|
||||
{
|
||||
switch (m_type) {
|
||||
case ColumnType::None: return {};
|
||||
case ColumnType::Numeric: return "NUMERIC";
|
||||
case ColumnType::Integer: return "INTEGER";
|
||||
case ColumnType::Real: return "REAL";
|
||||
case ColumnType::Text: return "TEXT";
|
||||
switch (type) {
|
||||
case ColumnType::None:
|
||||
return {};
|
||||
case ColumnType::Numeric:
|
||||
return "NUMERIC";
|
||||
case ColumnType::Integer:
|
||||
return "INTEGER";
|
||||
case ColumnType::Real:
|
||||
return "REAL";
|
||||
case ColumnType::Text:
|
||||
return "TEXT";
|
||||
}
|
||||
|
||||
Q_UNREACHABLE();
|
||||
@@ -98,16 +99,18 @@ public:
|
||||
|
||||
friend bool operator==(const Column &first, const Column &second)
|
||||
{
|
||||
return first.m_name == second.m_name
|
||||
&& first.m_type == second.m_type
|
||||
&& first.m_constraint == second.m_constraint;
|
||||
return first.name == second.name && first.type == second.type
|
||||
&& first.constraint
|
||||
== second.constraint /* && first.foreignKey == second.foreignKey*/;
|
||||
}
|
||||
|
||||
private:
|
||||
Utils::SmallString m_name;
|
||||
ColumnType m_type = ColumnType::Numeric;
|
||||
Contraint m_constraint = Contraint::NoConstraint;
|
||||
};
|
||||
public:
|
||||
ForeignKey foreignKey;
|
||||
Utils::SmallString name;
|
||||
Utils::SmallString tableName;
|
||||
ColumnType type = ColumnType::Numeric;
|
||||
Contraint constraint = Contraint::NoConstraint;
|
||||
}; // namespace Sqlite
|
||||
|
||||
using SqliteColumns = std::vector<Column>;
|
||||
using SqliteColumnConstReference = std::reference_wrapper<const Column>;
|
||||
|
||||
@@ -272,4 +272,12 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
class ForeignKeyColumnIsNotUnique : public Exception
|
||||
{
|
||||
public:
|
||||
ForeignKeyColumnIsNotUnique(const char *whatErrorHasHappen)
|
||||
: Exception(whatErrorHasHappen)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace Sqlite
|
||||
|
||||
65
src/libs/sqlite/sqliteforeignkey.h
Normal file
65
src/libs/sqlite/sqliteforeignkey.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sqliteglobal.h"
|
||||
|
||||
#include <utils/smallstring.h>
|
||||
|
||||
namespace Sqlite {
|
||||
|
||||
class ForeignKey
|
||||
{
|
||||
public:
|
||||
ForeignKey() = default;
|
||||
ForeignKey(Utils::SmallStringView table,
|
||||
Utils::SmallStringView column,
|
||||
ForeignKeyAction updateAction = {},
|
||||
ForeignKeyAction deleteAction = {},
|
||||
Enforment enforcement = {})
|
||||
: table(table)
|
||||
, column(column)
|
||||
, updateAction(updateAction)
|
||||
, deleteAction(deleteAction)
|
||||
, enforcement(enforcement)
|
||||
{}
|
||||
|
||||
friend bool operator==(const ForeignKey &first, const ForeignKey &second)
|
||||
{
|
||||
return first.table == second.table && first.column == second.column
|
||||
&& first.updateAction == second.updateAction
|
||||
&& first.deleteAction == second.deleteAction;
|
||||
}
|
||||
|
||||
public:
|
||||
Utils::SmallString table;
|
||||
Utils::SmallString column;
|
||||
ForeignKeyAction updateAction = {};
|
||||
ForeignKeyAction deleteAction = {};
|
||||
Enforment enforcement = {};
|
||||
};
|
||||
|
||||
} // namespace Sqlite
|
||||
@@ -48,17 +48,13 @@ enum class ColumnType : char
|
||||
None
|
||||
};
|
||||
|
||||
enum class Contraint : char
|
||||
{
|
||||
NoConstraint,
|
||||
PrimaryKey,
|
||||
Unique
|
||||
};
|
||||
enum class Contraint : char { NoConstraint, PrimaryKey, Unique, ForeignKey };
|
||||
|
||||
enum class ColumnConstraint : char
|
||||
{
|
||||
PrimaryKey
|
||||
};
|
||||
enum class ForeignKeyAction : char { NoAction, Restrict, SetNull, SetDefault, Cascade };
|
||||
|
||||
enum class Enforment : char { Immediate, Deferred };
|
||||
|
||||
enum class ColumnConstraint : char { PrimaryKey };
|
||||
|
||||
enum class JournalMode : char
|
||||
{
|
||||
|
||||
@@ -44,10 +44,7 @@ public:
|
||||
m_sqliteIndices.reserve(reserve);
|
||||
}
|
||||
|
||||
void setName(Utils::SmallString &&name)
|
||||
{
|
||||
m_tableName = std::move(name);
|
||||
}
|
||||
void setName(Utils::SmallStringView name) { m_tableName = name; }
|
||||
|
||||
Utils::SmallStringView name() const
|
||||
{
|
||||
@@ -74,11 +71,53 @@ public:
|
||||
m_useTemporaryTable = useTemporaryTable;
|
||||
}
|
||||
|
||||
Column &addColumn(Utils::SmallString &&name,
|
||||
ColumnType type = ColumnType::Numeric,
|
||||
Contraint constraint = Contraint::NoConstraint)
|
||||
Column &addColumn(Utils::SmallStringView name,
|
||||
ColumnType type = ColumnType::Numeric,
|
||||
Contraint constraint = Contraint::NoConstraint)
|
||||
{
|
||||
m_sqliteColumns.emplace_back(std::move(name), type, constraint);
|
||||
m_sqliteColumns.emplace_back(m_tableName, name, type, constraint);
|
||||
|
||||
return m_sqliteColumns.back();
|
||||
}
|
||||
|
||||
Column &addForeignKeyColumn(Utils::SmallStringView name,
|
||||
const Table &referencedTable,
|
||||
ForeignKeyAction foreignKeyupdateAction = {},
|
||||
ForeignKeyAction foreignKeyDeleteAction = {},
|
||||
Enforment foreignKeyEnforcement = {},
|
||||
ColumnType type = ColumnType::Integer)
|
||||
{
|
||||
m_sqliteColumns.emplace_back(m_tableName,
|
||||
name,
|
||||
type,
|
||||
Contraint::ForeignKey,
|
||||
referencedTable.name(),
|
||||
"",
|
||||
foreignKeyupdateAction,
|
||||
foreignKeyDeleteAction,
|
||||
foreignKeyEnforcement);
|
||||
|
||||
return m_sqliteColumns.back();
|
||||
}
|
||||
|
||||
Column &addForeignKeyColumn(Utils::SmallStringView name,
|
||||
const Column &referencedColumn,
|
||||
ForeignKeyAction foreignKeyupdateAction = {},
|
||||
ForeignKeyAction foreignKeyDeleteAction = {},
|
||||
Enforment foreignKeyEnforcement = {})
|
||||
{
|
||||
if (referencedColumn.constraint != Contraint::Unique)
|
||||
throw ForeignKeyColumnIsNotUnique("Foreign column key must be unique!");
|
||||
|
||||
m_sqliteColumns.emplace_back(m_tableName,
|
||||
name,
|
||||
referencedColumn.type,
|
||||
Contraint::ForeignKey,
|
||||
referencedColumn.tableName,
|
||||
referencedColumn.name,
|
||||
foreignKeyupdateAction,
|
||||
foreignKeyDeleteAction,
|
||||
foreignKeyEnforcement);
|
||||
|
||||
return m_sqliteColumns.back();
|
||||
}
|
||||
@@ -148,7 +187,7 @@ private:
|
||||
Utils::SmallStringVector columnNames;
|
||||
|
||||
for (const Column &column : columns)
|
||||
columnNames.push_back(column.name());
|
||||
columnNames.push_back(column.name);
|
||||
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user