Don't initialize database on file require (#6003)

This commit is contained in:
Rijk van Zanten
2021-06-02 11:41:42 -04:00
committed by GitHub
parent 02fc696c53
commit 77e00b7db4
39 changed files with 222 additions and 146 deletions

View File

@@ -1,76 +1,98 @@
import SchemaInspector from '@directus/schema';
import dotenv from 'dotenv';
import { knex, Knex } from 'knex';
import path from 'path';
import { performance } from 'perf_hooks';
import env from '../env';
import logger from '../logger';
import { getConfigFromEnv } from '../utils/get-config-from-env';
import { validateEnv } from '../utils/validate-env';
dotenv.config({ path: path.resolve(__dirname, '../../', '.env') });
let database: Knex | null = null;
let inspector: ReturnType<typeof SchemaInspector> | null = null;
const connectionConfig: Record<string, any> = getConfigFromEnv('DB_', [
'DB_CLIENT',
'DB_SEARCH_PATH',
'DB_CONNECTION_STRING',
'DB_POOL',
]);
const poolConfig = getConfigFromEnv('DB_POOL');
const requiredKeys = ['DB_CLIENT'];
if (env.DB_CLIENT && env.DB_CLIENT === 'sqlite3') {
requiredKeys.push('DB_FILENAME');
} else if (env.DB_CLIENT && env.DB_CLIENT === 'oracledb') {
requiredKeys.push('DB_USER', 'DB_PASSWORD', 'DB_CONNECT_STRING');
} else {
if (env.DB_CLIENT === 'pg') {
if (!env.DB_CONNECTION_STRING) {
requiredKeys.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER');
}
} else {
requiredKeys.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
export default function getDatabase() {
if (database) {
return database;
}
}
validateEnv(requiredKeys);
const connectionConfig: Record<string, any> = getConfigFromEnv('DB_', [
'DB_CLIENT',
'DB_SEARCH_PATH',
'DB_CONNECTION_STRING',
'DB_POOL',
]);
const knexConfig: Knex.Config = {
client: env.DB_CLIENT,
searchPath: env.DB_SEARCH_PATH,
connection: env.DB_CONNECTION_STRING || connectionConfig,
log: {
warn: (msg) => logger.warn(msg),
error: (msg) => logger.error(msg),
deprecate: (msg) => logger.info(msg),
debug: (msg) => logger.debug(msg),
},
pool: poolConfig,
};
const poolConfig = getConfigFromEnv('DB_POOL');
if (env.DB_CLIENT === 'sqlite3') {
knexConfig.useNullAsDefault = true;
poolConfig.afterCreate = (conn: any, cb: any) => {
conn.run('PRAGMA foreign_keys = ON', cb);
const requiredEnvVars = ['DB_CLIENT'];
if (env.DB_CLIENT && env.DB_CLIENT === 'sqlite3') {
requiredEnvVars.push('DB_FILENAME');
} else if (env.DB_CLIENT && env.DB_CLIENT === 'oracledb') {
requiredEnvVars.push('DB_USER', 'DB_PASSWORD', 'DB_CONNECT_STRING');
} else {
if (env.DB_CLIENT === 'pg') {
if (!env.DB_CONNECTION_STRING) {
requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER');
}
} else {
requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
}
}
validateEnv(requiredEnvVars);
const knexConfig: Knex.Config = {
client: env.DB_CLIENT,
searchPath: env.DB_SEARCH_PATH,
connection: env.DB_CONNECTION_STRING || connectionConfig,
log: {
warn: (msg) => logger.warn(msg),
error: (msg) => logger.error(msg),
deprecate: (msg) => logger.info(msg),
debug: (msg) => logger.debug(msg),
},
pool: poolConfig,
};
if (env.DB_CLIENT === 'sqlite3') {
knexConfig.useNullAsDefault = true;
poolConfig.afterCreate = (conn: any, cb: any) => {
conn.run('PRAGMA foreign_keys = ON', cb);
};
}
database = knex(knexConfig);
const times: Record<string, number> = {};
database
.on('query', (queryInfo) => {
times[queryInfo.__knexUid] = performance.now();
})
.on('query-response', (response, queryInfo) => {
const delta = performance.now() - times[queryInfo.__knexUid];
logger.trace(`[${delta.toFixed(3)}ms] ${queryInfo.sql} [${queryInfo.bindings.join(', ')}]`);
delete times[queryInfo.__knexUid];
});
return database;
}
const database = knex(knexConfig);
export function getSchemaInspector() {
if (inspector) {
return inspector;
}
const times: Record<string, number> = {};
const database = getDatabase();
database
.on('query', (queryInfo) => {
times[queryInfo.__knexUid] = performance.now();
})
.on('query-response', (response, queryInfo) => {
const delta = performance.now() - times[queryInfo.__knexUid];
logger.trace(`[${delta.toFixed(3)}ms] ${queryInfo.sql} [${queryInfo.bindings.join(', ')}]`);
});
inspector = SchemaInspector(database);
return inspector;
}
export async function hasDatabaseConnection(): Promise<boolean> {
const database = getDatabase();
try {
if (env.DB_CLIENT === 'oracledb') {
await database.raw('select 1 from DUAL');
@@ -93,13 +115,11 @@ export async function validateDBConnection(): Promise<void> {
}
}
export const schemaInspector = SchemaInspector(database);
export async function isInstalled(): Promise<boolean> {
const inspector = getSchemaInspector();
// The existence of a directus_collections table alone isn't a "proper" check to see if everything
// is installed correctly of course, but it's safe enough to assume that this collection only
// exists when using the installer CLI.
return await schemaInspector.hasTable('directus_collections');
return await inspector.hasTable('directus_collections');
}
export default database;

View File

@@ -1,6 +1,5 @@
import { Knex } from 'knex';
import SchemaInspector from 'knex-schema-inspector';
import { schemaInspector } from '..';
import logger from '../../logger';
import { RelationMeta } from '../../types';
@@ -22,8 +21,8 @@ export async function up(knex: Knex): Promise<void> {
for (const constraint of constraintsToAdd) {
if (!constraint.one_collection) continue;
const currentPrimaryKeyField = await schemaInspector.primary(constraint.many_collection);
const relatedPrimaryKeyField = await schemaInspector.primary(constraint.one_collection);
const currentPrimaryKeyField = await inspector.primary(constraint.many_collection);
const relatedPrimaryKeyField = await inspector.primary(constraint.one_collection);
if (!currentPrimaryKeyField || !relatedPrimaryKeyField) continue;
const rowsWithIllegalFKValues = await knex
@@ -67,8 +66,8 @@ export async function up(knex: Knex): Promise<void> {
// to `unsigned`, but defaults `.integer()` to `int`. This means that created m2o fields
// have the wrong type. This step will force the m2o `int` field into `unsigned`, but only
// if both types are integers, and only if we go from `int` to `int unsigned`.
const columnInfo = await schemaInspector.columnInfo(constraint.many_collection, constraint.many_field);
const relatedColumnInfo = await schemaInspector.columnInfo(constraint.one_collection!, relatedPrimaryKeyField);
const columnInfo = await inspector.columnInfo(constraint.many_collection, constraint.many_field);
const relatedColumnInfo = await inspector.columnInfo(constraint.one_collection!, relatedPrimaryKeyField);
try {
await knex.schema.alterTable(constraint.many_collection, (table) => {

View File

@@ -5,7 +5,7 @@ import { Item, Query, SchemaOverview } from '../types';
import { AST, FieldNode, NestedCollectionNode } from '../types/ast';
import applyQuery from '../utils/apply-query';
import { toArray } from '../utils/to-array';
import database from './index';
import getDatabase from './index';
type RunASTOptions = {
/**
@@ -39,7 +39,7 @@ export default async function runAST(
): Promise<null | Item | Item[]> {
const ast = cloneDeep(originalAST);
const knex = options?.knex || database;
const knex = options?.knex || getDatabase();
if (ast.type === 'm2a') {
const results: { [collection: string]: null | Item | Item[] } = {};