Skip to content

Commit 7799376

Browse files
author
alxndrsn
committed
test/database-indexes: add easy way to log FK indexes
By changing `false` to `true`, a list of database foreign keys and their corresponding reverse indexes will be printed. Implemented while try to understand why getodk#1589 required _some_ new "acteeId" reverse-indexes, but not others. Example output: ```sh database indexes ┌── Foreign Key Reverse Indexes ───────┐ ┌─────────┬──────────────────────────────────────┬─────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────┐ │ (index) │ FK table │ foreign key │ database index │ ├─────────┼──────────────────────────────────────┼─────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────┤ │ 0 │ 'actors' │ 'actors_acteeid_foreign' │ 'idx_fk_actors_acteeid' │ │ 1 │ 'assignments' │ 'assignments_acteeid_foreign' │ 'idx_fk_assignments_acteeid' │ │ 2 │ 'assignments' │ 'assignments_actorid_foreign' │ 'assignments_pkey' │ │ 3 │ 'assignments' │ 'assignments_roleid_foreign' │ 'idx_fk_assignments_roleid' │ │ 4 │ 'audits' │ 'audits_acteeid_foreign' │ 'audits_acteeid_loggedat_index' │ │ 5 │ 'audits' │ 'audits_actorid_foreign' │ 'audits_actorid_loggedat_index' │ │ 6 │ 'client_audits' │ 'client_audits_blobid_foreign' │ 'idx_fk_client_audits_blobid' │ │ 7 │ 'comments' │ 'comments_actorid_foreign' │ 'idx_fk_comments_actorid' │ │ 8 │ 'comments' │ 'comments_submissionid_foreign' │ 'comments_submissionid_index' │ │ 9 │ 'dataset_form_defs' │ 'dataset_form_defs_datasetid_foreign' │ 'dataset_form_defs_datasetid_formdefid_unique' │ │ 10 │ 'dataset_form_defs' │ 'dataset_form_defs_formdefid_foreign' │ 'idx_fk_dataset_form_defs_formdefid' │ │ 11 │ 'datasets' │ 'datasets_projectid_foreign' │ 'idx_fk_datasets_projectid' │ │ 12 │ 'ds_properties' │ 'ds_properties_datasetid_foreign' │ 'idx_fk_ds_properties_datasetid' │ │ 13 │ 'ds_property_fields' │ 'ds_property_fields_dspropertyid_foreign' │ 'ds_property_fields_dspropertyid_formdefid_unique' │ │ 14 │ 'entities' │ 'entities_createdby_foreign' │ 'idx_fk_entities_creatorid' │ │ 15 │ 'entities' │ 'entities_datasetid_foreign' │ 'entities_datasetid_createdat_id_index' │ │ 16 │ 'entity_def_sources' │ 'entity_def_sources_auditid_foreign' │ 'idx_fk_entity_def_sources_auditid' │ │ 17 │ 'entity_def_sources' │ 'entity_def_sources_submissiondefid_foreign' │ 'idx_fk_entity_def_sources_submissiondefid' │ │ 18 │ 'entity_defs' │ 'entity_defs_entityid_foreign' │ 'entity_defs_entityid_current_index' │ │ 19 │ 'entity_defs' │ 'entity_defs_sourceid_foreign' │ 'entity_defs_sourceid_index' │ │ 20 │ 'entity_submission_backlog' │ 'fk_audit_id' │ 'idx_fk_entity_submission_backlog_auditid' │ │ 21 │ 'entity_submission_backlog' │ 'fk_submission_defs' │ 'idx_fk_entity_submission_backlog_submissiondefid' │ │ 22 │ 'entity_submission_backlog' │ 'fk_submissions' │ 'idx_fk_entity_submission_backlog_submissionid' │ │ 23 │ 'field_keys' │ 'field_keys_actorid_foreign' │ 'field_keys_pkey' │ │ 24 │ 'field_keys' │ 'field_keys_createdby_foreign' │ 'idx_fk_field_keys_createdby' │ │ 25 │ 'field_keys' │ 'field_keys_projectid_foreign' │ 'idx_fk_field_keys_projectid' │ │ 26 │ 'form_attachments' │ 'form_attachments_blobid_foreign' │ 'idx_fk_form_attachments_blobid' │ │ 27 │ 'form_attachments' │ 'form_attachments_datasetid_foreign' │ 'idx_fk_form_attachments_datasetid' │ │ 28 │ 'form_attachments' │ 'form_attachments_formdefid_foreign' │ 'form_attachments_pkey' │ │ 29 │ 'form_attachments' │ 'form_attachments_formid_foreign' │ 'form_attachments_formid_index' │ │ 30 │ 'form_defs' │ 'form_defs_formid_foreign' │ 'form_defs_formid_publishedat_index' │ │ 31 │ 'form_defs' │ 'form_defs_keyid_foreign' │ 'idx_fk_form_defs_keyid' │ │ 32 │ 'form_defs' │ 'form_defs_schemaid_foreign' │ 'idx_fk_form_defs_schemaid' │ │ 33 │ 'form_defs' │ 'form_defs_xlsblobid_foreign' │ 'idx_fk_form_defs_xlsblobid' │ │ 34 │ 'form_field_values' │ 'form_field_values_formid_foreign' │ 'form_field_values_formid_index' │ │ 35 │ 'form_field_values' │ 'form_field_values_submissiondefid_foreign' │ 'form_field_values_submissiondefid_index' │ │ 36 │ 'form_fields' │ 'form_fields_formid_foreign' │ 'form_fields_formid_path_type_index' │ │ 37 │ 'form_fields' │ 'form_fields_schemaid_foreign' │ 'form_fields_pkey' │ │ 38 │ 'forms' │ 'forms_acteeid_foreign' │ 'idx_fk_forms_acteeid' │ │ 39 │ 'forms' │ 'forms_currentdefid_foreign' │ 'idx_fk_forms_currentdefid' │ │ 40 │ 'forms' │ 'forms_draftdefid_foreign' │ 'idx_fk_forms_draftdefid' │ │ 41 │ 'forms' │ 'forms_projectid_foreign' │ 'forms_projectid_xmlformid_deletedat_unique' │ │ 42 │ 'projects' │ 'projects_keyid_foreign' │ 'idx_fk_projects_keyid' │ │ 43 │ 'public_links' │ 'public_links_actorid_foreign' │ 'public_links_pkey' │ │ 44 │ 'public_links' │ 'public_links_createdby_foreign' │ 'idx_fk_public_links_createdby' │ │ 45 │ 'public_links' │ 'public_links_formid_foreign' │ 'idx_fk_public_links_formid' │ │ 46 │ 'sessions' │ 'sessions_actorid_foreign' │ 'sessions_actorid_expires_index' │ │ 47 │ 'submission_attachments' │ 'attachments_blobid_foreign' │ 'idx_fk_submission_attachments_blobid' │ │ 48 │ 'submission_attachments' │ 'submission_attachments_submissiondefid_foreign' │ 'submission_attachments_pkey' │ │ 49 │ 'submission_defs' │ 'submission_defs_actorid_foreign' │ 'idx_fk_submission_defs_submitterid' │ │ 50 │ 'submission_defs' │ 'submission_defs_formdefid_foreign' │ 'idx_fk_submission_defs_formdefid' │ │ 51 │ 'submission_defs' │ 'submission_defs_submissionid_foreign' │ 'submission_defs_submissionid_current_index' │ │ 52 │ 'submission_field_extract_geo_cache' │ 'submission_field_extract_geo_cache_submission_def_id_fkey' │ 'submission_field_extract_geo_cache_pkey' │ │ 53 │ 'submissions' │ 'submissions_formid_foreign' │ 'submissions_formid_instanceid_draft_unique' │ │ 54 │ 'submissions' │ 'submissions_submitter_foreign' │ 'idx_fk_submissions_submitterid' │ │ 55 │ 'user_project_preferences' │ 'user_project_preferences_projectId_fkey' │ 'idx_fk_user_project_preferences_projectid' │ │ 56 │ 'user_project_preferences' │ 'user_project_preferences_userId_fkey' │ 'user_project_preferences_primary_key' │ │ 57 │ 'user_site_preferences' │ 'user_site_preferences_userId_fkey' │ 'user_site_preferences_primary_key' │ │ 58 │ 'users' │ 'users_actorid_foreign' │ 'users_pkey' │ └─────────┴──────────────────────────────────────┴─────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────┘ ```
1 parent 7739129 commit 7799376

File tree

1 file changed

+28
-3
lines changed

1 file changed

+28
-3
lines changed

test/integration/other/database-indexes.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { sql } = require('slonik');
2-
const { curry, equals, slice } = require('ramda');
2+
const { ascend, curry, equals, prop, slice, sortWith } = require('ramda');
33

44
const { testContainer } = require('../setup');
55

@@ -9,7 +9,7 @@ const idxNameFor = fk => `idx_fk_${fk.local_tbl_name}_${fk.local_col_names}`;
99
const colsForIdx = fk => fk.local_col_names.map(col => `"${col}"`).join(', ');
1010
const createIdxStatement = fk => `CREATE UNIQUE? INDEX ${idxNameFor(fk)} ON "${fk.local_tbl_name}" (${colsForIdx(fk)});`;
1111

12-
describe('database indexes', () => {
12+
describe.only('database indexes', () => {
1313
it('should define indexes on both sides of foreign key relationships', testContainer(async ({ all }) => {
1414
const existingIndexes = await all(sql`
1515
SELECT tbl_class.relnamespace::regnamespace::text AS tbl_schema
@@ -36,16 +36,37 @@ describe('database indexes', () => {
3636
WHERE pg_constraint.contype = 'f'
3737
`);
3838

39+
const fkIndexes = [];
3940
const missingIndexes = foreignKeys
4041
.filter(fk => {
4142
const colsMatch = startsWith(fk.local_col_indexes);
42-
return !existingIndexes.find(idx => { // eslint-disable-line arrow-body-style
43+
const foundIdx = existingIndexes.find(idx => { // eslint-disable-line arrow-body-style
4344
return idx.tbl_schema === fk.local_tbl_schema &&
4445
idx.tbl_name === fk.local_tbl_name && // eslint-disable-line no-multi-spaces
4546
colsMatch(idx.indexed_columns);
4647
});
48+
if (!foundIdx) return true;
49+
50+
fkIndexes.push({
51+
'FK table': fk.local_tbl_name,
52+
'foreign key': fk.fk_name,
53+
'database index': foundIdx.idx_name,
54+
});
55+
return false;
4756
});
4857

58+
const envVarName = 'DEBUG_FK_INDEXES'
59+
if (process.env[envVarName]) {
60+
console.log('\n┌── Foreign Key Reverse Indexes ───────┐'); // eslint-disable-line no-console
61+
console.table( // eslint-disable-line no-console
62+
sortWith([
63+
ascend(prop('FK table')),
64+
ascend(prop('foreign key')),
65+
ascend(prop('database index')),
66+
])(fkIndexes),
67+
);
68+
}
69+
4970
await Promise.all(missingIndexes
5071
.map(async fk => {
5172
const cols = await all(sql`
@@ -67,6 +88,10 @@ describe('database indexes', () => {
6788
Or: a database migration should be added with the following indexes:
6889
6990
${missingIndexes.map(createIdxStatement).sort().join('\n ')}
91+
92+
To see existing indexes, run:
93+
94+
${envVarName}=1 npx mocha ${__filename}
7095
`);
7196
}));
7297
});

0 commit comments

Comments
 (0)