|
37 | 37 | #include "commands/tablespace.h"
|
38 | 38 | #include "executor/spi.h"
|
39 | 39 | #include "funcapi.h"
|
| 40 | +#include "mb/pg_wchar.h" |
40 | 41 | #include "miscadmin.h"
|
41 | 42 | #include "nodes/makefuncs.h"
|
42 | 43 | #include "nodes/nodeFuncs.h"
|
|
53 | 54 | #include "utils/array.h"
|
54 | 55 | #include "utils/builtins.h"
|
55 | 56 | #include "utils/fmgroids.h"
|
| 57 | +#include "utils/hsearch.h" |
56 | 58 | #include "utils/lsyscache.h"
|
57 | 59 | #include "utils/rel.h"
|
58 | 60 | #include "utils/snapmgr.h"
|
@@ -262,6 +264,15 @@ typedef struct
|
262 | 264 | #define deparse_columns_fetch(rangetable_index, dpns) \
|
263 | 265 | ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
|
264 | 266 |
|
| 267 | +/* |
| 268 | + * Entry in set_rtable_names' hash table |
| 269 | + */ |
| 270 | +typedef struct |
| 271 | +{ |
| 272 | + char name[NAMEDATALEN]; /* Hash key --- must be first */ |
| 273 | + int counter; /* Largest addition used so far for name */ |
| 274 | +} NameHashEntry; |
| 275 | + |
265 | 276 |
|
266 | 277 | /* ----------
|
267 | 278 | * Global data
|
@@ -306,8 +317,6 @@ static int print_function_arguments(StringInfo buf, HeapTuple proctup,
|
306 | 317 | static void print_function_rettype(StringInfo buf, HeapTuple proctup);
|
307 | 318 | static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
308 | 319 | Bitmapset *rels_used);
|
309 |
| -static bool refname_is_unique(char *refname, deparse_namespace *dpns, |
310 |
| - List *parent_namespaces); |
311 | 320 | static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
|
312 | 321 | List *parent_namespaces);
|
313 | 322 | static void set_simple_column_names(deparse_namespace *dpns);
|
@@ -2640,15 +2649,61 @@ static void
|
2640 | 2649 | set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
2641 | 2650 | Bitmapset *rels_used)
|
2642 | 2651 | {
|
| 2652 | + HASHCTL hash_ctl; |
| 2653 | + HTAB *names_hash; |
| 2654 | + NameHashEntry *hentry; |
| 2655 | + bool found; |
| 2656 | + int rtindex; |
2643 | 2657 | ListCell *lc;
|
2644 |
| - int rtindex = 1; |
2645 | 2658 |
|
2646 | 2659 | dpns->rtable_names = NIL;
|
| 2660 | + /* nothing more to do if empty rtable */ |
| 2661 | + if (dpns->rtable == NIL) |
| 2662 | + return; |
| 2663 | + |
| 2664 | + /* |
| 2665 | + * We use a hash table to hold known names, so that this process is O(N) |
| 2666 | + * not O(N^2) for N names. |
| 2667 | + */ |
| 2668 | + MemSet(&hash_ctl, 0, sizeof(hash_ctl)); |
| 2669 | + hash_ctl.keysize = NAMEDATALEN; |
| 2670 | + hash_ctl.entrysize = sizeof(NameHashEntry); |
| 2671 | + hash_ctl.hcxt = CurrentMemoryContext; |
| 2672 | + names_hash = hash_create("set_rtable_names names", |
| 2673 | + list_length(dpns->rtable), |
| 2674 | + &hash_ctl, |
| 2675 | + HASH_ELEM | HASH_CONTEXT); |
| 2676 | + /* Preload the hash table with names appearing in parent_namespaces */ |
| 2677 | + foreach(lc, parent_namespaces) |
| 2678 | + { |
| 2679 | + deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); |
| 2680 | + ListCell *lc2; |
| 2681 | + |
| 2682 | + foreach(lc2, olddpns->rtable_names) |
| 2683 | + { |
| 2684 | + char *oldname = (char *) lfirst(lc2); |
| 2685 | + |
| 2686 | + if (oldname == NULL) |
| 2687 | + continue; |
| 2688 | + hentry = (NameHashEntry *) hash_search(names_hash, |
| 2689 | + oldname, |
| 2690 | + HASH_ENTER, |
| 2691 | + &found); |
| 2692 | + /* we do not complain about duplicate names in parent namespaces */ |
| 2693 | + hentry->counter = 0; |
| 2694 | + } |
| 2695 | + } |
| 2696 | + |
| 2697 | + /* Now we can scan the rtable */ |
| 2698 | + rtindex = 1; |
2647 | 2699 | foreach(lc, dpns->rtable)
|
2648 | 2700 | {
|
2649 | 2701 | RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
2650 | 2702 | char *refname;
|
2651 | 2703 |
|
| 2704 | + /* Just in case this takes an unreasonable amount of time ... */ |
| 2705 | + CHECK_FOR_INTERRUPTS(); |
| 2706 | + |
2652 | 2707 | if (rels_used && !bms_is_member(rtindex, rels_used))
|
2653 | 2708 | {
|
2654 | 2709 | /* Ignore unreferenced RTE */
|
@@ -2676,56 +2731,62 @@ set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
2676 | 2731 | }
|
2677 | 2732 |
|
2678 | 2733 | /*
|
2679 |
| - * If the selected name isn't unique, append digits to make it so |
| 2734 | + * If the selected name isn't unique, append digits to make it so, and |
| 2735 | + * make a new hash entry for it once we've got a unique name. For a |
| 2736 | + * very long input name, we might have to truncate to stay within |
| 2737 | + * NAMEDATALEN. |
2680 | 2738 | */
|
2681 |
| - if (refname && |
2682 |
| - !refname_is_unique(refname, dpns, parent_namespaces)) |
| 2739 | + if (refname) |
2683 | 2740 | {
|
2684 |
| - char *modname = (char *) palloc(strlen(refname) + 32); |
2685 |
| - int i = 0; |
| 2741 | + hentry = (NameHashEntry *) hash_search(names_hash, |
| 2742 | + refname, |
| 2743 | + HASH_ENTER, |
| 2744 | + &found); |
| 2745 | + if (found) |
| 2746 | + { |
| 2747 | + /* Name already in use, must choose a new one */ |
| 2748 | + int refnamelen = strlen(refname); |
| 2749 | + char *modname = (char *) palloc(refnamelen + 16); |
| 2750 | + NameHashEntry *hentry2; |
2686 | 2751 |
|
2687 |
| - do |
| 2752 | + do |
| 2753 | + { |
| 2754 | + hentry->counter++; |
| 2755 | + for (;;) |
| 2756 | + { |
| 2757 | + /* |
| 2758 | + * We avoid using %.*s here because it can misbehave |
| 2759 | + * if the data is not valid in what libc thinks is the |
| 2760 | + * prevailing encoding. |
| 2761 | + */ |
| 2762 | + memcpy(modname, refname, refnamelen); |
| 2763 | + sprintf(modname + refnamelen, "_%d", hentry->counter); |
| 2764 | + if (strlen(modname) < NAMEDATALEN) |
| 2765 | + break; |
| 2766 | + /* drop chars from refname to keep all the digits */ |
| 2767 | + refnamelen = pg_mbcliplen(refname, refnamelen, |
| 2768 | + refnamelen - 1); |
| 2769 | + } |
| 2770 | + hentry2 = (NameHashEntry *) hash_search(names_hash, |
| 2771 | + modname, |
| 2772 | + HASH_ENTER, |
| 2773 | + &found); |
| 2774 | + } while (found); |
| 2775 | + hentry2->counter = 0; /* init new hash entry */ |
| 2776 | + refname = modname; |
| 2777 | + } |
| 2778 | + else |
2688 | 2779 | {
|
2689 |
| - sprintf(modname, "%s_%d", refname, ++i); |
2690 |
| - } while (!refname_is_unique(modname, dpns, parent_namespaces)); |
2691 |
| - refname = modname; |
| 2780 | + /* Name not previously used, need only initialize hentry */ |
| 2781 | + hentry->counter = 0; |
| 2782 | + } |
2692 | 2783 | }
|
2693 | 2784 |
|
2694 | 2785 | dpns->rtable_names = lappend(dpns->rtable_names, refname);
|
2695 | 2786 | rtindex++;
|
2696 | 2787 | }
|
2697 |
| -} |
2698 |
| - |
2699 |
| -/* |
2700 |
| - * refname_is_unique: is refname distinct from all already-chosen RTE names? |
2701 |
| - */ |
2702 |
| -static bool |
2703 |
| -refname_is_unique(char *refname, deparse_namespace *dpns, |
2704 |
| - List *parent_namespaces) |
2705 |
| -{ |
2706 |
| - ListCell *lc; |
2707 | 2788 |
|
2708 |
| - foreach(lc, dpns->rtable_names) |
2709 |
| - { |
2710 |
| - char *oldname = (char *) lfirst(lc); |
2711 |
| - |
2712 |
| - if (oldname && strcmp(oldname, refname) == 0) |
2713 |
| - return false; |
2714 |
| - } |
2715 |
| - foreach(lc, parent_namespaces) |
2716 |
| - { |
2717 |
| - deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); |
2718 |
| - ListCell *lc2; |
2719 |
| - |
2720 |
| - foreach(lc2, olddpns->rtable_names) |
2721 |
| - { |
2722 |
| - char *oldname = (char *) lfirst(lc2); |
2723 |
| - |
2724 |
| - if (oldname && strcmp(oldname, refname) == 0) |
2725 |
| - return false; |
2726 |
| - } |
2727 |
| - } |
2728 |
| - return true; |
| 2789 | + hash_destroy(names_hash); |
2729 | 2790 | }
|
2730 | 2791 |
|
2731 | 2792 | /*
|
@@ -3553,16 +3614,34 @@ make_colname_unique(char *colname, deparse_namespace *dpns,
|
3553 | 3614 | deparse_columns *colinfo)
|
3554 | 3615 | {
|
3555 | 3616 | /*
|
3556 |
| - * If the selected name isn't unique, append digits to make it so |
| 3617 | + * If the selected name isn't unique, append digits to make it so. For a |
| 3618 | + * very long input name, we might have to truncate to stay within |
| 3619 | + * NAMEDATALEN. |
3557 | 3620 | */
|
3558 | 3621 | if (!colname_is_unique(colname, dpns, colinfo))
|
3559 | 3622 | {
|
3560 |
| - char *modname = (char *) palloc(strlen(colname) + 32); |
| 3623 | + int colnamelen = strlen(colname); |
| 3624 | + char *modname = (char *) palloc(colnamelen + 16); |
3561 | 3625 | int i = 0;
|
3562 | 3626 |
|
3563 | 3627 | do
|
3564 | 3628 | {
|
3565 |
| - sprintf(modname, "%s_%d", colname, ++i); |
| 3629 | + i++; |
| 3630 | + for (;;) |
| 3631 | + { |
| 3632 | + /* |
| 3633 | + * We avoid using %.*s here because it can misbehave if the |
| 3634 | + * data is not valid in what libc thinks is the prevailing |
| 3635 | + * encoding. |
| 3636 | + */ |
| 3637 | + memcpy(modname, colname, colnamelen); |
| 3638 | + sprintf(modname + colnamelen, "_%d", i); |
| 3639 | + if (strlen(modname) < NAMEDATALEN) |
| 3640 | + break; |
| 3641 | + /* drop chars from colname to keep all the digits */ |
| 3642 | + colnamelen = pg_mbcliplen(colname, colnamelen, |
| 3643 | + colnamelen - 1); |
| 3644 | + } |
3566 | 3645 | } while (!colname_is_unique(modname, dpns, colinfo));
|
3567 | 3646 | colname = modname;
|
3568 | 3647 | }
|
|
0 commit comments