public inbox for [email protected]
help / color / mirror / Atom feed[PATCH 2/2] Simplify COPY FROM parsing by forcing lookahead.
2+ messages / 2 participants
[nested] [flat]
* [PATCH 2/2] Simplify COPY FROM parsing by forcing lookahead.
@ 2021-02-04 10:39 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 2+ messages in thread
From: Heikki Linnakangas @ 2021-02-04 10:39 UTC (permalink / raw)
Now that we don't support the old-style COPY protocol anymore, we can
force four bytes of lookahead like was suggested in an existing comment,
and simplify the loop in CopyReadLineText().
Discussion: https://www.postgresql.org/message-id/9ec25819-0a8a-d51a-17dc-4150bb3cca3b%40iki.fi
---
src/backend/commands/copyfromparse.c | 119 ++++++++---------------
src/include/commands/copyfrom_internal.h | 3 +-
2 files changed, 40 insertions(+), 82 deletions(-)
diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c
index dd629668540..757b240a3c8 100644
--- a/src/backend/commands/copyfromparse.c
+++ b/src/backend/commands/copyfromparse.c
@@ -46,21 +46,6 @@
* empty statements. See http://www.cit.gu.edu.au/~anthony/info/C/C.macros.
*/
-/*
- * This keeps the character read at the top of the loop in the buffer
- * even if there is more than one read-ahead.
- */
-#define IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(extralen) \
-if (1) \
-{ \
- if (raw_buf_ptr + (extralen) >= copy_buf_len && !hit_eof) \
- { \
- raw_buf_ptr = prev_raw_ptr; /* undo fetch */ \
- need_data = true; \
- continue; \
- } \
-} else ((void) 0)
-
/* This consumes the remainder of the buffer and breaks */
#define IF_NEED_REFILL_AND_EOF_BREAK(extralen) \
if (1) \
@@ -118,7 +103,7 @@ static int CopyGetData(CopyFromState cstate, void *databuf,
int minread, int maxread);
static inline bool CopyGetInt32(CopyFromState cstate, int32 *val);
static inline bool CopyGetInt16(CopyFromState cstate, int16 *val);
-static bool CopyLoadRawBuf(CopyFromState cstate);
+static bool CopyLoadRawBuf(CopyFromState cstate, int minread);
static int CopyReadBinaryData(CopyFromState cstate, char *dest, int nbytes);
void
@@ -209,7 +194,7 @@ CopyGetData(CopyFromState cstate, void *databuf, int minread, int maxread)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read from COPY file: %m")));
- if (bytesread == 0)
+ if (bytesread < maxread)
cstate->reached_eof = true;
break;
case COPY_FRONTEND:
@@ -278,6 +263,8 @@ CopyGetData(CopyFromState cstate, void *databuf, int minread, int maxread)
break;
case COPY_CALLBACK:
bytesread = cstate->data_source_cb(databuf, minread, maxread);
+ if (bytesread < minread)
+ cstate->reached_eof = true;
break;
}
@@ -329,14 +316,13 @@ CopyGetInt16(CopyFromState cstate, int16 *val)
/*
* CopyLoadRawBuf loads some more data into raw_buf
*
- * Returns true if able to obtain at least one more byte, else false.
+ * Returns true if able to obtain at least 'minread' bytes, else false.
*
* If RAW_BUF_BYTES(cstate) > 0, the unprocessed bytes are moved to the start
- * of the buffer and then we load more data after that. This case occurs only
- * when a multibyte character crosses a bufferload boundary.
+ * of the buffer and then we load more data after that.
*/
static bool
-CopyLoadRawBuf(CopyFromState cstate)
+CopyLoadRawBuf(CopyFromState cstate, int minread)
{
int nbytes = RAW_BUF_BYTES(cstate);
int inbytes;
@@ -347,14 +333,15 @@ CopyLoadRawBuf(CopyFromState cstate)
nbytes);
inbytes = CopyGetData(cstate, cstate->raw_buf + nbytes,
- 1, RAW_BUF_SIZE - nbytes);
+ minread, RAW_BUF_SIZE - nbytes);
nbytes += inbytes;
cstate->raw_buf[nbytes] = '\0';
cstate->raw_buf_index = 0;
cstate->raw_buf_len = nbytes;
cstate->bytes_processed += nbytes;
pgstat_progress_update_param(PROGRESS_COPY_BYTES_PROCESSED, cstate->bytes_processed);
- return (inbytes > 0);
+
+ return (inbytes >= minread);
}
/*
@@ -389,7 +376,7 @@ CopyReadBinaryData(CopyFromState cstate, char *dest, int nbytes)
/* Load more data if buffer is empty. */
if (RAW_BUF_BYTES(cstate) == 0)
{
- if (!CopyLoadRawBuf(cstate))
+ if (!CopyLoadRawBuf(cstate, 1))
break; /* EOF */
}
@@ -678,7 +665,7 @@ CopyReadLine(CopyFromState cstate)
do
{
cstate->raw_buf_index = cstate->raw_buf_len;
- } while (CopyLoadRawBuf(cstate));
+ } while (CopyLoadRawBuf(cstate, 1));
}
}
else
@@ -747,7 +734,6 @@ CopyReadLineText(CopyFromState cstate)
char *copy_raw_buf;
int raw_buf_ptr;
int copy_buf_len;
- bool need_data = false;
bool hit_eof = false;
bool result = false;
char mblen_str[2];
@@ -794,6 +780,7 @@ CopyReadLineText(CopyFromState cstate)
copy_raw_buf = cstate->raw_buf;
raw_buf_ptr = cstate->raw_buf_index;
copy_buf_len = cstate->raw_buf_len;
+ hit_eof = cstate->reached_eof;
for (;;)
{
@@ -801,38 +788,40 @@ CopyReadLineText(CopyFromState cstate)
char c;
/*
- * Load more data if needed. Ideally we would just force four bytes
- * of read-ahead and avoid the many calls to
- * IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(), but the COPY_OLD_FE protocol
- * does not allow us to read too far ahead or we might read into the
- * next data, so we read-ahead only as far we know we can. One
- * optimization would be to read-ahead four byte here if
- * cstate->copy_src != COPY_OLD_FE, but it hardly seems worth it,
- * considering the size of the buffer.
+ * Load more data if needed.
+ *
+ * We need to look ahead max three bytes in one iteration of the loop
+ * (for the sequence \.<CR><NL>), so make sure we have at least four
+ * bytes in the buffer. Note that we always guarantee that there is
+ * one \0 in the buffer, after last valid byte; the lookaheads below
+ * rely on that.
*/
- if (raw_buf_ptr >= copy_buf_len || need_data)
+#define COPY_READ_LINE_LOOKAHEAD 4
+ if (raw_buf_ptr + COPY_READ_LINE_LOOKAHEAD >= copy_buf_len)
{
- REFILL_LINEBUF;
+ if (!hit_eof)
+ {
+ REFILL_LINEBUF;
- /*
- * Try to read some more data. This will certainly reset
- * raw_buf_index to zero, and raw_buf_ptr must go with it.
- */
- if (!CopyLoadRawBuf(cstate))
- hit_eof = true;
- raw_buf_ptr = 0;
- copy_buf_len = cstate->raw_buf_len;
+ /*
+ * Try to read some more data. This will certainly reset
+ * raw_buf_index to zero, and raw_buf_ptr must go with it.
+ */
+ if (!CopyLoadRawBuf(cstate, COPY_READ_LINE_LOOKAHEAD))
+ hit_eof = true;
+ raw_buf_ptr = 0;
+ copy_buf_len = cstate->raw_buf_len;
+ }
/*
* If we are completely out of data, break out of the loop,
* reporting EOF.
*/
- if (copy_buf_len <= 0)
+ if (copy_buf_len - raw_buf_ptr <= 0)
{
result = true;
break;
}
- need_data = false;
}
/* OK to fetch a character */
@@ -841,20 +830,6 @@ CopyReadLineText(CopyFromState cstate)
if (cstate->opts.csv_mode)
{
- /*
- * If character is '\\' or '\r', we may need to look ahead below.
- * Force fetch of the next character if we don't already have it.
- * We need to do this before changing CSV state, in case one of
- * these characters is also the quote or escape character.
- *
- * Note: old-protocol does not like forced prefetch, but it's OK
- * here since we cannot validly be at EOF.
- */
- if (c == '\\' || c == '\r')
- {
- IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
- }
-
/*
* Dealing with quotes and escapes here is mildly tricky. If the
* quote char is also the escape char, there's no problem - we
@@ -888,14 +863,9 @@ CopyReadLineText(CopyFromState cstate)
cstate->eol_type == EOL_CRNL)
{
/*
- * If need more data, go back to loop top to load it.
- *
- * Note that if we are at EOF, c will wind up as '\0' because
- * of the guaranteed pad of raw_buf.
+ * Look at the next character. If we're at EOF, c2 will wind
+ * up as '\0' because of the guaranteed pad of raw_buf.
*/
- IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
-
- /* get next char */
c = copy_raw_buf[raw_buf_ptr];
if (c == '\n')
@@ -961,7 +931,6 @@ CopyReadLineText(CopyFromState cstate)
{
char c2;
- IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
IF_NEED_REFILL_AND_EOF_BREAK(0);
/* -----
@@ -976,16 +945,9 @@ CopyReadLineText(CopyFromState cstate)
{
raw_buf_ptr++; /* consume the '.' */
- /*
- * Note: if we loop back for more data here, it does not
- * matter that the CSV state change checks are re-executed; we
- * will come back here with no important state changed.
- */
if (cstate->eol_type == EOL_CRNL)
{
- /* Get the next character */
- IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
- /* if hit_eof, c2 will become '\0' */
+ /* Get next character. If hit_eof, c2 will become '\0' */
c2 = copy_raw_buf[raw_buf_ptr++];
if (c2 == '\n')
@@ -1008,9 +970,7 @@ CopyReadLineText(CopyFromState cstate)
}
}
- /* Get the next character */
- IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(0);
- /* if hit_eof, c2 will become '\0' */
+ /* Get next character. If hit_eof, c2 will become '\0' */
c2 = copy_raw_buf[raw_buf_ptr++];
if (c2 != '\r' && c2 != '\n')
@@ -1087,7 +1047,6 @@ not_end_of_copy:
mblen_str[0] = c;
mblen = pg_encoding_mblen(cstate->file_encoding, mblen_str);
- IF_NEED_REFILL_AND_NOT_EOF_CONTINUE(mblen - 1);
IF_NEED_REFILL_AND_EOF_BREAK(mblen - 1);
raw_buf_ptr += mblen - 1;
}
diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h
index 705f5b615be..c088c4facdb 100644
--- a/src/include/commands/copyfrom_internal.h
+++ b/src/include/commands/copyfrom_internal.h
@@ -70,8 +70,7 @@ typedef struct CopyFromStateData
CopySource copy_src; /* type of copy source */
FILE *copy_file; /* used if copy_src == COPY_FILE */
StringInfo fe_msgbuf; /* used if copy_src == COPY_NEW_FE */
- bool reached_eof; /* true if we read to end of copy data (not
- * all copy_src types maintain this) */
+ bool reached_eof; /* true if we read to end of copy data */
EolType eol_type; /* EOL type of input */
int file_encoding; /* file or remote side's character encoding */
--
2.30.0
--------------AD4B53627CEDABF9E8CC5B3C--
^ permalink raw reply [nested|flat] 2+ messages in thread
* Speeding up ruleutils' name de-duplication code, redux
@ 2024-07-29 22:14 Tom Lane <[email protected]>
0 siblings, 0 replies; 2+ messages in thread
From: Tom Lane @ 2024-07-29 22:14 UTC (permalink / raw)
To: [email protected]
When deparsing queries or expressions, ruleutils.c has to generate
unique names for RTEs and columns of RTEs. (Often, they're unique
already, but this isn't reliably true.) The original logic for that
involved just strcmp'ing a proposed name against all the ones already
assigned, which obviously is O(N^2) in the number of names being
considered. Back in commit 8004953b5, we fixed that problem for
generation of unique RTE names, by using a hash table to remember the
already-assigned names. However, that commit did not touch the logic
for de-duplicating the column names within each RTE, explaining
In principle the same problem applies to the column-name-de-duplication
code; but in practice that seems to be less of a problem, first because
N is limited since we don't support extremely wide tables, and second
because duplicate column names within an RTE are fairly rare, so that in
practice the cost is more like O(N^2) not O(N^3). It would be very much
messier to fix the column-name code, so for now I've left that alone.
But I think the time has come to do something about it. In [1]
I presented this Perl script to generate a database that gives
pg_upgrade indigestion:
-----
for (my $i = 0; $i < 100; $i++)
{
print "CREATE TABLE test_inh_check$i (\n";
for (my $j = 0; $j < 1000; $j++)
{
print "a$j float check (a$j > 10.2),\n";
}
print "b float);\n";
print "CREATE TABLE test_inh_check_child$i() INHERITS(test_inh_check$i);\n";
}
-----
On my development machine, it takes over 14 minutes to pg_upgrade
this, and it turns out that that time is largely spent in column
name de-duplication while deparsing the CHECK constraints. The
attached patch reduces that to about 3m45s.
(I think that we ought to reconsider MergeConstraintsIntoExisting's
use of deparsing to compare check constraints: it'd be faster and
probably more reliable to apply attnum translation to one parsetree
and then use equal(). But that's a matter for a different patch, and
this patch would still be useful for the pg_dump side of the problem.)
I was able to avoid a lot of the complexity I'd feared before by not
attempting to use hashing during set_using_names(), which only has to
consider columns merged by USING clauses, so it shouldn't have enough
of a performance problem to be worth touching. The hashing code needs
to be optional anyway because it's unlikely to be a win for narrow
tables, so we can simply ignore it until we reach the potentially
expensive steps. Also, things are already factored in such a way that
we only need to have one hashtable at a time, so this shouldn't cause
any large memory bloat.
I'll park this in the next CF.
regards, tom lane
[1] https://www.postgresql.org/message-id/2422717.1722201869%40sss.pgh.pa.us
Attachments:
[text/x-diff] v1-speed-up-column-name-deduplication.patch (9.9K, ../../[email protected]/2-v1-speed-up-column-name-deduplication.patch)
download | inline diff:
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 653685bffc..9eb4b858ff 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -223,6 +223,10 @@ typedef struct
* of aliases to columns of the right input. Thus, positions in the printable
* column alias list are not necessarily one-for-one with varattnos of the
* JOIN, so we need a separate new_colnames[] array for printing purposes.
+ *
+ * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
+ * non-duplicate column names. We ameliorate that by using a hash table that
+ * holds all the strings appearing in colnames, new_colnames, and parentUsing.
*/
typedef struct
{
@@ -290,6 +294,15 @@ typedef struct
int *leftattnos; /* left-child varattnos of join cols, or 0 */
int *rightattnos; /* right-child varattnos of join cols, or 0 */
List *usingNames; /* names assigned to merged columns */
+
+ /*
+ * Hash table holding copies of all the strings appearing in this struct's
+ * colnames, new_colnames, and parentUsing. We use a hash table only for
+ * sufficiently wide relations, and only during the colname-assignment
+ * functions set_relation_column_names and set_join_column_names;
+ * otherwise, names_hash is NULL.
+ */
+ HTAB *names_hash; /* entries are just strings */
} deparse_columns;
/* This macro is analogous to rt_fetch(), but for deparse_columns structs */
@@ -375,6 +388,9 @@ static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
static char *make_colname_unique(char *colname, deparse_namespace *dpns,
deparse_columns *colinfo);
static void expand_colnames_array_to(deparse_columns *colinfo, int n);
+static void build_colinfo_names_hash(deparse_columns *colinfo);
+static void add_to_names_hash(deparse_columns *colinfo, const char *name);
+static void destroy_colinfo_names_hash(deparse_columns *colinfo);
static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
deparse_columns *colinfo);
static char *get_rtable_name(int rtindex, deparse_context *context);
@@ -4136,6 +4152,10 @@ has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
*
* parentUsing is a list of all USING aliases assigned in parent joins of
* the current jointree node. (The passed-in list must not be modified.)
+ *
+ * Note that we do not use per-deparse_columns hash tables in this function.
+ * The number of names that need to be assigned should be small enough that
+ * we don't need to trouble with that.
*/
static void
set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
@@ -4411,6 +4431,9 @@ set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
+ /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
+ build_colinfo_names_hash(colinfo);
+
/*
* Scan the columns, select a unique alias for each one, and store it in
* colinfo->colnames and colinfo->new_colnames. The former array has NULL
@@ -4446,6 +4469,7 @@ set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
colname = make_colname_unique(colname, dpns, colinfo);
colinfo->colnames[i] = colname;
+ add_to_names_hash(colinfo, colname);
}
/* Put names of non-dropped columns in new_colnames[] too */
@@ -4459,6 +4483,9 @@ set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
changed_any = true;
}
+ /* We're now done needing the colinfo's names_hash */
+ destroy_colinfo_names_hash(colinfo);
+
/*
* Set correct length for new_colnames[] array. (Note: if columns have
* been added, colinfo->num_cols includes them, which is not really quite
@@ -4529,6 +4556,9 @@ set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
expand_colnames_array_to(colinfo, noldcolumns);
Assert(colinfo->num_cols == noldcolumns);
+ /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
+ build_colinfo_names_hash(colinfo);
+
/*
* Scan the join output columns, select an alias for each one, and store
* it in colinfo->colnames. If there are USING columns, set_using_names()
@@ -4566,6 +4596,7 @@ set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
if (rte->alias == NULL)
{
colinfo->colnames[i] = real_colname;
+ add_to_names_hash(colinfo, real_colname);
continue;
}
@@ -4582,6 +4613,7 @@ set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
colname = make_colname_unique(colname, dpns, colinfo);
colinfo->colnames[i] = colname;
+ add_to_names_hash(colinfo, colname);
}
/* Remember if any assigned aliases differ from "real" name */
@@ -4680,6 +4712,7 @@ set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
}
else
colinfo->new_colnames[j] = child_colname;
+ add_to_names_hash(colinfo, colinfo->new_colnames[j]);
}
colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
@@ -4729,6 +4762,7 @@ set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
}
else
colinfo->new_colnames[j] = child_colname;
+ add_to_names_hash(colinfo, colinfo->new_colnames[j]);
}
colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
@@ -4743,6 +4777,9 @@ set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
Assert(j == nnewcolumns);
#endif
+ /* We're now done needing the colinfo's names_hash */
+ destroy_colinfo_names_hash(colinfo);
+
/*
* For a named join, print column aliases if we changed any from the child
* names. Unnamed joins cannot print aliases.
@@ -4765,38 +4802,59 @@ colname_is_unique(const char *colname, deparse_namespace *dpns,
int i;
ListCell *lc;
- /* Check against already-assigned column aliases within RTE */
- for (i = 0; i < colinfo->num_cols; i++)
- {
- char *oldname = colinfo->colnames[i];
-
- if (oldname && strcmp(oldname, colname) == 0)
- return false;
- }
-
/*
- * If we're building a new_colnames array, check that too (this will be
- * partially but not completely redundant with the previous checks)
+ * If we have a hash table, consult that instead of linearly scanning the
+ * colinfo's strings.
*/
- for (i = 0; i < colinfo->num_new_cols; i++)
+ if (colinfo->names_hash)
{
- char *oldname = colinfo->new_colnames[i];
-
- if (oldname && strcmp(oldname, colname) == 0)
+ if (hash_search(colinfo->names_hash,
+ colname,
+ HASH_FIND,
+ NULL) != NULL)
return false;
}
-
- /* Also check against USING-column names that must be globally unique */
- foreach(lc, dpns->using_names)
+ else
{
- char *oldname = (char *) lfirst(lc);
+ /* Check against already-assigned column aliases within RTE */
+ for (i = 0; i < colinfo->num_cols; i++)
+ {
+ char *oldname = colinfo->colnames[i];
- if (strcmp(oldname, colname) == 0)
- return false;
+ if (oldname && strcmp(oldname, colname) == 0)
+ return false;
+ }
+
+ /*
+ * If we're building a new_colnames array, check that too (this will
+ * be partially but not completely redundant with the previous checks)
+ */
+ for (i = 0; i < colinfo->num_new_cols; i++)
+ {
+ char *oldname = colinfo->new_colnames[i];
+
+ if (oldname && strcmp(oldname, colname) == 0)
+ return false;
+ }
+
+ /*
+ * Also check against names already assigned for parent-join USING
+ * cols
+ */
+ foreach(lc, colinfo->parentUsing)
+ {
+ char *oldname = (char *) lfirst(lc);
+
+ if (strcmp(oldname, colname) == 0)
+ return false;
+ }
}
- /* Also check against names already assigned for parent-join USING cols */
- foreach(lc, colinfo->parentUsing)
+ /*
+ * Also check against USING-column names that must be globally unique.
+ * These are not hashed, but there should be few of them.
+ */
+ foreach(lc, dpns->using_names)
{
char *oldname = (char *) lfirst(lc);
@@ -4864,6 +4922,90 @@ expand_colnames_array_to(deparse_columns *colinfo, int n)
}
}
+/*
+ * build_colinfo_names_hash: optionally construct a hash table for colinfo
+ */
+static void
+build_colinfo_names_hash(deparse_columns *colinfo)
+{
+ HASHCTL hash_ctl;
+ int i;
+ ListCell *lc;
+
+ /*
+ * Use a hash table only for RTEs with at least 32 columns. (The cutoff
+ * is somewhat arbitrary, but let's choose it so that this code does get
+ * exercised in the regression tests.)
+ */
+ if (colinfo->num_cols < 32)
+ return;
+
+ /*
+ * Set up the hash table. The entries are just strings with no other
+ * payload.
+ */
+ hash_ctl.keysize = NAMEDATALEN;
+ hash_ctl.entrysize = NAMEDATALEN;
+ hash_ctl.hcxt = CurrentMemoryContext;
+ colinfo->names_hash = hash_create("deparse_columns names",
+ colinfo->num_cols + colinfo->num_new_cols,
+ &hash_ctl,
+ HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
+
+ /*
+ * Preload the hash table with any names already present (these would have
+ * come from set_using_names).
+ */
+ for (i = 0; i < colinfo->num_cols; i++)
+ {
+ char *oldname = colinfo->colnames[i];
+
+ if (oldname)
+ add_to_names_hash(colinfo, oldname);
+ }
+
+ for (i = 0; i < colinfo->num_new_cols; i++)
+ {
+ char *oldname = colinfo->new_colnames[i];
+
+ if (oldname)
+ add_to_names_hash(colinfo, oldname);
+ }
+
+ foreach(lc, colinfo->parentUsing)
+ {
+ char *oldname = (char *) lfirst(lc);
+
+ add_to_names_hash(colinfo, oldname);
+ }
+}
+
+/*
+ * add_to_names_hash: add a string to the names_hash, if we're using one
+ */
+static void
+add_to_names_hash(deparse_columns *colinfo, const char *name)
+{
+ if (colinfo->names_hash)
+ (void) hash_search(colinfo->names_hash,
+ name,
+ HASH_ENTER,
+ NULL);
+}
+
+/*
+ * destroy_colinfo_names_hash: destroy hash table when done with it
+ */
+static void
+destroy_colinfo_names_hash(deparse_columns *colinfo)
+{
+ if (colinfo->names_hash)
+ {
+ hash_destroy(colinfo->names_hash);
+ colinfo->names_hash = NULL;
+ }
+}
+
/*
* identify_join_columns: figure out where columns of a join come from
*
^ permalink raw reply [nested|flat] 2+ messages in thread
end of thread, other threads:[~2024-07-29 22:14 UTC | newest]
Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-02-04 10:39 [PATCH 2/2] Simplify COPY FROM parsing by forcing lookahead. Heikki Linnakangas <[email protected]>
2024-07-29 22:14 Speeding up ruleutils' name de-duplication code, redux Tom Lane <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox