|
36 | 36 | #include "commands/tablespace.h"
|
37 | 37 | #include "executor/spi.h"
|
38 | 38 | #include "funcapi.h"
|
| 39 | +#include "mb/pg_wchar.h" |
39 | 40 | #include "miscadmin.h"
|
40 | 41 | #include "nodes/makefuncs.h"
|
41 | 42 | #include "nodes/nodeFuncs.h"
|
|
52 | 53 | #include "utils/array.h"
|
53 | 54 | #include "utils/builtins.h"
|
54 | 55 | #include "utils/fmgroids.h"
|
| 56 | +#include "utils/hsearch.h" |
55 | 57 | #include "utils/lsyscache.h"
|
56 | 58 | #include "utils/rel.h"
|
57 | 59 | #include "utils/syscache.h"
|
@@ -260,6 +262,15 @@ typedef struct
|
260 | 262 | #define deparse_columns_fetch(rangetable_index, dpns) \
|
261 | 263 | ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
|
262 | 264 |
|
| 265 | +/* |
| 266 | + * Entry in set_rtable_names' hash table |
| 267 | + */ |
| 268 | +typedef struct |
| 269 | +{ |
| 270 | + char name[NAMEDATALEN]; /* Hash key --- must be first */ |
| 271 | + int counter; /* Largest addition used so far for name */ |
| 272 | +} NameHashEntry; |
| 273 | + |
263 | 274 |
|
264 | 275 | /* ----------
|
265 | 276 | * Global data
|
@@ -304,8 +315,6 @@ static int print_function_arguments(StringInfo buf, HeapTuple proctup,
|
304 | 315 | static void print_function_rettype(StringInfo buf, HeapTuple proctup);
|
305 | 316 | static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
306 | 317 | Bitmapset *rels_used);
|
307 |
| -static bool refname_is_unique(char *refname, deparse_namespace *dpns, |
308 |
| - List *parent_namespaces); |
309 | 318 | static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
|
310 | 319 | List *parent_namespaces);
|
311 | 320 | static void set_simple_column_names(deparse_namespace *dpns);
|
@@ -2491,15 +2500,61 @@ static void
|
2491 | 2500 | set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
2492 | 2501 | Bitmapset *rels_used)
|
2493 | 2502 | {
|
| 2503 | + HASHCTL hash_ctl; |
| 2504 | + HTAB *names_hash; |
| 2505 | + NameHashEntry *hentry; |
| 2506 | + bool found; |
| 2507 | + int rtindex; |
2494 | 2508 | ListCell *lc;
|
2495 |
| - int rtindex = 1; |
2496 | 2509 |
|
2497 | 2510 | dpns->rtable_names = NIL;
|
| 2511 | + /* nothing more to do if empty rtable */ |
| 2512 | + if (dpns->rtable == NIL) |
| 2513 | + return; |
| 2514 | + |
| 2515 | + /* |
| 2516 | + * We use a hash table to hold known names, so that this process is O(N) |
| 2517 | + * not O(N^2) for N names. |
| 2518 | + */ |
| 2519 | + MemSet(&hash_ctl, 0, sizeof(hash_ctl)); |
| 2520 | + hash_ctl.keysize = NAMEDATALEN; |
| 2521 | + hash_ctl.entrysize = sizeof(NameHashEntry); |
| 2522 | + hash_ctl.hcxt = CurrentMemoryContext; |
| 2523 | + names_hash = hash_create("set_rtable_names names", |
| 2524 | + list_length(dpns->rtable), |
| 2525 | + &hash_ctl, |
| 2526 | + HASH_ELEM | HASH_CONTEXT); |
| 2527 | + /* Preload the hash table with names appearing in parent_namespaces */ |
| 2528 | + foreach(lc, parent_namespaces) |
| 2529 | + { |
| 2530 | + deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); |
| 2531 | + ListCell *lc2; |
| 2532 | + |
| 2533 | + foreach(lc2, olddpns->rtable_names) |
| 2534 | + { |
| 2535 | + char *oldname = (char *) lfirst(lc2); |
| 2536 | + |
| 2537 | + if (oldname == NULL) |
| 2538 | + continue; |
| 2539 | + hentry = (NameHashEntry *) hash_search(names_hash, |
| 2540 | + oldname, |
| 2541 | + HASH_ENTER, |
| 2542 | + &found); |
| 2543 | + /* we do not complain about duplicate names in parent namespaces */ |
| 2544 | + hentry->counter = 0; |
| 2545 | + } |
| 2546 | + } |
| 2547 | + |
| 2548 | + /* Now we can scan the rtable */ |
| 2549 | + rtindex = 1; |
2498 | 2550 | foreach(lc, dpns->rtable)
|
2499 | 2551 | {
|
2500 | 2552 | RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
2501 | 2553 | char *refname;
|
2502 | 2554 |
|
| 2555 | + /* Just in case this takes an unreasonable amount of time ... */ |
| 2556 | + CHECK_FOR_INTERRUPTS(); |
| 2557 | + |
2503 | 2558 | if (rels_used && !bms_is_member(rtindex, rels_used))
|
2504 | 2559 | {
|
2505 | 2560 | /* Ignore unreferenced RTE */
|
@@ -2527,56 +2582,62 @@ set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
2527 | 2582 | }
|
2528 | 2583 |
|
2529 | 2584 | /*
|
2530 |
| - * If the selected name isn't unique, append digits to make it so |
| 2585 | + * If the selected name isn't unique, append digits to make it so, and |
| 2586 | + * make a new hash entry for it once we've got a unique name. For a |
| 2587 | + * very long input name, we might have to truncate to stay within |
| 2588 | + * NAMEDATALEN. |
2531 | 2589 | */
|
2532 |
| - if (refname && |
2533 |
| - !refname_is_unique(refname, dpns, parent_namespaces)) |
| 2590 | + if (refname) |
2534 | 2591 | {
|
2535 |
| - char *modname = (char *) palloc(strlen(refname) + 32); |
2536 |
| - int i = 0; |
| 2592 | + hentry = (NameHashEntry *) hash_search(names_hash, |
| 2593 | + refname, |
| 2594 | + HASH_ENTER, |
| 2595 | + &found); |
| 2596 | + if (found) |
| 2597 | + { |
| 2598 | + /* Name already in use, must choose a new one */ |
| 2599 | + int refnamelen = strlen(refname); |
| 2600 | + char *modname = (char *) palloc(refnamelen + 16); |
| 2601 | + NameHashEntry *hentry2; |
2537 | 2602 |
|
2538 |
| - do |
| 2603 | + do |
| 2604 | + { |
| 2605 | + hentry->counter++; |
| 2606 | + for (;;) |
| 2607 | + { |
| 2608 | + /* |
| 2609 | + * We avoid using %.*s here because it can misbehave |
| 2610 | + * if the data is not valid in what libc thinks is the |
| 2611 | + * prevailing encoding. |
| 2612 | + */ |
| 2613 | + memcpy(modname, refname, refnamelen); |
| 2614 | + sprintf(modname + refnamelen, "_%d", hentry->counter); |
| 2615 | + if (strlen(modname) < NAMEDATALEN) |
| 2616 | + break; |
| 2617 | + /* drop chars from refname to keep all the digits */ |
| 2618 | + refnamelen = pg_mbcliplen(refname, refnamelen, |
| 2619 | + refnamelen - 1); |
| 2620 | + } |
| 2621 | + hentry2 = (NameHashEntry *) hash_search(names_hash, |
| 2622 | + modname, |
| 2623 | + HASH_ENTER, |
| 2624 | + &found); |
| 2625 | + } while (found); |
| 2626 | + hentry2->counter = 0; /* init new hash entry */ |
| 2627 | + refname = modname; |
| 2628 | + } |
| 2629 | + else |
2539 | 2630 | {
|
2540 |
| - sprintf(modname, "%s_%d", refname, ++i); |
2541 |
| - } while (!refname_is_unique(modname, dpns, parent_namespaces)); |
2542 |
| - refname = modname; |
| 2631 | + /* Name not previously used, need only initialize hentry */ |
| 2632 | + hentry->counter = 0; |
| 2633 | + } |
2543 | 2634 | }
|
2544 | 2635 |
|
2545 | 2636 | dpns->rtable_names = lappend(dpns->rtable_names, refname);
|
2546 | 2637 | rtindex++;
|
2547 | 2638 | }
|
2548 |
| -} |
2549 |
| - |
2550 |
| -/* |
2551 |
| - * refname_is_unique: is refname distinct from all already-chosen RTE names? |
2552 |
| - */ |
2553 |
| -static bool |
2554 |
| -refname_is_unique(char *refname, deparse_namespace *dpns, |
2555 |
| - List *parent_namespaces) |
2556 |
| -{ |
2557 |
| - ListCell *lc; |
2558 | 2639 |
|
2559 |
| - foreach(lc, dpns->rtable_names) |
2560 |
| - { |
2561 |
| - char *oldname = (char *) lfirst(lc); |
2562 |
| - |
2563 |
| - if (oldname && strcmp(oldname, refname) == 0) |
2564 |
| - return false; |
2565 |
| - } |
2566 |
| - foreach(lc, parent_namespaces) |
2567 |
| - { |
2568 |
| - deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); |
2569 |
| - ListCell *lc2; |
2570 |
| - |
2571 |
| - foreach(lc2, olddpns->rtable_names) |
2572 |
| - { |
2573 |
| - char *oldname = (char *) lfirst(lc2); |
2574 |
| - |
2575 |
| - if (oldname && strcmp(oldname, refname) == 0) |
2576 |
| - return false; |
2577 |
| - } |
2578 |
| - } |
2579 |
| - return true; |
| 2640 | + hash_destroy(names_hash); |
2580 | 2641 | }
|
2581 | 2642 |
|
2582 | 2643 | /*
|
@@ -3404,16 +3465,34 @@ make_colname_unique(char *colname, deparse_namespace *dpns,
|
3404 | 3465 | deparse_columns *colinfo)
|
3405 | 3466 | {
|
3406 | 3467 | /*
|
3407 |
| - * If the selected name isn't unique, append digits to make it so |
| 3468 | + * If the selected name isn't unique, append digits to make it so. For a |
| 3469 | + * very long input name, we might have to truncate to stay within |
| 3470 | + * NAMEDATALEN. |
3408 | 3471 | */
|
3409 | 3472 | if (!colname_is_unique(colname, dpns, colinfo))
|
3410 | 3473 | {
|
3411 |
| - char *modname = (char *) palloc(strlen(colname) + 32); |
| 3474 | + int colnamelen = strlen(colname); |
| 3475 | + char *modname = (char *) palloc(colnamelen + 16); |
3412 | 3476 | int i = 0;
|
3413 | 3477 |
|
3414 | 3478 | do
|
3415 | 3479 | {
|
3416 |
| - sprintf(modname, "%s_%d", colname, ++i); |
| 3480 | + i++; |
| 3481 | + for (;;) |
| 3482 | + { |
| 3483 | + /* |
| 3484 | + * We avoid using %.*s here because it can misbehave if the |
| 3485 | + * data is not valid in what libc thinks is the prevailing |
| 3486 | + * encoding. |
| 3487 | + */ |
| 3488 | + memcpy(modname, colname, colnamelen); |
| 3489 | + sprintf(modname + colnamelen, "_%d", i); |
| 3490 | + if (strlen(modname) < NAMEDATALEN) |
| 3491 | + break; |
| 3492 | + /* drop chars from colname to keep all the digits */ |
| 3493 | + colnamelen = pg_mbcliplen(colname, colnamelen, |
| 3494 | + colnamelen - 1); |
| 3495 | + } |
3417 | 3496 | } while (!colname_is_unique(modname, dpns, colinfo));
|
3418 | 3497 | colname = modname;
|
3419 | 3498 | }
|
|
0 commit comments