Skip to content

Normalize random table names during fingerprinting - #386

Draft
duckinator wants to merge 2 commits into
pganalyze:18-latestfrom
duckinator:gh335b
Draft

duckinator wants to merge 2 commits into
pganalyze:18-latestfrom
duckinator:gh335b

Conversation

@duckinator

@duckinator duckinator commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

When fingerprinting relation names, ignore:

  1. segments that are >=2 characters long and all numbers.
  2. segments that are >=4 characters long with at least one digit.

The previous behavior is exclusively 1.

The purpose for 2 is to remove "random looking" sections.

This definition of "random looking" probably should be refined.

It was originally ">=2 characters long with at least one digit", but that broke existing tests using a table name "ft1".

Setting fingerprint options to PG_QUERY_FINGERPRINT_FULL_RELNAME causes the relname to be fingerprinted verbatim.

Comment thread test/fingerprint_tests.c Resolved
Comment thread src/pg_query_fingerprint.c Resolved
Comment thread test/fingerprint_tests.c
"SELECT * FROM _temp__2577316833_vk96nrgo_users",
"167db20b43982916",
"SELECT * FROM _temp__2577316835_vk9nrgo_users",
"167db20b43982916",

@seanlinsley seanlinsley Oct 1, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a list of table names we can test against. (Click "Details" to see the list)

Some issues with the current approach:

  • tenk1, tenk2, backup2024 all become an empty string
  • oauth1_tokens and oauth2_tokens both become tokens

It might help with debugging various approaches in this PR if we had debug output printing the normalized relname that fingerprinting uses.

Details
# django-bulk-load
loading_books_dc786da1fead11f
loading_books_dc786da1fead11g
loading_books_8f14e45fceea16f
_temp__2577316832_vk95nrgo_users
_temp__2577316833_vk96nrgo_users
_temp__2577316835_vk9nrgo_users

# date/time partitions
orders_2024_01
orders_2024_02
events_20231231
calls_p20240301
calls_p20240302
default_partition

# versioned identifiers
oauth1_tokens
oauth2_tokens
backup2024
backup2025
api_v1
api_v2
pump_9000
pump_9001

# multi-tenant/shard ids
tenant1001_users
tenant1002_users
shard_4_accounts

# table names from the vendored pg regression suite
a
a_star
a_temp_tbl
ab
ab1c
abbrev_abort_uuids
ac_test
agg_data_20k
agg_data_2k
alter1
alter_idx_rename_test
alter_idx_rename_test_parted
alter_table_under_transition_tables
alterlock2
am_partitioned_5p1
am_partitioned_6p1
array_index_op_test
as_select1
at_tab1
attach_parted_part1
big5_inputs
bug18634s
bug6051_2
bug6051_3
case2_tbl
check_ign_err2
child_0_10
child_10_20
collate_dep_test4t
concur_reindex_part_0_1
concur_reindex_part_10
ctl_stats3_child
ctl_stats3_parent
fk_partitioned_pk_61
fkpart13_t2_p1
fkpart13_t2_p2
iso8859_5_inputs
join_pt1p1
join_pt1p1p1
join_pt1p2
kd_point_tbl_ord_idx1
kd_point_tbl_ord_idx2
lock_tbl1a
lparted_by_int2_1
lparted_by_int2_16384
mc2p0
mc2p1
mcrparted1_lt_b
mcrparted3_c_to_common
mcrparted4_common_lt_0
mcrparted5_common_0_to_10
mcrparted6_common_ge_10
mcrparted7_gt_common_lt_d
mcrparted8_ge_d
noinh_con_copy1_parted
notnull_part1_1_upg
notnull_part1_2_upg
num_exp_power_10_ln
p1_c1c2
p1p2_c1
p1p2_c2
part5_def_p1
part_40_inf_ab
part_40_inf_cd
part_a_10_a_20
part_a_1_a_10
part_column_drop_1_10
parted2_stmt_trig1
pitest1_p1_like
plt1_adv_p1_null
plt2_adv_p1_null
plt2_adv_p2_1
plt2_adv_p2_2
pp_intrangepart2inf
rangep_0_to_100
rangep_0_to_100_1
rangep_0_to_100_2
rangep_100_to_200
rf_tbl_abcd_part_pk_1
rparted_by_int2_maxvalue
temporal_fk2_mltrng2mltrng
temporal_fk2_rng2rng
temporal_partitioned_fk_mltrng2mltrng
temporal_partitioned_fk_rng2rng
tenk1
tenk2
testpub_insert_onconfl_part_no_ri

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we change the code to drop a segment only when it matches a generator signature (ASCII-only, pure-hex 6+ chars, or digits in a segment with letters on both sides), rather than any segment with any digit. That should be a better fit since meaningful names commonly append numbers at the end.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm realizing that, since 2+ char long sequences of digits are dropped in both the old and new variant, that'd include stuff such as...

10, 20, 40 (but not 0) in:

mcrparted5_common_0_to_10
mcrparted6_common_ge_10
num_exp_power_10_ln
part_40_inf_ab
part_40_inf_cd
part_a_10_a_20

100 and 200 (but not 0, 1, or 2) in:

rangep_0_to_100
rangep_0_to_100_1
rangep_0_to_100_2
rangep_100_to_200

Is this the behavior we want moving forward? I imagine there'd be backwards-compatibility concerns if we changed it.

@duckinator
duckinator force-pushed the gh335b branch 2 times, most recently from e6aa8e5 to 66d7735 Compare October 1, 2026 21:49
When fingerprinting relation names, this PR makes libpg_query:
1. ignore segments that are >=2 characters and all digits;
2. ignore segments that are >2 characters, start and end with a letter,
   and contain at least one digit in the middle.

The existing behavior is exclusively 1.

The purpose for 2 is to remove "random looking" sections. The rationale
is that, usually, if if a segment is meaningful numbers are appeneded to
the end.

Setting fingerprint options to PG_QUERY_FINGERPRINT_FULL_RELNAME causes
the relname to be fingerprinted verbatim.
@duckinator

duckinator commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

It increasingly seems like we might want 2 additional kinds of test for fingerprints:

  1. tests to verify that a relvar is normalized as expected (e.g., "plt2_adv_p2_2" stays the same, but plt2_adv_p2_22 => plt2_adv_p2_)
  2. tests to verify that two relvars have the same (e.g., orders_2024_01 and orders_2024_02) or different (calls_p20240301 and calls_p20240302).

@seanlinsley @lfittl do you agree? and should i include that in this PR? i suspect it might actually speed up my work to add this, because it's currently a lot of chasing edge cases without a mechanism to add tests for the actual edge cases i find.

EDIT: I went ahead with this, since writing it out made it very clear it was needed. We can figure out details like naming and such later.

@duckinator

Copy link
Copy Markdown
Contributor Author

In practice the test framework I added for summary stuff can handle this just fine, so I'd just use that. But it means a third file full of fingerprint tests (fingerprint_tests.c, fingerprint_opts_tests.c, and whatever this winds up being called).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants