Skip to content

Commit dd5077c

Browse files
authored
HCK-12924: fix getting table columns meta-info without "select" permission (#159)
* add fallback query to get table columns without select permission * fix getting foreign columns without select permission * fix mapping interval mod from pg catalogs * use tolerant query * add comment
1 parent 2bbb259 commit dd5077c

File tree

3 files changed

+248
-15
lines changed

3 files changed

+248
-15
lines changed

reverse_engineering/helpers/postgresHelpers/viewHelper.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const removeViewNameSuffix = name => name.slice(0, -VIEW_SUFFIX.length);
1111
const setViewSuffix = name => `${name}${VIEW_SUFFIX}`;
1212

1313
const generateCreateViewScript = (viewName, viewData, viewDefinitionFallback = {}) => {
14-
const selectStatement = _.trim(viewData.view_definition || viewDefinitionFallback.definition || '');
14+
const selectStatement = _.trim(viewData?.view_definition || viewDefinitionFallback.definition || '');
1515

1616
if (!selectStatement) {
1717
return '';
@@ -20,13 +20,13 @@ const generateCreateViewScript = (viewName, viewData, viewDefinitionFallback = {
2020
return `CREATE VIEW ${wrapInQuotes(viewName)} AS ${selectStatement}`;
2121
};
2222

23-
const prepareViewData = (viewData, viewOptions, triggers, tableToastOptions) => {
23+
const prepareViewData = ({ viewData, viewOptions, triggers, tableToastOptions, isRecursive }) => {
2424
const data = {
25-
withCheckOption: viewData.check_option !== 'NONE' || _.isNil(viewData.check_option),
26-
checkTestingScope: getCheckTestingScope(viewData.check_option),
25+
withCheckOption: viewData?.check_option !== 'NONE' || _.isNil(viewData?.check_option),
26+
checkTestingScope: getCheckTestingScope(viewData?.check_option),
2727
viewOptions: _.fromPairs(_.map(viewOptions?.view_options, splitByEqualitySymbol)),
2828
temporary: viewOptions?.persistence === 't',
29-
recursive: isViewRecursive(viewData),
29+
recursive: isRecursive,
3030
description: viewOptions?.description,
3131
triggers,
3232
...prepareMaterializedViewData({ viewData, viewOptions, tableToastOptions }),
@@ -36,9 +36,9 @@ const prepareViewData = (viewData, viewOptions, triggers, tableToastOptions) =>
3636

3737
const prepareMaterializedViewData = ({ viewData, viewOptions, tableToastOptions }) => {
3838
return {
39-
...(viewData.table_type && { materialized: viewData.table_type === TABLE_TYPE.materializedView }),
40-
...(viewData.view_tablespace_name && { view_tablespace_name: viewData.view_tablespace_name }),
41-
...(viewData.is_populated && { withDataOption: viewData.is_populated }),
39+
...(viewData?.table_type && { materialized: viewData.table_type === TABLE_TYPE.materializedView }),
40+
...(viewData?.view_tablespace_name && { view_tablespace_name: viewData.view_tablespace_name }),
41+
...(viewData?.is_populated && { withDataOption: viewData.is_populated }),
4242
...(viewOptions?.view_options && {
4343
storage_parameter: prepareStorageParameters(viewOptions.view_options, tableToastOptions),
4444
}),
@@ -53,8 +53,8 @@ const getCheckTestingScope = check_option => {
5353
return check_option;
5454
};
5555

56-
const isViewRecursive = viewData => {
57-
return _.startsWith(_.trim(viewData.view_definition), 'WITH RECURSIVE');
56+
const isViewRecursive = viewDefinition => {
57+
return _.startsWith(_.trim(viewDefinition), 'WITH RECURSIVE');
5858
};
5959

6060
const splitByEqualitySymbol = item => _.split(item, '=');
@@ -66,4 +66,5 @@ module.exports = {
6666
generateCreateViewScript,
6767
setViewSuffix,
6868
prepareViewData,
69+
isViewRecursive,
6970
};

reverse_engineering/helpers/postgresService.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const {
2323
generateCreateViewScript,
2424
setViewSuffix,
2525
prepareViewData,
26+
isViewRecursive,
2627
} = require('./postgresHelpers/viewHelper');
2728
const { getTriggers } = require('./postgresHelpers/triggerHelper');
2829
const queryConstants = require('./queryConstants');
@@ -329,6 +330,18 @@ module.exports = {
329330
logger.progress('Get columns', schemaName, tableName);
330331

331332
const tableColumns = await db.query(queryConstants.GET_TABLE_COLUMNS, [tableName, schemaName]);
333+
334+
// If information_schema.columns returns empty results, it might be due to permission issues
335+
// Fall back to pg_catalog tables which are more accessible
336+
if (tableColumns.length === 0) {
337+
logger.info('No columns returned from information_schema.columns, falling back to pg_catalog tables', {
338+
schemaName,
339+
tableName,
340+
});
341+
342+
return await this._getTableColumnsFromCatalog(tableName, schemaName, tableOid);
343+
}
344+
332345
const tableColumnsAdditionalData = await db.queryTolerant(queryConstants.GET_TABLE_COLUMNS_ADDITIONAL_DATA, [
333346
tableOid,
334347
]);
@@ -341,6 +354,12 @@ module.exports = {
341354
});
342355
},
343356

357+
async _getTableColumnsFromCatalog(tableName, schemaName, tableOid) {
358+
logger.progress('Get columns from pg_catalog', schemaName, tableName);
359+
360+
return await db.queryTolerant(queryConstants.GET_TABLE_COLUMNS_FROM_CATALOG, [tableOid]);
361+
},
362+
344363
async _getDocuments(schemaName, tableName, attributes, recordSamplingSettings) {
345364
logger.progress('Sampling table', schemaName, tableName);
346365

@@ -377,7 +396,7 @@ module.exports = {
377396
(await db.query(queryConstants.GET_VIEW_DATA, [viewName, schemaName], true)) ??
378397
(await db.query(queryConstants.GET_MATERIALIZED_VIEW_DATA, [viewName, schemaName], true));
379398
const viewDefinitionFallback =
380-
!viewData.view_definition &&
399+
!viewData?.view_definition &&
381400
(await db.queryTolerant(queryConstants.GET_VIEW_SELECT_STMT_FALLBACK, [viewName, schemaName], true));
382401
const viewOptions = await db.queryTolerant(queryConstants.GET_VIEW_OPTIONS, [viewName, schemaOid], true);
383402
const triggers = await this._getTriggers(
@@ -394,7 +413,8 @@ module.exports = {
394413
);
395414

396415
const script = generateCreateViewScript(viewName, viewData, viewDefinitionFallback);
397-
const data = prepareViewData(viewData, viewOptions, triggers, tableToastOptions);
416+
const isRecursive = isViewRecursive(viewData?.view_definition || viewDefinitionFallback);
417+
const data = prepareViewData({ viewData, viewOptions, triggers, tableToastOptions, isRecursive });
398418

399419
if (!script) {
400420
logger.info('View select statement was not retrieved', { schemaName, viewName });

0 commit comments

Comments
 (0)