mirror of
https://github.com/strapi/strapi.git
synced 2025-09-22 14:59:07 +00:00
Merge pull request #11557 from strapi/v4/database-improvements
Support schema option and add forceMigration options
This commit is contained in:
commit
3b11037883
@ -123,10 +123,6 @@ class MysqlSchemaInspector {
|
||||
return schema;
|
||||
}
|
||||
|
||||
getDatabaseSchema() {
|
||||
return this.db.connection.client.connectionSettings.schema || 'public';
|
||||
}
|
||||
|
||||
async getTables() {
|
||||
const [rows] = await this.db.connection.raw(SQL_QUERIES.TABLE_LIST);
|
||||
|
||||
|
@ -139,7 +139,7 @@ class PostgresqlSchemaInspector {
|
||||
}
|
||||
|
||||
getDatabaseSchema() {
|
||||
return this.db.connection.client.connectionSettings.schema || 'public';
|
||||
return this.db.connection.getSchemaName() || 'public';
|
||||
}
|
||||
|
||||
async getTables() {
|
||||
|
@ -13,16 +13,32 @@ const errors = require('./errors');
|
||||
// TODO: move back into strapi
|
||||
const { transformContentTypes } = require('./utils/content-types');
|
||||
|
||||
const createConnection = config => {
|
||||
const knexInstance = knex(config);
|
||||
|
||||
return Object.assign(knexInstance, {
|
||||
getSchemaName() {
|
||||
return this.client.connectionSettings.schema;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
class Database {
|
||||
constructor(config) {
|
||||
this.metadata = createMetadata(config.models);
|
||||
|
||||
this.config = config;
|
||||
this.config = {
|
||||
connection: {},
|
||||
settings: {
|
||||
forceMigration: true,
|
||||
},
|
||||
...config,
|
||||
};
|
||||
|
||||
this.dialect = getDialect(this);
|
||||
this.dialect.configure();
|
||||
|
||||
this.connection = knex(this.config.connection);
|
||||
this.connection = createConnection(this.config.connection);
|
||||
|
||||
this.dialect.initialize();
|
||||
|
||||
@ -42,6 +58,17 @@ class Database {
|
||||
return this.entityManager.getRepository(uid);
|
||||
}
|
||||
|
||||
getConnection(tableName) {
|
||||
const schema = this.connection.getSchemaName();
|
||||
const connection = tableName ? this.connection(tableName) : this.connection;
|
||||
return schema ? connection.withSchema(schema) : connection;
|
||||
}
|
||||
|
||||
getSchemaConnection(trx = this.connection) {
|
||||
const schema = this.connection.getSchemaName();
|
||||
return schema ? trx.schema.withSchema(schema) : trx.schema;
|
||||
}
|
||||
|
||||
queryBuilder(uid) {
|
||||
return this.entityManager.createQueryBuilder(uid);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ const createUmzugProvider = db => {
|
||||
|
||||
fse.ensureDirSync(migrationDir);
|
||||
|
||||
const wrapFn = fn => db => db.connection.transaction(trx => Promise.resolve(fn(trx)));
|
||||
const wrapFn = fn => db => db.getConnection().transaction(trx => Promise.resolve(fn(trx)));
|
||||
const storage = createStorage({ db, tableName: 'strapi_migrations' });
|
||||
|
||||
return new Umzug({
|
||||
|
@ -1,13 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const createStorage = (opts = {}) => {
|
||||
const tableName = opts.tableName || 'strapi_migrations';
|
||||
const knex = opts.db.connection;
|
||||
const { db, tableName = 'strapi_migrations' } = opts;
|
||||
|
||||
const hasMigrationTable = () => knex.schema.hasTable(tableName);
|
||||
const hasMigrationTable = () => db.getSchemaConnection().hasTable(tableName);
|
||||
|
||||
const createMigrationTable = () => {
|
||||
return knex.schema.createTable(tableName, table => {
|
||||
return db.getSchemaConnection().createTable(tableName, table => {
|
||||
table.increments('id');
|
||||
table.string('name');
|
||||
table.datetime('time', { useTz: false });
|
||||
@ -16,7 +15,8 @@ const createStorage = (opts = {}) => {
|
||||
|
||||
return {
|
||||
async logMigration(migrationName) {
|
||||
await knex
|
||||
await db
|
||||
.getConnection()
|
||||
.insert({
|
||||
name: migrationName,
|
||||
time: new Date(),
|
||||
@ -25,7 +25,8 @@ const createStorage = (opts = {}) => {
|
||||
},
|
||||
|
||||
async unlogMigration(migrationName) {
|
||||
await knex(tableName)
|
||||
await db
|
||||
.getConnection(tableName)
|
||||
.del()
|
||||
.where({ name: migrationName });
|
||||
},
|
||||
@ -36,7 +37,8 @@ const createStorage = (opts = {}) => {
|
||||
return [];
|
||||
}
|
||||
|
||||
const logs = await knex
|
||||
const logs = await db
|
||||
.getConnection(tableName)
|
||||
.select()
|
||||
.from(tableName)
|
||||
.orderBy('time');
|
||||
|
@ -67,7 +67,7 @@ const applyJoin = (qb, join) => {
|
||||
orderBy,
|
||||
} = join;
|
||||
|
||||
qb[method]({ [alias]: referencedTable }, inner => {
|
||||
qb[method](`${referencedTable} as ${alias}`, inner => {
|
||||
inner.on(`${rootTable}.${rootColumn}`, `${alias}.${referencedColumn}`);
|
||||
|
||||
if (on) {
|
||||
|
@ -205,10 +205,13 @@ const createQueryBuilder = (uid, db) => {
|
||||
this.select('id');
|
||||
const subQB = this.getKnexQuery();
|
||||
|
||||
const nestedSubQuery = db.connection.select('id').from(subQB.as('subQuery'));
|
||||
const nestedSubQuery = db
|
||||
.getConnection()
|
||||
.select('id')
|
||||
.from(subQB.as('subQuery'));
|
||||
|
||||
return db
|
||||
.connection(tableName)
|
||||
.getConnection(tableName)
|
||||
[state.type]()
|
||||
.whereIn('id', nestedSubQuery);
|
||||
},
|
||||
@ -257,9 +260,9 @@ const createQueryBuilder = (uid, db) => {
|
||||
this.select('*');
|
||||
}
|
||||
|
||||
const aliasedTableName = this.mustUseAlias() ? { [this.alias]: tableName } : tableName;
|
||||
const aliasedTableName = this.mustUseAlias() ? `${tableName} as ${this.alias}` : tableName;
|
||||
|
||||
const qb = db.connection(aliasedTableName);
|
||||
const qb = db.getConnection(aliasedTableName);
|
||||
|
||||
if (this.shouldUseSubQuery()) {
|
||||
return this.runSubQuery();
|
||||
|
@ -11,8 +11,8 @@ module.exports = db => {
|
||||
* Returns a knex schema builder instance
|
||||
* @param {string} table - table name
|
||||
*/
|
||||
getSchemaBuilder(table, trx = db.connection) {
|
||||
return table.schema ? trx.schema.withSchema(table.schema) : trx.schema;
|
||||
getSchemaBuilder(trx) {
|
||||
return db.getSchemaConnection(trx);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -20,10 +20,7 @@ module.exports = db => {
|
||||
* @param {Schema} schema - database schema
|
||||
*/
|
||||
async createSchema(schema) {
|
||||
// TODO: ensure database exists;
|
||||
|
||||
await db.connection.transaction(async trx => {
|
||||
// create tables without FKs first do avoid ordering issues
|
||||
await this.createTables(schema.tables, trx);
|
||||
});
|
||||
},
|
||||
@ -36,14 +33,14 @@ module.exports = db => {
|
||||
async createTables(tables, trx) {
|
||||
for (const table of tables) {
|
||||
debug(`Creating table: ${table.name}`);
|
||||
const schemaBuilder = this.getSchemaBuilder(table, trx);
|
||||
const schemaBuilder = this.getSchemaBuilder(trx);
|
||||
await helpers.createTable(schemaBuilder, table);
|
||||
}
|
||||
|
||||
// create FKs once all the tables exist
|
||||
for (const table of tables) {
|
||||
debug(`Creating table foreign keys: ${table.name}`);
|
||||
const schemaBuilder = this.getSchemaBuilder(table, trx);
|
||||
const schemaBuilder = this.getSchemaBuilder(trx);
|
||||
await helpers.createTableForeignKeys(schemaBuilder, table);
|
||||
}
|
||||
},
|
||||
@ -61,7 +58,7 @@ module.exports = db => {
|
||||
|
||||
await db.connection.transaction(async trx => {
|
||||
for (const table of schema.tables.reverse()) {
|
||||
const schemaBuilder = this.getSchemaBuilder(table, trx);
|
||||
const schemaBuilder = this.getSchemaBuilder(trx);
|
||||
await helpers.dropTable(schemaBuilder, table);
|
||||
}
|
||||
});
|
||||
@ -73,29 +70,33 @@ module.exports = db => {
|
||||
*/
|
||||
// TODO: implement force option to disable removal in DB
|
||||
async updateSchema(schemaDiff) {
|
||||
const { forceMigration } = db.config.settings;
|
||||
|
||||
await db.dialect.startSchemaUpdate();
|
||||
await db.connection.transaction(async trx => {
|
||||
await this.createTables(schemaDiff.tables.added, trx);
|
||||
|
||||
// drop all delete table foreign keys then delete the tables
|
||||
for (const table of schemaDiff.tables.removed) {
|
||||
debug(`Removing table foreign keys: ${table.name}`);
|
||||
if (forceMigration) {
|
||||
// drop all delete table foreign keys then delete the tables
|
||||
for (const table of schemaDiff.tables.removed) {
|
||||
debug(`Removing table foreign keys: ${table.name}`);
|
||||
|
||||
const schemaBuilder = this.getSchemaBuilder(table, trx);
|
||||
await helpers.dropTableForeignKeys(schemaBuilder, table);
|
||||
}
|
||||
const schemaBuilder = this.getSchemaBuilder(trx);
|
||||
await helpers.dropTableForeignKeys(schemaBuilder, table);
|
||||
}
|
||||
|
||||
for (const table of schemaDiff.tables.removed) {
|
||||
debug(`Removing table: ${table.name}`);
|
||||
for (const table of schemaDiff.tables.removed) {
|
||||
debug(`Removing table: ${table.name}`);
|
||||
|
||||
const schemaBuilder = this.getSchemaBuilder(table, trx);
|
||||
await helpers.dropTable(schemaBuilder, table);
|
||||
const schemaBuilder = this.getSchemaBuilder(trx);
|
||||
await helpers.dropTable(schemaBuilder, table);
|
||||
}
|
||||
}
|
||||
|
||||
for (const table of schemaDiff.tables.updated) {
|
||||
debug(`Updating table: ${table.name}`);
|
||||
// alter table
|
||||
const schemaBuilder = this.getSchemaBuilder(table, trx);
|
||||
const schemaBuilder = this.getSchemaBuilder(trx);
|
||||
|
||||
await helpers.alterTable(schemaBuilder, table);
|
||||
}
|
||||
@ -118,7 +119,11 @@ const createHelpers = db => {
|
||||
const constraint = tableBuilder
|
||||
.foreign(columns, name)
|
||||
.references(referencedColumns)
|
||||
.inTable(referencedTable);
|
||||
.inTable(
|
||||
db.connection.getSchemaName()
|
||||
? `${db.connection.getSchemaName()}.${referencedTable}`
|
||||
: referencedTable
|
||||
);
|
||||
|
||||
if (onDelete) {
|
||||
constraint.onDelete(onDelete);
|
||||
@ -167,6 +172,10 @@ const createHelpers = db => {
|
||||
* @param {Index} index
|
||||
*/
|
||||
const dropIndex = (tableBuilder, index) => {
|
||||
if (!db.config.settings.forceMigration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { type, columns, name } = index;
|
||||
|
||||
switch (type) {
|
||||
@ -221,7 +230,11 @@ const createHelpers = db => {
|
||||
* @param {Column} column
|
||||
*/
|
||||
const dropColumn = (tableBuilder, column) => {
|
||||
tableBuilder.dropColumn(column.name);
|
||||
if (!db.config.settings.forceMigration) {
|
||||
return;
|
||||
}
|
||||
|
||||
return tableBuilder.dropColumn(column.name);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -316,7 +329,13 @@ const createHelpers = db => {
|
||||
* @param {Knex.SchemaBuilder} schemaBuilder
|
||||
* @param {Table} table
|
||||
*/
|
||||
const dropTable = (schemaBuilder, table) => schemaBuilder.dropTableIfExists(table.name);
|
||||
const dropTable = (schemaBuilder, table) => {
|
||||
if (!db.config.settings.forceMigration) {
|
||||
return;
|
||||
}
|
||||
|
||||
return schemaBuilder.dropTableIfExists(table.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a table foreign keys constraints
|
||||
@ -336,6 +355,10 @@ const createHelpers = db => {
|
||||
* @param {Table} table
|
||||
*/
|
||||
const dropTableForeignKeys = async (schemaBuilder, table) => {
|
||||
if (!db.config.settings.forceMigration) {
|
||||
return;
|
||||
}
|
||||
|
||||
// foreign keys
|
||||
await schemaBuilder.table(table.name, tableBuilder => {
|
||||
(table.foreignKeys || []).forEach(foreignKey => dropForeignKey(tableBuilder, foreignKey));
|
||||
|
@ -5,10 +5,10 @@ const crypto = require('crypto');
|
||||
const TABLE_NAME = 'strapi_database_schema';
|
||||
|
||||
module.exports = db => {
|
||||
const hasSchemaTable = () => db.connection.schema.hasTable(TABLE_NAME);
|
||||
const hasSchemaTable = () => db.getSchemaConnection().hasTable(TABLE_NAME);
|
||||
|
||||
const createSchemaTable = () => {
|
||||
return db.connection.schema.createTable(TABLE_NAME, t => {
|
||||
return db.getSchemaConnection().createTable(TABLE_NAME, t => {
|
||||
t.increments('id');
|
||||
t.json('schema');
|
||||
t.datetime('time', { useTz: false });
|
||||
@ -26,7 +26,8 @@ module.exports = db => {
|
||||
async read() {
|
||||
await checkTableExists();
|
||||
|
||||
const res = await db.connection
|
||||
const res = await db
|
||||
.getConnection()
|
||||
.select('*')
|
||||
.from(TABLE_NAME)
|
||||
.orderBy('time', 'DESC')
|
||||
@ -55,21 +56,24 @@ module.exports = db => {
|
||||
await checkTableExists();
|
||||
|
||||
// NOTE: we can remove this to add history
|
||||
await db.connection(TABLE_NAME).delete();
|
||||
await db.getConnection(TABLE_NAME).delete();
|
||||
|
||||
const time = new Date();
|
||||
|
||||
await db.connection(TABLE_NAME).insert({
|
||||
schema: JSON.stringify(schema),
|
||||
hash: this.hashSchema(schema),
|
||||
time,
|
||||
});
|
||||
await db
|
||||
.getConnection()
|
||||
.insert({
|
||||
schema: JSON.stringify(schema),
|
||||
hash: this.hashSchema(schema),
|
||||
time,
|
||||
})
|
||||
.into(TABLE_NAME);
|
||||
},
|
||||
|
||||
async clear() {
|
||||
await checkTableExists();
|
||||
|
||||
await db.connection(TABLE_NAME).truncate();
|
||||
await db.getConnection(TABLE_NAME).truncate();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -366,7 +366,7 @@ class Strapi {
|
||||
this.telemetry.bootstrap();
|
||||
|
||||
let oldContentTypes;
|
||||
if (await this.db.connection.schema.hasTable(coreStoreModel.collectionName)) {
|
||||
if (await this.db.getSchemaConnection().hasTable(coreStoreModel.collectionName)) {
|
||||
oldContentTypes = await this.store.get({
|
||||
type: 'strapi',
|
||||
name: 'content_types',
|
||||
|
Loading…
x
Reference in New Issue
Block a user