Fix/pg schema with inherit (#1446)
* fix: pg schema in sync runner * fix: restore with pg schema * chore: databse utils * fix: test * Update nocobase-test.yml * fix: test * chore: db schema test options * feat: collection add schema table name method * fix: update to bigint test * fix: schema test * chore: show constraints query path * chore: show constraints query path * Update nocobase-test.yml * chore: test * fix: test * fix: test * fix: test table name * fix: inherited collection test
This commit is contained in:
parent
77f83d4278
commit
89c1d39b6b
107
.github/workflows/nocobase-test.yml
vendored
107
.github/workflows/nocobase-test.yml
vendored
@ -7,19 +7,16 @@ on:
|
||||
- develop
|
||||
paths:
|
||||
- 'packages/**'
|
||||
# paths-ignore:
|
||||
# - 'docs/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/**'
|
||||
# paths-ignore:
|
||||
# - 'docs/**'
|
||||
|
||||
jobs:
|
||||
sqlite-test:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: ['16', '18']
|
||||
underscored: [true, false]
|
||||
runs-on: ubuntu-latest
|
||||
container: node:${{ matrix.node_version }}
|
||||
steps:
|
||||
@ -36,12 +33,14 @@ jobs:
|
||||
env:
|
||||
DB_DIALECT: sqlite
|
||||
DB_STORAGE: /tmp/db.sqlite
|
||||
DB_UNDERSCORED: ${{ matrix.underscored }}
|
||||
|
||||
postgres-test:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: ['16', '18']
|
||||
|
||||
underscored: [true, false]
|
||||
schema: [public, nocobase]
|
||||
runs-on: ubuntu-latest
|
||||
container: node:${{ matrix.node_version }}
|
||||
services:
|
||||
@ -77,11 +76,14 @@ jobs:
|
||||
DB_USER: nocobase
|
||||
DB_PASSWORD: password
|
||||
DB_DATABASE: nocobase
|
||||
DB_UNDERSCORED: ${{ matrix.underscored }}
|
||||
DB_SCHEMA: ${{ matrix.schema }}
|
||||
|
||||
mysql-test:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: ['16', '18']
|
||||
underscored: [true, false]
|
||||
runs-on: ubuntu-latest
|
||||
container: node:${{ matrix.node_version }}
|
||||
services:
|
||||
@ -109,97 +111,4 @@ jobs:
|
||||
DB_USER: root
|
||||
DB_PASSWORD: password
|
||||
DB_DATABASE: nocobase
|
||||
|
||||
sqlite-underscored-test:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: ['16']
|
||||
runs-on: ubuntu-latest
|
||||
container: node:${{ matrix.node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- name: Test with Sqlite
|
||||
run: yarn test
|
||||
env:
|
||||
DB_DIALECT: sqlite
|
||||
DB_STORAGE: /tmp/db.sqlite
|
||||
DB_UNDERSCORED: true
|
||||
|
||||
postgres-underscored-test:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: ['16']
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container: node:${{ matrix.node_version }}
|
||||
services:
|
||||
# Label used to access the service container
|
||||
postgres:
|
||||
# Docker Hub image
|
||||
image: postgres:10
|
||||
# Provide the password for postgres
|
||||
env:
|
||||
POSTGRES_USER: nocobase
|
||||
POSTGRES_PASSWORD: password
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- name: Test with postgres
|
||||
run: yarn test
|
||||
env:
|
||||
DB_DIALECT: postgres
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: nocobase
|
||||
DB_PASSWORD: password
|
||||
DB_DATABASE: nocobase
|
||||
DB_UNDERSCORED: true
|
||||
|
||||
mysql-underscored-test:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: ['16']
|
||||
runs-on: ubuntu-latest
|
||||
container: node:${{ matrix.node_version }}
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
MYSQL_DATABASE: nocobase
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- name: Test with MySQL
|
||||
run: yarn test
|
||||
env:
|
||||
DB_DIALECT: mysql
|
||||
DB_HOST: mysql
|
||||
DB_PORT: 3306
|
||||
DB_USER: root
|
||||
DB_PASSWORD: password
|
||||
DB_DATABASE: nocobase
|
||||
DB_UNDERSCORED: true
|
||||
DB_UNDERSCORED: ${{ matrix.underscored }}
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
QueryInterfaceDropTableOptions,
|
||||
SyncOptions,
|
||||
Transactionable,
|
||||
Utils
|
||||
Utils,
|
||||
} from 'sequelize';
|
||||
import { Database } from './database';
|
||||
import { Field, FieldOptions } from './fields';
|
||||
@ -532,4 +532,18 @@ export class Collection<
|
||||
public isParent() {
|
||||
return this.context.database.inheritanceMap.isParentNode(this.name);
|
||||
}
|
||||
|
||||
public addSchemaTableName() {
|
||||
const tableName = this.model.tableName;
|
||||
|
||||
if (this.options.schema) {
|
||||
return this.db.utils.addSchema(tableName, this.options.schema);
|
||||
}
|
||||
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public quotedTableName() {
|
||||
return this.db.utils.quoteTable(this.addSchemaTableName());
|
||||
}
|
||||
}
|
||||
|
38
packages/core/database/src/database-utils/index.ts
Normal file
38
packages/core/database/src/database-utils/index.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import Database from '../database';
|
||||
import lodash from 'lodash';
|
||||
|
||||
export default class DatabaseUtils {
|
||||
constructor(public db: Database) {}
|
||||
|
||||
addSchema(tableName, schema?) {
|
||||
if (this.db.options.schema) {
|
||||
schema = this.db.options.schema;
|
||||
}
|
||||
|
||||
if (schema) {
|
||||
// @ts-ignore
|
||||
tableName = this.db.sequelize.getQueryInterface().queryGenerator.addSchema({
|
||||
tableName,
|
||||
_schema: this.db.options.schema,
|
||||
});
|
||||
}
|
||||
|
||||
return tableName;
|
||||
}
|
||||
|
||||
quoteTable(tableName) {
|
||||
const queryGenerator = this.db.sequelize.getQueryInterface().queryGenerator;
|
||||
// @ts-ignore
|
||||
tableName = queryGenerator.quoteTable(lodash.isPlainObject(tableName) ? tableName : this.addSchema(tableName));
|
||||
|
||||
return tableName;
|
||||
}
|
||||
|
||||
schema() {
|
||||
if (!this.db.inDialect('postgres')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.db.options.schema || 'public';
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ import {
|
||||
Sequelize,
|
||||
SyncOptions,
|
||||
Transactionable,
|
||||
Utils
|
||||
Utils,
|
||||
} from 'sequelize';
|
||||
import { SequelizeStorage, Umzug } from 'umzug';
|
||||
import { Collection, CollectionOptions, RepositoryType } from './collection';
|
||||
@ -58,9 +58,11 @@ import {
|
||||
SyncListener,
|
||||
UpdateListener,
|
||||
UpdateWithAssociationsListener,
|
||||
ValidateListener
|
||||
ValidateListener,
|
||||
} from './types';
|
||||
import { snakeCase } from './utils';
|
||||
import { patchSequelizeQueryInterface, snakeCase } from './utils';
|
||||
|
||||
import DatabaseUtils from './database-utils';
|
||||
|
||||
export interface MergeOptions extends merge.Options {}
|
||||
|
||||
@ -157,6 +159,7 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
||||
modelCollection = new Map<ModelStatic<any>, Collection>();
|
||||
tableNameCollectionMap = new Map<string, Collection>();
|
||||
|
||||
utils = new DatabaseUtils(this);
|
||||
referenceMap = new ReferencesMap();
|
||||
inheritanceMap = new InheritanceMap();
|
||||
|
||||
@ -197,9 +200,10 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
||||
// https://github.com/sequelize/sequelize/issues/1774
|
||||
require('pg').defaults.parseInt8 = true;
|
||||
}
|
||||
this.options = opts;
|
||||
|
||||
this.sequelize = new Sequelize(opts);
|
||||
this.options = opts;
|
||||
|
||||
this.collections = new Map();
|
||||
this.modelHook = new ModelHook(this);
|
||||
|
||||
@ -263,6 +267,7 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
||||
});
|
||||
|
||||
this.initListener();
|
||||
patchSequelizeQueryInterface(this);
|
||||
}
|
||||
|
||||
initListener() {
|
||||
@ -321,6 +326,10 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.schema && !options.schema) {
|
||||
options.schema = this.options.schema;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ export function getConfigByEnv() {
|
||||
},
|
||||
timezone: process.env.DB_TIMEZONE,
|
||||
underscored: process.env.DB_UNDERSCORED === 'true',
|
||||
schema: process.env.DB_SCHEMA !== 'public' ? process.env.DB_SCHEMA : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ export class SyncRunner {
|
||||
|
||||
const inheritedCollection = model.collection as InheritedCollection;
|
||||
const db = inheritedCollection.context.database;
|
||||
const schemaName = db.options.schema || 'public';
|
||||
|
||||
const dialect = db.sequelize.getDialect();
|
||||
|
||||
const queryInterface = db.sequelize.getQueryInterface();
|
||||
@ -27,7 +29,10 @@ export class SyncRunner {
|
||||
|
||||
const parentTables = parents.map((parent) => parent.model.tableName);
|
||||
|
||||
const tableName = model.getTableName();
|
||||
const tableName = model.tableName;
|
||||
|
||||
const schemaTableName = db.utils.addSchema(tableName);
|
||||
const quoteTableName = db.utils.quoteTable(tableName);
|
||||
|
||||
const attributes = model.tableAttributes;
|
||||
|
||||
@ -44,6 +49,7 @@ export class SyncRunner {
|
||||
`SELECT column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = '${parent}'
|
||||
and table_schema = '${schemaName}'
|
||||
and "column_name" = 'id';`,
|
||||
{
|
||||
transaction,
|
||||
@ -60,7 +66,7 @@ export class SyncRunner {
|
||||
throw new Error(`Can't find sequence name of ${parent}`);
|
||||
}
|
||||
|
||||
const regex = new RegExp(/nextval\('("?\w+"?)\'.*\)/);
|
||||
const regex = new RegExp(/nextval\('(.*)'::regclass\)/);
|
||||
const match = regex.exec(columnDefault);
|
||||
|
||||
const sequenceName = match[1];
|
||||
@ -82,25 +88,24 @@ export class SyncRunner {
|
||||
}
|
||||
}
|
||||
|
||||
await this.createTable(tableName, childAttributes, options, model, parentTables);
|
||||
await this.createTable(schemaTableName, childAttributes, options, model, parentTables, db);
|
||||
|
||||
if (maxSequenceName) {
|
||||
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(
|
||||
(parent) => db.getCollection(parent).model.tableName,
|
||||
);
|
||||
|
||||
const sequenceTables = [...parentsDeep, tableName];
|
||||
const sequenceTables = [...parentsDeep, tableName.toString()];
|
||||
|
||||
for (const sequenceTable of sequenceTables) {
|
||||
const queryName = Boolean(sequenceTable.match(/[A-Z]/)) ? `"${sequenceTable}"` : sequenceTable;
|
||||
const queryName =
|
||||
Boolean(sequenceTable.match(/[A-Z]/)) && !sequenceTable.includes(`"`) ? `"${sequenceTable}"` : sequenceTable;
|
||||
|
||||
const idColumnQuery = await queryInterface.sequelize.query(
|
||||
`
|
||||
SELECT true
|
||||
FROM pg_attribute
|
||||
WHERE attrelid = '${queryName}'::regclass -- cast to a registered class (table)
|
||||
AND attname = 'id'
|
||||
AND NOT attisdropped
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schemaName}';
|
||||
`,
|
||||
{
|
||||
transaction,
|
||||
@ -112,7 +117,7 @@ AND NOT attisdropped
|
||||
}
|
||||
|
||||
await queryInterface.sequelize.query(
|
||||
`alter table "${sequenceTable}"
|
||||
`alter table "${schemaName}"."${sequenceTable}"
|
||||
alter column id set default nextval('${maxSequenceName}')`,
|
||||
{
|
||||
transaction,
|
||||
@ -134,7 +139,7 @@ AND NOT attisdropped
|
||||
}
|
||||
}
|
||||
|
||||
static async createTable(tableName, attributes, options, model, parentTables) {
|
||||
static async createTable(tableName, attributes, options, model, parentTables, db) {
|
||||
let sql = '';
|
||||
|
||||
options = { ...options };
|
||||
@ -159,7 +164,11 @@ AND NOT attisdropped
|
||||
|
||||
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(
|
||||
';',
|
||||
` INHERITS (${parentTables.map((t) => `"${t}"`).join(', ')});`,
|
||||
` INHERITS (${parentTables
|
||||
.map((t) => {
|
||||
return db.utils.quoteTable(db.utils.addSchema(t, db.options.schema));
|
||||
})
|
||||
.join(', ')});`,
|
||||
);
|
||||
|
||||
return await model.sequelize.query(sql, options);
|
||||
|
@ -2,6 +2,7 @@ import crypto from 'crypto';
|
||||
import { IdentifierError } from './errors/identifier-error';
|
||||
import { Model } from './model';
|
||||
import lodash from 'lodash';
|
||||
import Database from './database';
|
||||
|
||||
type HandleAppendsQueryOptions = {
|
||||
templateModel: any;
|
||||
@ -82,3 +83,36 @@ export function getTableName(collectionName: string, options) {
|
||||
export function snakeCase(name: string) {
|
||||
return require('sequelize').Utils.underscore(name);
|
||||
}
|
||||
|
||||
export function patchSequelizeQueryInterface(db: Database) {
|
||||
if (db.inDialect('postgres')) {
|
||||
//@ts-ignore
|
||||
const queryGenerator = db.sequelize.dialect.queryGenerator;
|
||||
|
||||
queryGenerator.showConstraintsQuery = (tableName, constraintName) => {
|
||||
const lines = [
|
||||
'SELECT constraint_catalog AS "constraintCatalog",',
|
||||
'constraint_schema AS "constraintSchema",',
|
||||
'constraint_name AS "constraintName",',
|
||||
'table_catalog AS "tableCatalog",',
|
||||
'table_schema AS "tableSchema",',
|
||||
'table_name AS "tableName",',
|
||||
'constraint_type AS "constraintType",',
|
||||
'is_deferrable AS "isDeferrable",',
|
||||
'initially_deferred AS "initiallyDeferred"',
|
||||
'from INFORMATION_SCHEMA.table_constraints',
|
||||
`WHERE table_name='${tableName}'`,
|
||||
];
|
||||
|
||||
if (!constraintName) {
|
||||
lines.push(`AND constraint_name='${constraintName}'`);
|
||||
}
|
||||
|
||||
if (db.options.schema && db.options.schema !== 'public') {
|
||||
lines.push(`AND table_schema='${db.options.schema}'`);
|
||||
}
|
||||
|
||||
return lines.join(' ');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ interface Resource {
|
||||
export class MockServer extends Application {
|
||||
async loadAndInstall(options: any = {}) {
|
||||
await this.load({ method: 'install' });
|
||||
|
||||
await this.install({
|
||||
...options,
|
||||
sync: {
|
||||
@ -69,7 +70,7 @@ export class MockServer extends Application {
|
||||
}
|
||||
|
||||
async cleanDb() {
|
||||
await this.db.sequelize.getQueryInterface().dropAllTables();
|
||||
await this.db.clean({ drop: true });
|
||||
}
|
||||
|
||||
agent(): SuperAgentTest & { resource: (name: string, resourceOf?: any) => Resource } {
|
||||
|
@ -97,7 +97,7 @@ describe('collections repository', () => {
|
||||
|
||||
const testCollection = app.db.getCollection('test');
|
||||
|
||||
const tableInfo = await app.db.sequelize.getQueryInterface().describeTable(testCollection.model.tableName);
|
||||
const tableInfo = await app.db.sequelize.getQueryInterface().describeTable(testCollection.addSchemaTableName());
|
||||
|
||||
expect(tableInfo['field']).toBeDefined();
|
||||
});
|
||||
@ -504,7 +504,7 @@ describe('collections repository', () => {
|
||||
|
||||
const indexes = (await app.db.sequelize
|
||||
.getQueryInterface()
|
||||
.showIndex(app.db.getCollection('test').model.tableName)) as any;
|
||||
.showIndex(app.db.getCollection('test').addSchemaTableName())) as any;
|
||||
|
||||
const columnName = app.db.getCollection('test').model.rawAttributes.testField.field;
|
||||
|
||||
@ -528,7 +528,7 @@ describe('collections repository', () => {
|
||||
|
||||
const afterIndexes = (await app.db.sequelize
|
||||
.getQueryInterface()
|
||||
.showIndex(app.db.getCollection('test').model.tableName)) as any;
|
||||
.showIndex(app.db.getCollection('test').addSchemaTableName())) as any;
|
||||
|
||||
expect(
|
||||
afterIndexes.find(
|
||||
|
@ -9,6 +9,9 @@ export async function createApp(options = {}) {
|
||||
...options,
|
||||
});
|
||||
|
||||
await app.db.clean({ drop: true });
|
||||
await app.db.sync({});
|
||||
|
||||
app.plugin(PluginErrorHandler, { name: 'error-handler' });
|
||||
app.plugin(Plugin, { name: 'collection-manager' });
|
||||
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
|
||||
|
@ -309,7 +309,6 @@ pgOnly()('Inherited Collection', () => {
|
||||
|
||||
const studentCollection = await db.getCollection('students');
|
||||
|
||||
console.log(studentCollection.fields);
|
||||
await studentCollection.repository.create({
|
||||
values: {
|
||||
name: 'foo',
|
||||
|
@ -0,0 +1,54 @@
|
||||
import Database, { Repository } from '@nocobase/database';
|
||||
import Application from '@nocobase/server';
|
||||
import { createApp } from '..';
|
||||
import { pgOnly } from '@nocobase/test';
|
||||
|
||||
pgOnly()('Inherited Collection with schema options', () => {
|
||||
let db: Database;
|
||||
let app: Application;
|
||||
|
||||
let collectionRepository: Repository;
|
||||
|
||||
let fieldsRepository: Repository;
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await createApp({
|
||||
database: {
|
||||
schema: 'testSchema',
|
||||
},
|
||||
});
|
||||
|
||||
db = app.db;
|
||||
|
||||
collectionRepository = db.getCollection('collections').repository;
|
||||
fieldsRepository = db.getCollection('fields').repository;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.destroy();
|
||||
});
|
||||
|
||||
it('should create inherited collection in difference schema', async () => {
|
||||
await collectionRepository.create({
|
||||
values: {
|
||||
name: 'b',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
context: {},
|
||||
});
|
||||
|
||||
await collectionRepository.create({
|
||||
values: {
|
||||
name: 'a',
|
||||
inherits: ['b'],
|
||||
fields: [{ type: 'string', name: 'bField' }],
|
||||
},
|
||||
context: {},
|
||||
});
|
||||
});
|
||||
});
|
@ -68,17 +68,16 @@ excludeSqlite()('update id to bigint test', () => {
|
||||
await db.sync();
|
||||
|
||||
const assertBigInt = async (collectionName, fieldName) => {
|
||||
const tableInfo = await db.sequelize
|
||||
.getQueryInterface()
|
||||
.describeTable(
|
||||
db.getCollection(collectionName) ? db.getCollection(collectionName).model.tableName : collectionName,
|
||||
);
|
||||
const tableName = db.getCollection(collectionName)
|
||||
? db.getCollection(collectionName).addSchemaTableName()
|
||||
: collectionName;
|
||||
|
||||
if (process.env.DB_UNDERSCORED) {
|
||||
const tableInfo = await db.sequelize.getQueryInterface().describeTable(tableName);
|
||||
|
||||
if (db.options.underscored) {
|
||||
fieldName = lodash.snakeCase(fieldName);
|
||||
}
|
||||
|
||||
console.log(`${collectionName}, ${fieldName}`, tableInfo[fieldName].type);
|
||||
expect(tableInfo[fieldName].type).toBe('BIGINT');
|
||||
};
|
||||
|
||||
@ -92,7 +91,7 @@ excludeSqlite()('update id to bigint test', () => {
|
||||
|
||||
let usersTableInfo = await db.sequelize
|
||||
.getQueryInterface()
|
||||
.describeTable(db.getCollection('users').model.tableName);
|
||||
.describeTable(db.getCollection('users').addSchemaTableName());
|
||||
|
||||
assertInteger(usersTableInfo.id.type);
|
||||
|
||||
|
@ -38,6 +38,8 @@ export default class UpdateIdToBigIntMigrator extends Migration {
|
||||
let sql;
|
||||
|
||||
const tableName = model.tableName;
|
||||
const addSchemaTableName = db.utils.addSchema(tableName);
|
||||
const quoteTableName = db.utils.quoteTable(addSchemaTableName);
|
||||
|
||||
const collection = db.modelCollection.get(model);
|
||||
|
||||
@ -56,7 +58,7 @@ export default class UpdateIdToBigIntMigrator extends Migration {
|
||||
|
||||
if (model.rawAttributes[fieldName].type instanceof DataTypes.INTEGER) {
|
||||
if (db.inDialect('postgres')) {
|
||||
sql = `ALTER TABLE "${tableName}" ALTER COLUMN "${columnName}" SET DATA TYPE BIGINT;`;
|
||||
sql = `ALTER TABLE ${quoteTableName} ALTER COLUMN "${columnName}" SET DATA TYPE BIGINT;`;
|
||||
} else if (db.inDialect('mysql')) {
|
||||
const dataTypeOrOptions = model.rawAttributes[fieldName];
|
||||
const attributeName = fieldName;
|
||||
@ -74,7 +76,7 @@ export default class UpdateIdToBigIntMigrator extends Migration {
|
||||
},
|
||||
);
|
||||
|
||||
sql = queryGenerator.changeColumnQuery(tableName, query);
|
||||
sql = queryGenerator.changeColumnQuery(addSchemaTableName, query);
|
||||
|
||||
sql = sql.replace(' PRIMARY KEY;', ' ;');
|
||||
}
|
||||
@ -89,7 +91,7 @@ export default class UpdateIdToBigIntMigrator extends Migration {
|
||||
}
|
||||
|
||||
if (db.inDialect('postgres')) {
|
||||
const sequenceQuery = `SELECT pg_get_serial_sequence('"${model.tableName}"', '${columnName}');`;
|
||||
const sequenceQuery = `SELECT pg_get_serial_sequence('${quoteTableName}', '${columnName}');`;
|
||||
const [result] = await this.sequelize.query(sequenceQuery, {});
|
||||
const sequenceName = result[0]['pg_get_serial_sequence'];
|
||||
|
||||
|
@ -107,7 +107,7 @@ export class FieldModel extends MagicAttributeModel {
|
||||
|
||||
const queryInterface = this.db.sequelize.getQueryInterface() as any;
|
||||
|
||||
const existsIndexes = await queryInterface.showIndex(collection.model.tableName, {
|
||||
const existsIndexes = await queryInterface.showIndex(collection.addSchemaTableName(), {
|
||||
transaction: options.transaction,
|
||||
});
|
||||
|
||||
@ -120,11 +120,7 @@ export class FieldModel extends MagicAttributeModel {
|
||||
let constraintName = `${tableName}_${field.name}_uk`;
|
||||
|
||||
if (existUniqueIndex) {
|
||||
const existsUniqueConstraints = await queryInterface.showConstraint(
|
||||
collection.model.tableName,
|
||||
constraintName,
|
||||
{},
|
||||
);
|
||||
const existsUniqueConstraints = await queryInterface.showConstraint(tableName, constraintName, {});
|
||||
|
||||
existsUniqueConstraint = existsUniqueConstraints[0];
|
||||
}
|
||||
@ -133,7 +129,7 @@ export class FieldModel extends MagicAttributeModel {
|
||||
// @ts-ignore
|
||||
await collection.sync({ ...options, force: false, alter: { drop: false } });
|
||||
|
||||
await queryInterface.addConstraint(tableName, {
|
||||
await queryInterface.addConstraint(collection.addSchemaTableName(), {
|
||||
type: 'unique',
|
||||
fields: [columnName],
|
||||
name: constraintName,
|
||||
@ -142,7 +138,7 @@ export class FieldModel extends MagicAttributeModel {
|
||||
}
|
||||
|
||||
if (!unique && existsUniqueConstraint) {
|
||||
await queryInterface.removeConstraint(collection.model.tableName, constraintName, {
|
||||
await queryInterface.removeConstraint(collection.addSchemaTableName(), constraintName, {
|
||||
transaction: options.transaction,
|
||||
});
|
||||
}
|
||||
@ -164,7 +160,7 @@ export class FieldModel extends MagicAttributeModel {
|
||||
const queryInterface = collection.db.sequelize.getQueryInterface();
|
||||
|
||||
await queryInterface.changeColumn(
|
||||
collection.model.tableName,
|
||||
collection.addSchemaTableName(),
|
||||
collection.model.rawAttributes[this.get('name')].field,
|
||||
{
|
||||
type: field.dataType,
|
||||
|
@ -19,6 +19,7 @@ describe('dump', () => {
|
||||
app = mockServer();
|
||||
|
||||
db = app.db;
|
||||
await app.cleanDb();
|
||||
|
||||
app.db.collection({
|
||||
name: 'users',
|
||||
@ -52,7 +53,6 @@ describe('dump', () => {
|
||||
fields: [],
|
||||
});
|
||||
|
||||
await app.cleanDb();
|
||||
await db.sync();
|
||||
});
|
||||
|
||||
@ -148,7 +148,7 @@ $$`);
|
||||
await db.sequelize.query(`
|
||||
CREATE TRIGGER last_name_changes
|
||||
BEFORE UPDATE
|
||||
ON ${app.db.getCollection('users').model.tableName}
|
||||
ON ${app.db.getCollection('users').quotedTableName()}
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE trigger_function();
|
||||
`);
|
||||
|
@ -177,7 +177,7 @@ ORDER BY table_schema, table_name`,
|
||||
const dataStream = fs.createWriteStream(dataFilePath);
|
||||
|
||||
const rows = await app.db.sequelize.query(
|
||||
sqlAdapter(app.db, `SELECT * FROM ${collection.isParent() ? 'ONLY' : ''} "${collection.model.tableName}"`),
|
||||
sqlAdapter(app.db, `SELECT * FROM ${collection.isParent() ? 'ONLY' : ''} ${collection.quotedTableName()}`),
|
||||
{
|
||||
type: 'SELECT',
|
||||
},
|
||||
|
@ -135,12 +135,12 @@ export class Restorer extends AppMigrator {
|
||||
|
||||
const metaContent = await fsPromises.readFile(collectionMetaPath, 'utf8');
|
||||
const meta = JSON.parse(metaContent);
|
||||
const tableName = meta.tableName;
|
||||
const tableName = this.app.db.utils.quoteTable(meta.tableName);
|
||||
|
||||
try {
|
||||
// disable trigger
|
||||
if (this.app.db.inDialect('postgres')) {
|
||||
await this.app.db.sequelize.query(`ALTER TABLE IF EXISTS "${tableName}" DISABLE TRIGGER ALL`);
|
||||
await this.app.db.sequelize.query(`ALTER TABLE IF EXISTS ${tableName} DISABLE TRIGGER ALL`);
|
||||
}
|
||||
|
||||
await this.importCollection({
|
||||
@ -152,7 +152,7 @@ export class Restorer extends AppMigrator {
|
||||
});
|
||||
} finally {
|
||||
if (this.app.db.inDialect('postgres')) {
|
||||
await this.app.db.sequelize.query(`ALTER TABLE IF EXISTS "${tableName}" ENABLE TRIGGER ALL`);
|
||||
await this.app.db.sequelize.query(`ALTER TABLE IF EXISTS ${tableName} ENABLE TRIGGER ALL`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -214,6 +214,8 @@ export class Restorer extends AppMigrator {
|
||||
rowCondition?: (row: any) => boolean;
|
||||
}) {
|
||||
const app = this.app;
|
||||
const db = app.db;
|
||||
|
||||
const collectionName = options.name;
|
||||
const dir = this.workDir;
|
||||
const collection = app.db.getCollection(collectionName);
|
||||
@ -224,15 +226,16 @@ export class Restorer extends AppMigrator {
|
||||
const meta = JSON.parse(metaContent);
|
||||
app.log.info(`collection meta ${metaContent}`);
|
||||
|
||||
const tableName = meta.tableName;
|
||||
const addSchemaTableName = db.utils.addSchema(meta.tableName);
|
||||
const tableName = db.utils.quoteTable(meta.tableName);
|
||||
|
||||
if (options.clear !== false) {
|
||||
// truncate old data
|
||||
let sql = `TRUNCATE TABLE "${tableName}"`;
|
||||
let sql = `TRUNCATE TABLE ${tableName}`;
|
||||
|
||||
if (app.db.inDialect('sqlite')) {
|
||||
sql = `DELETE
|
||||
FROM "${tableName}"`;
|
||||
FROM ${tableName}`;
|
||||
}
|
||||
|
||||
await app.db.sequelize.query(sqlAdapter(app.db, sql));
|
||||
@ -290,7 +293,7 @@ export class Restorer extends AppMigrator {
|
||||
|
||||
//@ts-ignore
|
||||
const sql = collection.model.queryInterface.queryGenerator.bulkInsertQuery(
|
||||
tableName,
|
||||
addSchemaTableName,
|
||||
rowsWithMeta,
|
||||
{},
|
||||
fieldMappedAttributes,
|
||||
@ -311,18 +314,20 @@ export class Restorer extends AppMigrator {
|
||||
if (this.app.db.inDialect('postgres')) {
|
||||
const sequenceNameResult = await app.db.sequelize.query(
|
||||
`SELECT column_default FROM information_schema.columns WHERE
|
||||
table_name='${collection.model.tableName}' and "column_name" = 'id';`,
|
||||
table_name='${collection.model.tableName}' and "column_name" = 'id' and table_schema = '${
|
||||
app.db.options.schema || 'public'
|
||||
}';`,
|
||||
);
|
||||
|
||||
if (sequenceNameResult[0].length) {
|
||||
const columnDefault = sequenceNameResult[0][0]['column_default'];
|
||||
if (columnDefault.includes(`${collection.model.tableName}_id_seq`)) {
|
||||
const regex = new RegExp(/nextval\('("?\w+"?)\'.*\)/);
|
||||
const regex = new RegExp(/nextval\('(.*)'::regclass\)/);
|
||||
const match = regex.exec(columnDefault);
|
||||
const sequenceName = match[1];
|
||||
|
||||
const maxVal = await app.db.sequelize.query(
|
||||
`SELECT MAX("${primaryKeyAttribute.field}") FROM "${collection.model.tableName}"`,
|
||||
`SELECT MAX("${primaryKeyAttribute.field}") FROM ${tableName}`,
|
||||
{
|
||||
type: 'SELECT',
|
||||
},
|
||||
|
@ -13,8 +13,7 @@ describe('action test', () => {
|
||||
|
||||
db = app.db;
|
||||
|
||||
const queryInterface = db.sequelize.getQueryInterface();
|
||||
await queryInterface.dropAllTables();
|
||||
await db.clean({ drop: true });
|
||||
|
||||
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
|
||||
|
||||
|
@ -19,9 +19,7 @@ describe('ui schema model', () => {
|
||||
|
||||
db = app.db;
|
||||
|
||||
const queryInterface = db.sequelize.getQueryInterface();
|
||||
await queryInterface.dropAllTables();
|
||||
|
||||
await db.clean({ drop: true });
|
||||
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
|
||||
|
||||
await app.load();
|
||||
|
@ -23,7 +23,7 @@ describe('ui_schema repository with cache', () => {
|
||||
db = app.db;
|
||||
cache = app.cache;
|
||||
|
||||
await db.sequelize.getQueryInterface().dropAllTables();
|
||||
await db.clean({ drop: true });
|
||||
|
||||
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
|
||||
|
||||
|
@ -22,8 +22,7 @@ describe('ui_schema repository', () => {
|
||||
|
||||
db = app.db;
|
||||
|
||||
const queryInterface = db.sequelize.getQueryInterface();
|
||||
await queryInterface.dropAllTables();
|
||||
await db.clean({ drop: true });
|
||||
|
||||
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
|
||||
|
||||
|
@ -20,7 +20,7 @@ describe('ui-schema', () => {
|
||||
|
||||
db = app.db;
|
||||
|
||||
await db.sequelize.getQueryInterface().dropAllTables();
|
||||
await db.clean({ drop: true });
|
||||
|
||||
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
|
||||
|
||||
|
@ -103,7 +103,7 @@ export class UiSchemaRepository extends Repository {
|
||||
|
||||
tableNameAdapter(tableName) {
|
||||
if (this.database.sequelize.getDialect() === 'postgres') {
|
||||
return `"${tableName}"`;
|
||||
return `"${this.database.options.schema || 'public'}"."${tableName}"`;
|
||||
}
|
||||
return tableName;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user