1 /*-------------------------------------------------------------------------
4 * Implementation of simple filter file parser
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * src/bin/pg_dump/filter.c
12 *-------------------------------------------------------------------------
14 #include "postgres_fe.h"
16 #include "common/logging.h"
17 #include "common/string.h"
19 #include "lib/stringinfo.h"
20 #include "pqexpbuffer.h"
22 #define is_keyword_str(cstr, str, bytes) \
23 ((strlen(cstr) == (bytes)) && (pg_strncasecmp((cstr), (str), (bytes)) == 0))
26 * Following routines are called from pg_dump, pg_dumpall and pg_restore.
27 * Since the implementation of exit_nicely is application specific, each
28 * application need to pass a function pointer to the exit_nicely function to
29 * use for exiting on errors.
33 * Opens filter's file and initialize fstate structure.
36 filter_init(FilterStateData
*fstate
, const char *filename
, exit_function f_exit
)
38 fstate
->filename
= filename
;
40 fstate
->exit_nicely
= f_exit
;
41 initStringInfo(&fstate
->linebuff
);
43 if (strcmp(filename
, "-") != 0)
45 fstate
->fp
= fopen(filename
, "r");
48 pg_log_error("could not open filter file \"%s\": %m", filename
);
49 fstate
->exit_nicely(1);
57 * Release allocated resources for the given filter.
60 filter_free(FilterStateData
*fstate
)
65 free(fstate
->linebuff
.data
);
66 fstate
->linebuff
.data
= NULL
;
68 if (fstate
->fp
&& fstate
->fp
!= stdin
)
70 if (fclose(fstate
->fp
) != 0)
71 pg_log_error("could not close filter file \"%s\": %m", fstate
->filename
);
78 * Translate FilterObjectType enum to string. The main purpose is for error
82 filter_object_type_name(FilterObjectType fot
)
86 case FILTER_OBJECT_TYPE_NONE
:
87 return "comment or empty line";
88 case FILTER_OBJECT_TYPE_TABLE_DATA
:
90 case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN
:
91 return "table data and children";
92 case FILTER_OBJECT_TYPE_DATABASE
:
94 case FILTER_OBJECT_TYPE_EXTENSION
:
96 case FILTER_OBJECT_TYPE_FOREIGN_DATA
:
97 return "foreign data";
98 case FILTER_OBJECT_TYPE_FUNCTION
:
100 case FILTER_OBJECT_TYPE_INDEX
:
102 case FILTER_OBJECT_TYPE_SCHEMA
:
104 case FILTER_OBJECT_TYPE_TABLE
:
106 case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN
:
107 return "table and children";
108 case FILTER_OBJECT_TYPE_TRIGGER
:
112 /* should never get here */
117 * Returns true when keyword is one of supported object types, and
118 * set related objtype. Returns false, when keyword is not assigned
119 * with known object type.
122 get_object_type(const char *keyword
, int size
, FilterObjectType
*objtype
)
124 if (is_keyword_str("table_data", keyword
, size
))
125 *objtype
= FILTER_OBJECT_TYPE_TABLE_DATA
;
126 else if (is_keyword_str("table_data_and_children", keyword
, size
))
127 *objtype
= FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN
;
128 else if (is_keyword_str("database", keyword
, size
))
129 *objtype
= FILTER_OBJECT_TYPE_DATABASE
;
130 else if (is_keyword_str("extension", keyword
, size
))
131 *objtype
= FILTER_OBJECT_TYPE_EXTENSION
;
132 else if (is_keyword_str("foreign_data", keyword
, size
))
133 *objtype
= FILTER_OBJECT_TYPE_FOREIGN_DATA
;
134 else if (is_keyword_str("function", keyword
, size
))
135 *objtype
= FILTER_OBJECT_TYPE_FUNCTION
;
136 else if (is_keyword_str("index", keyword
, size
))
137 *objtype
= FILTER_OBJECT_TYPE_INDEX
;
138 else if (is_keyword_str("schema", keyword
, size
))
139 *objtype
= FILTER_OBJECT_TYPE_SCHEMA
;
140 else if (is_keyword_str("table", keyword
, size
))
141 *objtype
= FILTER_OBJECT_TYPE_TABLE
;
142 else if (is_keyword_str("table_and_children", keyword
, size
))
143 *objtype
= FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN
;
144 else if (is_keyword_str("trigger", keyword
, size
))
145 *objtype
= FILTER_OBJECT_TYPE_TRIGGER
;
154 pg_log_filter_error(FilterStateData
*fstate
, const char *fmt
,...)
160 vsnprintf(buf
, sizeof(buf
), fmt
, argp
);
163 if (fstate
->fp
== stdin
)
164 pg_log_error("invalid format in filter read from standard input on line %d: %s",
165 fstate
->lineno
, buf
);
167 pg_log_error("invalid format in filter read from file \"%s\" on line %d: %s",
168 fstate
->filename
, fstate
->lineno
, buf
);
172 * filter_get_keyword - read the next filter keyword from buffer
174 * Search for keywords (limited to ascii alphabetic characters) in
175 * the passed in line buffer. Returns NULL when the buffer is empty or the first
176 * char is not alpha. The char '_' is allowed, except as the first character.
177 * The length of the found keyword is returned in the size parameter.
180 filter_get_keyword(const char **line
, int *size
)
182 const char *ptr
= *line
;
183 const char *result
= NULL
;
185 /* Set returned length preemptively in case no keyword is found */
188 /* Skip initial whitespace */
189 while (isspace((unsigned char) *ptr
))
192 if (isalpha((unsigned char) *ptr
))
196 while (isalpha((unsigned char) *ptr
) || *ptr
== '_')
199 *size
= ptr
- result
;
208 * read_quoted_string - read quoted possibly multi line string
210 * Reads a quoted string which can span over multiple lines and returns a
211 * pointer to next char after ending double quotes; it will exit on errors.
214 read_quoted_string(FilterStateData
*fstate
,
218 appendPQExpBufferChar(pattern
, '"');
224 * We can ignore \r or \n chars because the string is read by
225 * pg_get_line_buf, so these chars should be just trailing chars.
227 if (*str
== '\r' || *str
== '\n')
235 Assert(fstate
->linebuff
.data
);
237 if (!pg_get_line_buf(fstate
->fp
, &fstate
->linebuff
))
239 if (ferror(fstate
->fp
))
240 pg_log_error("could not read from filter file \"%s\": %m",
243 pg_log_filter_error(fstate
, _("unexpected end of file"));
245 fstate
->exit_nicely(1);
248 str
= fstate
->linebuff
.data
;
250 appendPQExpBufferChar(pattern
, '\n');
256 appendPQExpBufferChar(pattern
, '"');
261 appendPQExpBufferChar(pattern
, '"');
267 else if (*str
== '\\')
271 appendPQExpBufferChar(pattern
, '\n');
272 else if (*str
== '\\')
273 appendPQExpBufferChar(pattern
, '\\');
278 appendPQExpBufferChar(pattern
, *str
++);
285 * read_pattern - reads on object pattern from input
287 * This function will parse any valid identifier (quoted or not, qualified or
288 * not), which can also includes the full signature for routines.
289 * Note that this function takes special care to sanitize the detected
290 * identifier (removing extraneous whitespaces or other unnecessary
291 * characters). This is necessary as most backup/restore filtering functions
292 * only recognize identifiers if they are written exactly the same way as
293 * they are output by the server.
295 * Returns a pointer to next character after the found identifier and exits
299 read_pattern(FilterStateData
*fstate
, const char *str
, PQExpBuffer pattern
)
301 bool skip_space
= true;
302 bool found_space
= false;
304 /* Skip initial whitespace */
305 while (isspace((unsigned char) *str
))
310 pg_log_filter_error(fstate
, _("missing object name pattern"));
311 fstate
->exit_nicely(1);
314 while (*str
&& *str
!= '#')
316 while (*str
&& !isspace((unsigned char) *str
) && !strchr("#,.()\"", *str
))
319 * Append space only when it is allowed, and when it was found in
322 if (!skip_space
&& found_space
)
324 appendPQExpBufferChar(pattern
, ' ');
328 appendPQExpBufferChar(pattern
, *str
++);
336 appendPQExpBufferChar(pattern
, ' ');
338 str
= read_quoted_string(fstate
, str
, pattern
);
340 else if (*str
== ',')
342 appendPQExpBufferStr(pattern
, ", ");
346 else if (*str
&& strchr(".()", *str
))
348 appendPQExpBufferChar(pattern
, *str
++);
354 /* skip ending whitespaces */
355 while (isspace((unsigned char) *str
))
366 * filter_read_item - Read command/type/pattern triplet from a filter file
368 * This will parse one filter item from the filter file, and while it is a
369 * row based format a pattern may span more than one line due to how object
370 * names can be constructed. The expected format of the filter file is:
372 * <command> <object_type> <pattern>
374 * command can be "include" or "exclude".
376 * Supported object types are described by enum FilterObjectType
377 * (see function get_object_type).
379 * pattern can be any possibly-quoted and possibly-qualified identifier. It
380 * follows the same rules as other object include and exclude functions so it
381 * can also use wildcards.
383 * Returns true when one filter item was successfully read and parsed. When
384 * object name contains \n chars, then more than one line from input file can
385 * be processed. Returns false when the filter file reaches EOF. In case of
386 * error, the function will emit an appropriate error message and exit.
389 filter_read_item(FilterStateData
*fstate
,
391 FilterCommandType
*comtype
,
392 FilterObjectType
*objtype
)
394 if (pg_get_line_buf(fstate
->fp
, &fstate
->linebuff
))
396 const char *str
= fstate
->linebuff
.data
;
399 PQExpBufferData pattern
;
403 /* Skip initial white spaces */
404 while (isspace((unsigned char) *str
))
408 * Skip empty lines or lines where the first non-whitespace character
409 * is a hash indicating a comment.
411 if (*str
!= '\0' && *str
!= '#')
414 * First we expect sequence of two keywords, {include|exclude}
415 * followed by the object type to operate on.
417 keyword
= filter_get_keyword(&str
, &size
);
420 pg_log_filter_error(fstate
,
421 _("no filter command found (expected \"include\" or \"exclude\")"));
422 fstate
->exit_nicely(1);
425 if (is_keyword_str("include", keyword
, size
))
426 *comtype
= FILTER_COMMAND_TYPE_INCLUDE
;
427 else if (is_keyword_str("exclude", keyword
, size
))
428 *comtype
= FILTER_COMMAND_TYPE_EXCLUDE
;
431 pg_log_filter_error(fstate
,
432 _("invalid filter command (expected \"include\" or \"exclude\")"));
433 fstate
->exit_nicely(1);
436 keyword
= filter_get_keyword(&str
, &size
);
439 pg_log_filter_error(fstate
, _("missing filter object type"));
440 fstate
->exit_nicely(1);
443 if (!get_object_type(keyword
, size
, objtype
))
445 pg_log_filter_error(fstate
,
446 _("unsupported filter object type: \"%.*s\""), size
, keyword
);
447 fstate
->exit_nicely(1);
450 initPQExpBuffer(&pattern
);
452 str
= read_pattern(fstate
, str
, &pattern
);
453 *objname
= pattern
.data
;
458 *comtype
= FILTER_COMMAND_TYPE_NONE
;
459 *objtype
= FILTER_OBJECT_TYPE_NONE
;
465 if (ferror(fstate
->fp
))
467 pg_log_error("could not read from filter file \"%s\": %m", fstate
->filename
);
468 fstate
->exit_nicely(1);