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:
ChengLei Shao 2023-02-16 10:53:04 +08:00 committed by GitHub
parent 77f83d4278
commit 89c1d39b6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 234 additions and 165 deletions

View File

@ -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 }}

View File

@ -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());
}
}

View 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';
}
}

View File

@ -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;
}
});
}

View File

@ -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,
};
}

View File

@ -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);

View File

@ -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(' ');
};
}
}

View File

@ -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 } {

View File

@ -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(

View File

@ -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' });

View File

@ -309,7 +309,6 @@ pgOnly()('Inherited Collection', () => {
const studentCollection = await db.getCollection('students');
console.log(studentCollection.fields);
await studentCollection.repository.create({
values: {
name: 'foo',

View File

@ -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: {},
});
});
});

View File

@ -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);

View File

@ -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'];

View File

@ -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,

View File

@ -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();
`);

View File

@ -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',
},

View File

@ -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',
},

View File

@ -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' });

View File

@ -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();

View File

@ -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' });

View File

@ -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' });

View File

@ -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' });

View File

@ -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;
}