=== Applying patches on top of PostgreSQL commit ID 25dc4850747bb12e871af3589736463edd1d2aa6 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Wed Jan 21 23:53:20 UTC 2026 On branch cf/6326 nothing to commit, working tree clean === using 'git am' to apply patch ./v4-0001-pg_stat_statements-Move-query-normalization-to-co.patch === Applying: pg_stat_statements: Move query normalization to core Using index info to reconstruct a base tree... M contrib/pg_stat_statements/pg_stat_statements.c M src/backend/nodes/queryjumblefuncs.c M src/include/nodes/queryjumble.h Falling back to patching base and 3-way merge... Auto-merging src/include/nodes/queryjumble.h Auto-merging src/backend/nodes/queryjumblefuncs.c Auto-merging contrib/pg_stat_statements/pg_stat_statements.c CONFLICT (content): Merge conflict in contrib/pg_stat_statements/pg_stat_statements.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 pg_stat_statements: Move query normalization to core When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". Unstaged changes after reset: M contrib/pg_stat_statements/pg_stat_statements.c M src/backend/nodes/queryjumblefuncs.c M src/include/nodes/queryjumble.h === using patch(1) to apply patch ./v4-0001-pg_stat_statements-Move-query-normalization-to-co.patch === patching file contrib/pg_stat_statements/pg_stat_statements.c Hunk #5 FAILED at 2823. 1 out of 5 hunks FAILED -- saving rejects to file contrib/pg_stat_statements/pg_stat_statements.c.rej patching file src/backend/nodes/queryjumblefuncs.c patching file src/include/nodes/queryjumble.h Unstaged changes after reset: M contrib/pg_stat_statements/pg_stat_statements.c M src/backend/nodes/queryjumblefuncs.c M src/include/nodes/queryjumble.h Removing contrib/pg_stat_statements/pg_stat_statements.c.rej === using 'git apply' to apply patch ./v4-0001-pg_stat_statements-Move-query-normalization-to-co.patch === Applied patch to 'contrib/pg_stat_statements/pg_stat_statements.c' with conflicts. Applied patch to 'src/backend/nodes/queryjumblefuncs.c' cleanly. Applied patch to 'src/include/nodes/queryjumble.h' cleanly. U contrib/pg_stat_statements/pg_stat_statements.c diff --cc contrib/pg_stat_statements/pg_stat_statements.c index 4a427533bd8,b90891859d9..00000000000 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@@ -2823,256 -2823,3 +2823,259 @@@ release_lock return stats_reset; } ++<<<<<<< ours + +/* + * Generate a normalized version of the query string that will be used to + * represent all similar queries. + * + * Note that the normalized representation may well vary depending on + * just which "equivalent" query is used to create the hashtable entry. + * We assume this is OK. + * + * If query_loc > 0, then "query" has been advanced by that much compared to + * the original string start, so we need to translate the provided locations + * to compensate. (This lets us avoid re-scanning statements before the one + * of interest, so it's worth doing.) + * + * *query_len_p contains the input string length, and is updated with + * the result string length on exit. The resulting string might be longer + * or shorter depending on what happens with replacement of constants. + * + * Returns a palloc'd string. + */ +static char * +generate_normalized_query(JumbleState *jstate, const char *query, + int query_loc, int *query_len_p) +{ + char *norm_query; + int query_len = *query_len_p; + int norm_query_buflen, /* Space allowed for norm_query */ + len_to_wrt, /* Length (in bytes) to write */ + quer_loc = 0, /* Source query byte location */ + n_quer_loc = 0, /* Normalized query byte location */ + last_off = 0, /* Offset from start for previous tok */ + last_tok_len = 0; /* Length (in bytes) of that tok */ + int num_constants_replaced = 0; + + /* + * Get constants' lengths (core system only gives us locations). Note + * this also ensures the items are sorted by location. + */ + fill_in_constant_lengths(jstate, query, query_loc); + + /* + * Allow for $n symbols to be longer than the constants they replace. + * Constants must take at least one byte in text form, while a $n symbol + * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We + * could refine that limit based on the max value of n for the current + * query, but it hardly seems worth any extra effort to do so. + */ + norm_query_buflen = query_len + jstate->clocations_count * 10; + + /* Allocate result buffer */ + norm_query = palloc(norm_query_buflen + 1); + + for (int i = 0; i < jstate->clocations_count; i++) + { + int off, /* Offset from start for cur tok */ + tok_len; /* Length (in bytes) of that tok */ + + /* + * If we have an external param at this location, but no lists are + * being squashed across the query, then we skip here; this will make + * us print the characters found in the original query that represent + * the parameter in the next iteration (or after the loop is done), + * which is a bit odd but seems to work okay in most cases. + */ + if (jstate->clocations[i].extern_param && !jstate->has_squashed_lists) + continue; + + off = jstate->clocations[i].location; + + /* Adjust recorded location if we're dealing with partial string */ + off -= query_loc; + + tok_len = jstate->clocations[i].length; + + if (tok_len < 0) + continue; /* ignore any duplicates */ + + /* Copy next chunk (what precedes the next constant) */ + len_to_wrt = off - last_off; + len_to_wrt -= last_tok_len; + Assert(len_to_wrt >= 0); + memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt); + n_quer_loc += len_to_wrt; + + /* + * And insert a param symbol in place of the constant token; and, if + * we have a squashable list, insert a placeholder comment starting + * from the list's second value. + */ + n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d%s", + num_constants_replaced + 1 + jstate->highest_extern_param_id, + jstate->clocations[i].squashed ? " /*, ... */" : ""); + num_constants_replaced++; + + /* move forward */ + quer_loc = off + tok_len; + last_off = off; + last_tok_len = tok_len; + } + + /* + * We've copied up until the last ignorable constant. Copy over the + * remaining bytes of the original query string. + */ + len_to_wrt = query_len - quer_loc; + + Assert(len_to_wrt >= 0); + memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt); + n_quer_loc += len_to_wrt; + + Assert(n_quer_loc <= norm_query_buflen); + norm_query[n_quer_loc] = '\0'; + + *query_len_p = n_quer_loc; + return norm_query; +} + +/* + * Given a valid SQL string and an array of constant-location records, + * fill in the textual lengths of those constants. + * + * The constants may use any allowed constant syntax, such as float literals, + * bit-strings, single-quoted strings and dollar-quoted strings. This is + * accomplished by using the public API for the core scanner. + * + * It is the caller's job to ensure that the string is a valid SQL statement + * with constants at the indicated locations. Since in practice the string + * has already been parsed, and the locations that the caller provides will + * have originated from within the authoritative parser, this should not be + * a problem. + * + * Multiple constants can have the same location. We reset lengths of those + * past the first to -1 so that they can later be ignored. + * + * If query_loc > 0, then "query" has been advanced by that much compared to + * the original string start, so we need to translate the provided locations + * to compensate. (This lets us avoid re-scanning statements before the one + * of interest, so it's worth doing.) + * + * N.B. There is an assumption that a '-' character at a Const location begins + * a negative numeric constant. This precludes there ever being another + * reason for a constant to start with a '-'. + */ +static void +fill_in_constant_lengths(JumbleState *jstate, const char *query, + int query_loc) +{ + LocationLen *locs; + core_yyscan_t yyscanner; + core_yy_extra_type yyextra; + core_YYSTYPE yylval; + YYLTYPE yylloc; + + /* + * Sort the records by location so that we can process them in order while + * scanning the query text. + */ + if (jstate->clocations_count > 1) + qsort(jstate->clocations, jstate->clocations_count, + sizeof(LocationLen), comp_location); + locs = jstate->clocations; + + /* initialize the flex scanner --- should match raw_parser() */ + yyscanner = scanner_init(query, + &yyextra, + &ScanKeywords, + ScanKeywordTokens); + + /* Search for each constant, in sequence */ + for (int i = 0; i < jstate->clocations_count; i++) + { + int loc; + int tok; + + /* Ignore constants after the first one in the same location */ + if (i > 0 && locs[i].location == locs[i - 1].location) + { + locs[i].length = -1; + continue; + } + + if (locs[i].squashed) + continue; /* squashable list, ignore */ + + /* Adjust recorded location if we're dealing with partial string */ + loc = locs[i].location - query_loc; + Assert(loc >= 0); + + /* + * We have a valid location for a constant that's not a dupe. Lex + * tokens until we find the desired constant. + */ + for (;;) + { + tok = core_yylex(&yylval, &yylloc, yyscanner); + + /* We should not hit end-of-string, but if we do, behave sanely */ + if (tok == 0) + break; /* out of inner for-loop */ + + /* + * We should find the token position exactly, but if we somehow + * run past it, work with that. + */ + if (yylloc >= loc) + { + if (query[loc] == '-') + { + /* + * It's a negative value - this is the one and only case + * where we replace more than a single token. + * + * Do not compensate for the core system's special-case + * adjustment of location to that of the leading '-' + * operator in the event of a negative constant. It is + * also useful for our purposes to start from the minus + * symbol. In this way, queries like "select * from foo + * where bar = 1" and "select * from foo where bar = -2" + * will have identical normalized query strings. + */ + tok = core_yylex(&yylval, &yylloc, yyscanner); + if (tok == 0) + break; /* out of inner for-loop */ + } + + /* + * We now rely on the assumption that flex has placed a zero + * byte after the text of the current token in scanbuf. + */ + locs[i].length = strlen(yyextra.scanbuf + loc); + break; /* out of inner for-loop */ + } + } + + /* If we hit end-of-string, give up, leaving remaining lengths -1 */ + if (tok == 0) + break; + } + + scanner_finish(yyscanner); +} + +/* + * comp_location: comparator for qsorting LocationLen structs by location + */ +static int +comp_location(const void *a, const void *b) +{ + int l = ((const LocationLen *) a)->location; + int r = ((const LocationLen *) b)->location; + + return pg_cmp_s32(l, r); +} ++======= ++>>>>>>> theirs