From 22c36347b4aeb90756fb7cb5993163850b37f416 Mon Sep 17 00:00:00 2001 From: yevhenii-moroziuk Date: Tue, 9 Jun 2026 18:50:11 +0300 Subject: [PATCH 1/5] HCK-16468: Make all call sequential --- .../databaseService/databaseService.js | 24 ++++----- .../reverseEngineeringService.js | 51 +++++++++++-------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/reverse_engineering/databaseService/databaseService.js b/reverse_engineering/databaseService/databaseService.js index 5469bb9..1c79865 100644 --- a/reverse_engineering/databaseService/databaseService.js +++ b/reverse_engineering/databaseService/databaseService.js @@ -33,7 +33,11 @@ const getClient = async ({ client, dbName, meta, logger }) => { }, async query(...queryParams) { try { - return await currentDbConnectionClient.query(...queryParams); + const result = await currentDbConnectionClient.query(...queryParams); + if (meta.action) { + logger.log('info', { action: meta?.action }, 'Query finished'); + } + return result; } catch (error) { if (meta) { if (error.number === PERMISSION_DENIED_CODE) { @@ -315,6 +319,8 @@ const getIndexesBucketCount = async ({ client, dbName, indexesId, logger }) => { logger, }); + logger.log('info', { message: `Get '${dbName}' database indexes bucket count.` }, 'Reverse Engineering'); + return mapResponse( await currentDbConnectionClient.query(` SELECT hs.total_bucket_count, hs.index_id @@ -791,10 +797,8 @@ const getDatabaseXmlSchemaCollection = async ({ client, dbName, allUniqueSchemas logger.log('info', { message: `Get '${dbName}' database xml schema collection.` }, 'Reverse Engineering'); - const schemaAlias = 'xsc'; const tableAlias = 'xcu'; const whereClauseParts = getWhereClauseForUniqueSchemasAndTables({ - schemaAlias, tableAlias, allUniqueSchemasAndTables, }); @@ -922,12 +926,8 @@ const buildDescriptionCommentsRetrieveQuery = ({ schema, entity }) => { return `SELECT objtype, objname, value FROM fn_listextendedproperty ('MS_Description', ${schemaTemplate}, ${entityTemplate});`; }; -const getWhereClauseForUniqueSchemasAndTables = ({ - schemaAlias, - tableAlias, - allUniqueSchemasAndTables: { schemas, tables }, -}) => - `OBJECT_SCHEMA_NAME(${schemaAlias || tableAlias}.object_id) IN (${[...schemas].join(', ')}) +const getWhereClauseForUniqueSchemasAndTables = ({ tableAlias, allUniqueSchemasAndTables: { schemas, tables } }) => + `OBJECT_SCHEMA_NAME(${tableAlias}.object_id) IN (${[...schemas].join(', ')}) AND OBJECT_NAME(${tableAlias}.object_id) IN (${[...tables].join(', ')})`; const getDatabaseProcedures = async ({ client, dbName, logger }) => { @@ -951,11 +951,11 @@ const getDatabaseProcedures = async ({ client, dbName, logger }) => { sm.definition AS procedure_body, ep.value AS description FROM sys.procedures p - JOIN sys.schemas s + JOIN sys.schemas s ON p.schema_id = s.schema_id - LEFT JOIN sys.sql_modules sm + LEFT JOIN sys.sql_modules sm ON p.object_id = sm.object_id - LEFT JOIN sys.extended_properties ep + LEFT JOIN sys.extended_properties ep ON ep.major_id = p.object_id AND ep.minor_id = 0 AND ep.name = 'MS_Description' diff --git a/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js b/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js index 8590fe3..1330f74 100644 --- a/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js +++ b/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js @@ -288,28 +288,35 @@ const addTotalBucketCountToDatabaseIndexes = ({ databaseIndexes, indexesBucketCo const fetchDatabaseMetadata = async ({ client, dbName, tablesInfo, logger }) => { const allUniqueSchemasAndTables = getAllUniqueSchemasAndTables({ tablesInfo }); - const [ - rawDatabaseIndexes, - databaseMemoryOptimizedTables, - databaseCheckConstraints, - xmlSchemaCollections, - databaseUDT, - viewsIndexes, - fullTextIndexes, - spatialIndexes, - procedures, - ] = await Promise.all([ - getDatabaseIndexes({ client, dbName, tablesInfo, logger }), - getDatabaseMemoryOptimizedTables({ client, dbName, logger }), - getDatabaseCheckConstraints({ client, dbName, allUniqueSchemasAndTables, logger }), - getDatabaseXmlSchemaCollection({ client, dbName, allUniqueSchemasAndTables, logger }), - getDatabaseUserDefinedTypes({ client, dbName, logger }), - getViewsIndexes({ client, dbName, logger }), - getFullTextIndexes({ client, dbName, allUniqueSchemasAndTables, logger }), - getSpatialIndexes({ client, dbName, allUniqueSchemasAndTables, logger }), - getDatabaseProcedures({ client, dbName, logger }), - ]); - + const rawDatabaseIndexes = await getDatabaseIndexes({ client, dbName, tablesInfo, logger }); + const databaseCheckConstraints = await getDatabaseCheckConstraints({ + client, + dbName, + allUniqueSchemasAndTables, + logger, + }); + const viewsIndexes = await getViewsIndexes({ client, dbName, logger }); + const databaseMemoryOptimizedTables = await getDatabaseMemoryOptimizedTables({ client, dbName, logger }); + const xmlSchemaCollections = await getDatabaseXmlSchemaCollection({ + client, + dbName, + allUniqueSchemasAndTables, + logger, + }); + const databaseUDT = await getDatabaseUserDefinedTypes({ client, dbName, logger }); + const fullTextIndexes = await getFullTextIndexes({ + client, + dbName, + allUniqueSchemasAndTables, + logger, + }); + const spatialIndexes = await getSpatialIndexes({ + client, + dbName, + allUniqueSchemasAndTables, + logger, + }); + const procedures = await getDatabaseProcedures({ client, dbName, logger }); const indexesBucketCount = await getIndexesBucketCount({ client, dbName, From 095056344be0ad0a51e789a115ab30b73562c375 Mon Sep 17 00:00:00 2001 From: yevhenii-moroziuk Date: Wed, 10 Jun 2026 07:49:34 +0300 Subject: [PATCH 2/5] HCK-16468: Skip memory optimized query if unsupported --- .../databaseService/databaseService.js | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/reverse_engineering/databaseService/databaseService.js b/reverse_engineering/databaseService/databaseService.js index 1c79865..7cba2f7 100644 --- a/reverse_engineering/databaseService/databaseService.js +++ b/reverse_engineering/databaseService/databaseService.js @@ -34,8 +34,8 @@ const getClient = async ({ client, dbName, meta, logger }) => { async query(...queryParams) { try { const result = await currentDbConnectionClient.query(...queryParams); - if (meta.action) { - logger.log('info', { action: meta?.action }, 'Query finished'); + if (meta?.action) { + logger.log('info', { action: meta.action }, 'Query finished'); } return result; } catch (error) { @@ -544,19 +544,42 @@ const getDatabaseMemoryOptimizedTables = async ({ client, dbName, logger }) => { logger.log('info', { message: `Get '${dbName}' database memory optimized indexes.` }, 'Reverse Engineering'); + const { isMemoryOptimizedTableSupported, isHistoryTableSupported } = await getSysTablesCatalogCapabilities({ + client, + dbName, + }); + + if (!isMemoryOptimizedTableSupported) { + return []; + } + + if (isHistoryTableSupported) { + return mapResponse( + await currentDbConnectionClient.query` + SELECT + T.name, + T.durability, + T.durability_desc, + OBJECT_NAME(T.history_table_id) AS history_table, + SCHEMA_NAME(O.schema_id) AS history_schema, + T.temporal_type_desc, + T.is_memory_optimized + FROM sys.tables T + LEFT JOIN sys.objects O + ON T.history_table_id = O.object_id + WHERE T.is_memory_optimized = 1 + `, + ); + } + return mapResponse( await currentDbConnectionClient.query` SELECT T.name, T.durability, T.durability_desc, - OBJECT_NAME(T.history_table_id) AS history_table, - SCHEMA_NAME(O.schema_id) AS history_schema, - T.temporal_type_desc, T.is_memory_optimized FROM sys.tables T - LEFT JOIN sys.objects O - ON T.history_table_id = O.object_id WHERE T.is_memory_optimized = 1 `, ); @@ -893,6 +916,24 @@ const mapResponse = async (response = Promise.resolve({})) => { return resp.recordset ? resp.recordset : resp; }; +const getSysTablesCatalogCapabilities = async ({ client, dbName }) => { + const connectionClient = await getNewConnectionClientByDb(client, dbName); + const [{ Has_HistoryTableId, Has_IsMemoryOptimized } = {}] = await mapResponse( + connectionClient.query(` + SELECT + COUNT(CASE WHEN name = 'history_table_id' THEN 1 END) AS Has_HistoryTableId, + COUNT(CASE WHEN name = 'is_memory_optimized' THEN 1 END) AS Has_IsMemoryOptimized + FROM sys.all_columns + WHERE object_id = OBJECT_ID('sys.tables') + `), + ); + + return { + isHistoryTableSupported: Boolean(Has_HistoryTableId), + isMemoryOptimizedTableSupported: Boolean(Has_IsMemoryOptimized), + }; +}; + const getDescriptionComments = async ({ client, dbName, schema, entity, logger }) => { const currentDbConnectionClient = await getClient({ client, From b644b000f1450e32c29841aeee71625ac04a7f41 Mon Sep 17 00:00:00 2001 From: yevhenii-moroziuk Date: Wed, 10 Jun 2026 11:43:00 +0300 Subject: [PATCH 3/5] HCK-16468: Fix remarks --- .../databaseService/databaseService.js | 29 ++++++++++++++++--- .../reverseEngineeringService.js | 16 +++++----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/reverse_engineering/databaseService/databaseService.js b/reverse_engineering/databaseService/databaseService.js index 7cba2f7..55daf17 100644 --- a/reverse_engineering/databaseService/databaseService.js +++ b/reverse_engineering/databaseService/databaseService.js @@ -33,10 +33,11 @@ const getClient = async ({ client, dbName, meta, logger }) => { }, async query(...queryParams) { try { + logger.log('info', { query: queryParams }, 'Performing query'); + const start = Date.now(); const result = await currentDbConnectionClient.query(...queryParams); - if (meta?.action) { - logger.log('info', { action: meta.action }, 'Query finished'); - } + const duration = Date.now() - start; + logger.log('info', { 'action': meta.action, duration: `$${duration}ms` }, 'Query executed'); return result; } catch (error) { if (meta) { @@ -269,6 +270,7 @@ const getDatabaseIndexes = async ({ client, dbName, tablesInfo, logger }) => { }); logger.log('info', { message: `Get '${dbName}' database indexes.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering table index metadata', containerName: dbName, entityName: '' }); const tableAlias = 't'; const leftJoinClauseParts = Object.entries(tablesInfo).map(([schemaName, tableNames]) => { @@ -349,6 +351,7 @@ const getSpatialIndexes = async ({ client, dbName, allUniqueSchemasAndTables, lo }); logger.log('info', { message: `Get '${dbName}' database spatial indexes.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering spatial index metadata', containerName: dbName, entityName: '' }); const tableAlias = 't'; const whereClauseParts = getWhereClauseForUniqueSchemasAndTables({ tableAlias, allUniqueSchemasAndTables }); @@ -406,6 +409,7 @@ const getFullTextIndexes = async ({ client, dbName, allUniqueSchemasAndTables, l }); logger.log('info', { message: `Get '${dbName}' database full text indexes.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering full-text index metadata', containerName: dbName, entityName: '' }); const tableAlias = 'F'; const whereClauseParts = getWhereClauseForUniqueSchemasAndTables({ tableAlias, allUniqueSchemasAndTables }); @@ -464,6 +468,7 @@ const getViewsIndexes = async ({ client, dbName, logger }) => { }); logger.log('info', { message: `Get '${dbName}' database views indexes.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering view index metadata', containerName: dbName, entityName: '' }); const tableAlias = 'ind'; @@ -542,7 +547,12 @@ const getDatabaseMemoryOptimizedTables = async ({ client, dbName, logger }) => { logger, }); - logger.log('info', { message: `Get '${dbName}' database memory optimized indexes.` }, 'Reverse Engineering'); + logger.log( + 'info', + { message: `Detecting if '${dbName}' database supports memory optimized tables.` }, + 'Reverse Engineering', + ); + logger.progress({ message: 'Discovering memory-optimized table metadata', containerName: dbName, entityName: '' }); const { isMemoryOptimizedTableSupported, isHistoryTableSupported } = await getSysTablesCatalogCapabilities({ client, @@ -550,6 +560,11 @@ const getDatabaseMemoryOptimizedTables = async ({ client, dbName, logger }) => { }); if (!isMemoryOptimizedTableSupported) { + logger.log( + 'info', + { message: `Memory optimized tables are not supported in '${dbName}' database.` }, + 'Reverse Engineering', + ); return []; } @@ -572,6 +587,8 @@ const getDatabaseMemoryOptimizedTables = async ({ client, dbName, logger }) => { ); } + logger.log('info', { message: `History tables are not supported in '${dbName}' database.` }, 'Reverse Engineering'); + return mapResponse( await currentDbConnectionClient.query` SELECT @@ -598,6 +615,7 @@ const getDatabaseCheckConstraints = async ({ client, dbName, allUniqueSchemasAnd }); logger.log('info', { message: `Get '${dbName}' database check constraints.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering check constraint metadata', containerName: dbName, entityName: '' }); const tableAlias = 't'; const whereClauseParts = getWhereClauseForUniqueSchemasAndTables({ tableAlias, allUniqueSchemasAndTables }); @@ -819,6 +837,7 @@ const getDatabaseXmlSchemaCollection = async ({ client, dbName, allUniqueSchemas }); logger.log('info', { message: `Get '${dbName}' database xml schema collection.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering XML schema collection metadata', containerName: dbName, entityName: '' }); const tableAlias = 'xcu'; const whereClauseParts = getWhereClauseForUniqueSchemasAndTables({ @@ -887,6 +906,7 @@ const getDatabaseUserDefinedTypes = async ({ client, dbName, logger }) => { }); logger.log('info', { message: `Get '${dbName}' database UDTs.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering user-defined type metadata', containerName: dbName, entityName: '' }); return mapResponse(currentDbConnectionClient.query` SELECT * FROM sys.types @@ -984,6 +1004,7 @@ const getDatabaseProcedures = async ({ client, dbName, logger }) => { }); logger.log('info', { message: `Get '${dbName}' database procedures.` }, 'Reverse Engineering'); + logger.progress({ message: 'Discovering stored procedure metadata', containerName: dbName, entityName: '' }); const response = await currentDbConnectionClient.query(` SELECT diff --git a/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js b/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js index 1330f74..8de2b9e 100644 --- a/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js +++ b/reverse_engineering/reverseEngineeringService/reverseEngineeringService.js @@ -296,14 +296,6 @@ const fetchDatabaseMetadata = async ({ client, dbName, tablesInfo, logger }) => logger, }); const viewsIndexes = await getViewsIndexes({ client, dbName, logger }); - const databaseMemoryOptimizedTables = await getDatabaseMemoryOptimizedTables({ client, dbName, logger }); - const xmlSchemaCollections = await getDatabaseXmlSchemaCollection({ - client, - dbName, - allUniqueSchemasAndTables, - logger, - }); - const databaseUDT = await getDatabaseUserDefinedTypes({ client, dbName, logger }); const fullTextIndexes = await getFullTextIndexes({ client, dbName, @@ -323,6 +315,14 @@ const fetchDatabaseMetadata = async ({ client, dbName, tablesInfo, logger }) => indexesId: rawDatabaseIndexes.map(i => i.index_id), logger, }); + const databaseUDT = await getDatabaseUserDefinedTypes({ client, dbName, logger }); + const databaseMemoryOptimizedTables = await getDatabaseMemoryOptimizedTables({ client, dbName, logger }); + const xmlSchemaCollections = await getDatabaseXmlSchemaCollection({ + client, + dbName, + allUniqueSchemasAndTables, + logger, + }); const uniqueDatabaseIndexesColumns = getUniqueIndexesColumns({ indexesColumns: rawDatabaseIndexes }); const databaseIndexes = addTotalBucketCountToDatabaseIndexes({ From 58c116184875e9b5e45451ed34f35367ce85dc1e Mon Sep 17 00:00:00 2001 From: yevhenii-moroziuk Date: Wed, 10 Jun 2026 12:11:10 +0300 Subject: [PATCH 4/5] HCK-16468: Logged normalized query --- .../databaseService/databaseService.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/reverse_engineering/databaseService/databaseService.js b/reverse_engineering/databaseService/databaseService.js index 55daf17..72f3a3f 100644 --- a/reverse_engineering/databaseService/databaseService.js +++ b/reverse_engineering/databaseService/databaseService.js @@ -9,6 +9,15 @@ const { parseProcedure } = require('../helpers/parsers/parseProcedure'); const PERMISSION_DENIED_CODE = 297; +const normalizeQueryForLogging = queryParams => + queryParams.map(param => { + if (typeof param === 'string') { + return param.replace(/\t/g, ''); + } + + return param; + }); + const addPermissionDeniedMetaData = ({ error, meta }) => { error.message = 'The user does not have permission to perform ' + @@ -33,11 +42,11 @@ const getClient = async ({ client, dbName, meta, logger }) => { }, async query(...queryParams) { try { - logger.log('info', { query: queryParams }, 'Performing query'); + logger.log('info', { query: normalizeQueryForLogging(queryParams) }, 'Performing query'); const start = Date.now(); const result = await currentDbConnectionClient.query(...queryParams); const duration = Date.now() - start; - logger.log('info', { 'action': meta.action, duration: `$${duration}ms` }, 'Query executed'); + logger.log('info', { 'action': meta.action, duration: `${duration}ms` }, 'Query executed'); return result; } catch (error) { if (meta) { From 8c77dd0e2025eafbd10d85f58e8d6ec9a5ddd1a9 Mon Sep 17 00:00:00 2001 From: yevhenii-moroziuk Date: Wed, 10 Jun 2026 12:35:13 +0300 Subject: [PATCH 5/5] HCK-16468: Improve query formatting in logs --- reverse_engineering/databaseService/databaseService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverse_engineering/databaseService/databaseService.js b/reverse_engineering/databaseService/databaseService.js index 72f3a3f..3d1f40b 100644 --- a/reverse_engineering/databaseService/databaseService.js +++ b/reverse_engineering/databaseService/databaseService.js @@ -12,7 +12,7 @@ const PERMISSION_DENIED_CODE = 297; const normalizeQueryForLogging = queryParams => queryParams.map(param => { if (typeof param === 'string') { - return param.replace(/\t/g, ''); + return param.replace(/\t/g, ' '); } return param;