Skip to content

Commit

Permalink
fix(introspection): handle sqlite datasources in broken relationship …
Browse files Browse the repository at this point in the history
…logger (#811)
  • Loading branch information
SkyHuss authored Sep 13, 2023
1 parent 94ab0cc commit 78be39d
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 19 deletions.
58 changes: 44 additions & 14 deletions packages/datasource-sql/src/introspection/introspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,14 @@ export default class Introspector {
tableName: string,
): Promise<Table> {
const queryInterface = sequelize.getQueryInterface() as QueryInterfaceExt;
const [columnDescriptions, tableIndexes, tableReferences, constraintNamesForForeignKey] =
await Promise.all([
queryInterface.describeTable(tableName),
queryInterface.showIndex(tableName),
queryInterface.getForeignKeyReferencesForTable(tableName),
sequelize.query(
`SELECT constraint_name, table_name from information_schema.table_constraints
where table_name = :tableName and constraint_type = 'FOREIGN KEY';`,
{ replacements: { tableName }, type: QueryTypes.SELECT },
),
]);

const [columnDescriptions, tableIndexes, tableReferences] = await Promise.all([
queryInterface.describeTable(tableName),
queryInterface.showIndex(tableName),
queryInterface.getForeignKeyReferencesForTable(tableName),
]);

await this.detectBrokenRelationship(tableName, sequelize, tableReferences, logger);

const columns = await Promise.all(
Object.entries(columnDescriptions).map(async ([name, description]) => {
Expand All @@ -58,8 +55,6 @@ export default class Introspector {
}),
);

this.detectBrokenRelationship(constraintNamesForForeignKey, tableReferences, logger);

return {
name: tableName,
columns: columns.filter(Boolean),
Expand Down Expand Up @@ -135,7 +130,42 @@ export default class Introspector {
}
}

private static detectBrokenRelationship(
private static async detectBrokenRelationship(
tableName: string,
sequelize: Sequelize,
tableReferences: SequelizeReference[],
logger: Logger,
) {
let constraintNamesForForeignKey: Array<{ constraint_name: string; table_name: string }> = [];
const dialect = sequelize.getDialect() as Dialect;

if (dialect === 'sqlite') {
constraintNamesForForeignKey = await sequelize.query<{
constraint_name: string;
table_name: string;
}>(
`SELECT "from" as constraint_name, :tableName as table_name
from pragma_foreign_key_list(:tableName);`,
{
replacements: { tableName },
type: QueryTypes.SELECT,
},
);
} else {
constraintNamesForForeignKey = await sequelize.query<{
constraint_name: string;
table_name: string;
}>(
`SELECT constraint_name, table_name from information_schema.table_constraints
where table_name = :tableName and constraint_type = 'FOREIGN KEY';`,
{ replacements: { tableName }, type: QueryTypes.SELECT },
);
}

this.logBrokenRelationship(constraintNamesForForeignKey, tableReferences, logger);
}

private static logBrokenRelationship(
constraintNamesForForeignKey: unknown[],
tableReferences: SequelizeReference[],
logger: Logger,
Expand Down
59 changes: 54 additions & 5 deletions packages/datasource-sql/test/introspection/intropector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,35 +45,84 @@ describe('Introspector', () => {
const mockShowIndex = jest.fn().mockResolvedValue([{ fields: ['column1'], unique: true }]);
const mockQuery = jest.fn().mockResolvedValue([
{
table_name: 'table1',
constraint_name: 'fk_column1',
table_name: 'table1',
},
{
table_name: 'table1',
constraint_name: 'fk_column2',
table_name: 'table1',
},
{
table_name: 'table1',
constraint_name: 'fk_unknown_column',
table_name: 'table1',
},
]);
mockQueryInterface.describeTable = mockDescribeTable;
mockQueryInterface.showIndex = mockShowIndex;
mockSequelize.query = mockQuery;
mockSequelize.getDialect = jest.fn().mockResolvedValue('postgres');
mockSequelize.getDialect = jest.fn().mockReturnValue('postgres');

await Introspector.introspect(mockSequelize, logger);

// Assert the Sequelize method calls
expect(mockDescribeTable).toHaveBeenCalledWith('table1');
expect(mockShowIndex).toHaveBeenCalledWith('table1');
expect(mockQuery).toHaveBeenCalledWith(
`SELECT constraint_name, table_name from information_schema.table_constraints
`SELECT constraint_name, table_name from information_schema.table_constraints
where table_name = :tableName and constraint_type = 'FOREIGN KEY';`,
{ replacements: { tableName: 'table1' }, type: QueryTypes.SELECT },
);

// Assert the logger call
expect(logger).toHaveBeenCalledTimes(1);
expect(logger).toHaveBeenCalledWith(
'Error',
// eslint-disable-next-line max-len
"Failed to load constraints on relation 'fk_unknown_column' on table 'table1'. The relation will be ignored.",
);
});

it('should log errors for missing constraint names for sqlite datasources', async () => {
// Mock the Sequelize methods
const mockDescribeTable = jest.fn().mockResolvedValue({
column1: { type: 'INTEGER', allowNull: false, primaryKey: true },
});
const mockShowIndex = jest.fn().mockResolvedValue([{ fields: ['column1'], unique: true }]);
const mockQuery = jest.fn().mockResolvedValue([
{
constraint_name: 'fk_column1',
table_name: 'table1',
},
{
constraint_name: 'fk_column2',
table_name: 'table1',
},
{
constraint_name: 'fk_unknown_column',
table_name: 'table1',
},
]);
mockQueryInterface.describeTable = mockDescribeTable;
mockQueryInterface.showIndex = mockShowIndex;
mockSequelize.query = mockQuery;
mockSequelize.getDialect = jest.fn().mockReturnValue('sqlite');

await Introspector.introspect(mockSequelize, logger);

// Assert the Sequelize method calls
expect(mockDescribeTable).toHaveBeenCalledWith('table1');
expect(mockShowIndex).toHaveBeenCalledWith('table1');
expect(mockQuery).toHaveBeenCalledWith(
`SELECT "from" as constraint_name, :tableName as table_name
from pragma_foreign_key_list(:tableName);`,
{
replacements: { tableName: 'table1' },
type: QueryTypes.SELECT,
},
);

// Assert the logger call
expect(logger).toHaveBeenCalledTimes(1);
expect(logger).toHaveBeenCalledWith(
'Error',
// eslint-disable-next-line max-len
Expand Down

0 comments on commit 78be39d

Please sign in to comment.