PostgreSQL Source Code  git master
filter.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * filter.c
4  * Implementation of simple filter file parser
5  *
6  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/bin/pg_dump/filter.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres_fe.h"
15 
16 #include "common/fe_memutils.h"
17 #include "common/logging.h"
18 #include "common/string.h"
19 #include "filter.h"
20 #include "lib/stringinfo.h"
21 #include "pqexpbuffer.h"
22 
23 #define is_keyword_str(cstr, str, bytes) \
24  ((strlen(cstr) == (bytes)) && (pg_strncasecmp((cstr), (str), (bytes)) == 0))
25 
26 /*
27  * Following routines are called from pg_dump, pg_dumpall and pg_restore.
28  * Since the implementation of exit_nicely is application specific, each
29  * application need to pass a function pointer to the exit_nicely function to
30  * use for exiting on errors.
31  */
32 
33 /*
34  * Opens filter's file and initialize fstate structure.
35  */
36 void
37 filter_init(FilterStateData *fstate, const char *filename, exit_function f_exit)
38 {
39  fstate->filename = filename;
40  fstate->lineno = 0;
41  fstate->exit_nicely = f_exit;
42  initStringInfo(&fstate->linebuff);
43 
44  if (strcmp(filename, "-") != 0)
45  {
46  fstate->fp = fopen(filename, "r");
47  if (!fstate->fp)
48  {
49  pg_log_error("could not open filter file \"%s\": %m", filename);
50  fstate->exit_nicely(1);
51  }
52  }
53  else
54  fstate->fp = stdin;
55 }
56 
57 /*
58  * Release allocated resources for the given filter.
59  */
60 void
62 {
63  if (!fstate)
64  return;
65 
66  free(fstate->linebuff.data);
67  fstate->linebuff.data = NULL;
68 
69  if (fstate->fp && fstate->fp != stdin)
70  {
71  if (fclose(fstate->fp) != 0)
72  pg_log_error("could not close filter file \"%s\": %m", fstate->filename);
73 
74  fstate->fp = NULL;
75  }
76 }
77 
78 /*
79  * Translate FilterObjectType enum to string. The main purpose is for error
80  * message formatting.
81  */
82 const char *
84 {
85  switch (fot)
86  {
88  return "comment or empty line";
90  return "table data";
92  return "table data and children";
94  return "database";
96  return "extension";
98  return "foreign data";
100  return "function";
102  return "index";
104  return "schema";
106  return "table";
108  return "table and children";
110  return "trigger";
111  }
112 
113  /* should never get here */
114  pg_unreachable();
115 }
116 
117 /*
118  * Returns true when keyword is one of supported object types, and
119  * set related objtype. Returns false, when keyword is not assigned
120  * with known object type.
121  */
122 static bool
123 get_object_type(const char *keyword, int size, FilterObjectType *objtype)
124 {
125  if (is_keyword_str("table_data", keyword, size))
127  else if (is_keyword_str("table_data_and_children", keyword, size))
129  else if (is_keyword_str("database", keyword, size))
130  *objtype = FILTER_OBJECT_TYPE_DATABASE;
131  else if (is_keyword_str("extension", keyword, size))
132  *objtype = FILTER_OBJECT_TYPE_EXTENSION;
133  else if (is_keyword_str("foreign_data", keyword, size))
135  else if (is_keyword_str("function", keyword, size))
136  *objtype = FILTER_OBJECT_TYPE_FUNCTION;
137  else if (is_keyword_str("index", keyword, size))
138  *objtype = FILTER_OBJECT_TYPE_INDEX;
139  else if (is_keyword_str("schema", keyword, size))
140  *objtype = FILTER_OBJECT_TYPE_SCHEMA;
141  else if (is_keyword_str("table", keyword, size))
142  *objtype = FILTER_OBJECT_TYPE_TABLE;
143  else if (is_keyword_str("table_and_children", keyword, size))
145  else if (is_keyword_str("trigger", keyword, size))
146  *objtype = FILTER_OBJECT_TYPE_TRIGGER;
147  else
148  return false;
149 
150  return true;
151 }
152 
153 
154 void
155 pg_log_filter_error(FilterStateData *fstate, const char *fmt,...)
156 {
157  va_list argp;
158  char buf[256];
159 
160  va_start(argp, fmt);
161  vsnprintf(buf, sizeof(buf), fmt, argp);
162  va_end(argp);
163 
164  pg_log_error("invalid format in filter read from \"%s\" on line %d: %s",
165  (fstate->fp == stdin ? "stdin" : fstate->filename),
166  fstate->lineno,
167  buf);
168 }
169 
170 /*
171  * filter_get_keyword - read the next filter keyword from buffer
172  *
173  * Search for keywords (limited to ascii alphabetic characters) in
174  * the passed in line buffer. Returns NULL when the buffer is empty or the first
175  * char is not alpha. The char '_' is allowed, except as the first character.
176  * The length of the found keyword is returned in the size parameter.
177  */
178 static const char *
179 filter_get_keyword(const char **line, int *size)
180 {
181  const char *ptr = *line;
182  const char *result = NULL;
183 
184  /* Set returned length preemptively in case no keyword is found */
185  *size = 0;
186 
187  /* Skip initial whitespace */
188  while (isspace((unsigned char) *ptr))
189  ptr++;
190 
191  if (isalpha((unsigned char) *ptr))
192  {
193  result = ptr++;
194 
195  while (isalpha((unsigned char) *ptr) || *ptr == '_')
196  ptr++;
197 
198  *size = ptr - result;
199  }
200 
201  *line = ptr;
202 
203  return result;
204 }
205 
206 /*
207  * read_quoted_pattern - read quoted possibly multi line string
208  *
209  * Reads a quoted string which can span over multiple lines and returns a
210  * pointer to next char after ending double quotes; it will exit on errors.
211  */
212 static const char *
214  const char *str,
215  PQExpBuffer pattern)
216 {
217  appendPQExpBufferChar(pattern, '"');
218  str++;
219 
220  while (1)
221  {
222  /*
223  * We can ignore \r or \n chars because the string is read by
224  * pg_get_line_buf, so these chars should be just trailing chars.
225  */
226  if (*str == '\r' || *str == '\n')
227  {
228  str++;
229  continue;
230  }
231 
232  if (*str == '\0')
233  {
234  Assert(fstate->linebuff.data);
235 
236  if (!pg_get_line_buf(fstate->fp, &fstate->linebuff))
237  {
238  if (ferror(fstate->fp))
239  pg_log_error("could not read from filter file \"%s\": %m",
240  fstate->filename);
241  else
242  pg_log_filter_error(fstate, _("unexpected end of file"));
243 
244  fstate->exit_nicely(1);
245  }
246 
247  str = fstate->linebuff.data;
248 
249  appendPQExpBufferChar(pattern, '\n');
250  fstate->lineno++;
251  }
252 
253  if (*str == '"')
254  {
255  appendPQExpBufferChar(pattern, '"');
256  str++;
257 
258  if (*str == '"')
259  {
260  appendPQExpBufferChar(pattern, '"');
261  str++;
262  }
263  else
264  break;
265  }
266  else if (*str == '\\')
267  {
268  str++;
269  if (*str == 'n')
270  appendPQExpBufferChar(pattern, '\n');
271  else if (*str == '\\')
272  appendPQExpBufferChar(pattern, '\\');
273 
274  str++;
275  }
276  else
277  appendPQExpBufferChar(pattern, *str++);
278  }
279 
280  return str;
281 }
282 
283 /*
284  * read_pattern - reads on object pattern from input
285  *
286  * This function will parse any valid identifier (quoted or not, qualified or
287  * not), which can also includes the full signature for routines.
288  * Note that this function takes special care to sanitize the detected
289  * identifier (removing extraneous whitespaces or other unnecessary
290  * characters). This is necessary as most backup/restore filtering functions
291  * only recognize identifiers if they are written exactly the same way as
292  * they are output by the server.
293  *
294  * Returns a pointer to next character after the found identifier and exits
295  * on error.
296  */
297 static const char *
298 read_pattern(FilterStateData *fstate, const char *str, PQExpBuffer pattern)
299 {
300  bool skip_space = true;
301  bool found_space = false;
302 
303  /* Skip initial whitespace */
304  while (isspace((unsigned char) *str))
305  str++;
306 
307  if (*str == '\0')
308  {
309  pg_log_filter_error(fstate, _("missing object name pattern"));
310  fstate->exit_nicely(1);
311  }
312 
313  while (*str && *str != '#')
314  {
315  while (*str && !isspace((unsigned char) *str) && !strchr("#,.()\"", *str))
316  {
317  /*
318  * Append space only when it is allowed, and when it was found in
319  * original string.
320  */
321  if (!skip_space && found_space)
322  {
323  appendPQExpBufferChar(pattern, ' ');
324  skip_space = true;
325  }
326 
327  appendPQExpBufferChar(pattern, *str++);
328  }
329 
330  skip_space = false;
331 
332  if (*str == '"')
333  {
334  if (found_space)
335  appendPQExpBufferChar(pattern, ' ');
336 
337  str = read_quoted_string(fstate, str, pattern);
338  }
339  else if (*str == ',')
340  {
341  appendPQExpBufferStr(pattern, ", ");
342  skip_space = true;
343  str++;
344  }
345  else if (*str && strchr(".()", *str))
346  {
347  appendPQExpBufferChar(pattern, *str++);
348  skip_space = true;
349  }
350 
351  found_space = false;
352 
353  /* skip ending whitespaces */
354  while (isspace((unsigned char) *str))
355  {
356  found_space = true;
357  str++;
358  }
359  }
360 
361  return str;
362 }
363 
364 /*
365  * filter_read_item - Read command/type/pattern triplet from a filter file
366  *
367  * This will parse one filter item from the filter file, and while it is a
368  * row based format a pattern may span more than one line due to how object
369  * names can be constructed. The expected format of the filter file is:
370  *
371  * <command> <object_type> <pattern>
372  *
373  * command can be "include" or "exclude".
374  *
375  * Supported object types are described by enum FilterObjectType
376  * (see function get_object_type).
377  *
378  * pattern can be any possibly-quoted and possibly-qualified identifier. It
379  * follows the same rules as other object include and exclude functions so it
380  * can also use wildcards.
381  *
382  * Returns true when one filter item was successfully read and parsed. When
383  * object name contains \n chars, then more than one line from input file can
384  * be processed. Returns false when the filter file reaches EOF. In case of
385  * error, the function will emit an appropriate error message and exit.
386  */
387 bool
389  char **objname,
390  FilterCommandType *comtype,
391  FilterObjectType *objtype)
392 {
393  if (pg_get_line_buf(fstate->fp, &fstate->linebuff))
394  {
395  const char *str = fstate->linebuff.data;
396  const char *keyword;
397  int size;
398  PQExpBufferData pattern;
399 
400  fstate->lineno++;
401 
402  /* Skip initial white spaces */
403  while (isspace((unsigned char) *str))
404  str++;
405 
406  /*
407  * Skip empty lines or lines where the first non-whitespace character
408  * is a hash indicating a comment.
409  */
410  if (*str != '\0' && *str != '#')
411  {
412  /*
413  * First we expect sequence of two keywords, {include|exclude}
414  * followed by the object type to operate on.
415  */
416  keyword = filter_get_keyword(&str, &size);
417  if (!keyword)
418  {
419  pg_log_filter_error(fstate,
420  _("no filter command found (expected \"include\" or \"exclude\")"));
421  fstate->exit_nicely(1);
422  }
423 
424  if (is_keyword_str("include", keyword, size))
425  *comtype = FILTER_COMMAND_TYPE_INCLUDE;
426  else if (is_keyword_str("exclude", keyword, size))
427  *comtype = FILTER_COMMAND_TYPE_EXCLUDE;
428  else
429  {
430  pg_log_filter_error(fstate,
431  _("invalid filter command (expected \"include\" or \"exclude\")"));
432  fstate->exit_nicely(1);
433  }
434 
435  keyword = filter_get_keyword(&str, &size);
436  if (!keyword)
437  {
438  pg_log_filter_error(fstate, _("missing filter object type"));
439  fstate->exit_nicely(1);
440  }
441 
442  if (!get_object_type(keyword, size, objtype))
443  {
444  pg_log_filter_error(fstate,
445  _("unsupported filter object type: \"%.*s\""), size, keyword);
446  fstate->exit_nicely(1);
447  }
448 
449  initPQExpBuffer(&pattern);
450 
451  str = read_pattern(fstate, str, &pattern);
452  *objname = pattern.data;
453  }
454  else
455  {
456  *objname = NULL;
457  *comtype = FILTER_COMMAND_TYPE_NONE;
458  *objtype = FILTER_OBJECT_TYPE_NONE;
459  }
460 
461  return true;
462  }
463 
464  if (ferror(fstate->fp))
465  {
466  pg_log_error("could not read from filter file \"%s\": %m", fstate->filename);
467  fstate->exit_nicely(1);
468  }
469 
470  return false;
471 }
#define pg_unreachable()
Definition: c.h:283
#define _(x)
Definition: elog.c:90
static bool get_object_type(const char *keyword, int size, FilterObjectType *objtype)
Definition: filter.c:123
void filter_init(FilterStateData *fstate, const char *filename, exit_function f_exit)
Definition: filter.c:37
void filter_free(FilterStateData *fstate)
Definition: filter.c:61
static const char * read_quoted_string(FilterStateData *fstate, const char *str, PQExpBuffer pattern)
Definition: filter.c:213
#define is_keyword_str(cstr, str, bytes)
Definition: filter.c:23
bool filter_read_item(FilterStateData *fstate, char **objname, FilterCommandType *comtype, FilterObjectType *objtype)
Definition: filter.c:388
void pg_log_filter_error(FilterStateData *fstate, const char *fmt,...)
Definition: filter.c:155
static const char * filter_get_keyword(const char **line, int *size)
Definition: filter.c:179
const char * filter_object_type_name(FilterObjectType fot)
Definition: filter.c:83
static const char * read_pattern(FilterStateData *fstate, const char *str, PQExpBuffer pattern)
Definition: filter.c:298
FilterObjectType
Definition: filter.h:48
@ FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN
Definition: filter.h:51
@ FILTER_OBJECT_TYPE_SCHEMA
Definition: filter.h:57
@ FILTER_OBJECT_TYPE_INDEX
Definition: filter.h:56
@ FILTER_OBJECT_TYPE_TRIGGER
Definition: filter.h:60
@ FILTER_OBJECT_TYPE_FOREIGN_DATA
Definition: filter.h:54
@ FILTER_OBJECT_TYPE_DATABASE
Definition: filter.h:52
@ FILTER_OBJECT_TYPE_FUNCTION
Definition: filter.h:55
@ FILTER_OBJECT_TYPE_TABLE_DATA
Definition: filter.h:50
@ FILTER_OBJECT_TYPE_NONE
Definition: filter.h:49
@ FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN
Definition: filter.h:59
@ FILTER_OBJECT_TYPE_EXTENSION
Definition: filter.h:53
@ FILTER_OBJECT_TYPE_TABLE
Definition: filter.h:58
FilterCommandType
Definition: filter.h:38
@ FILTER_COMMAND_TYPE_NONE
Definition: filter.h:39
@ FILTER_COMMAND_TYPE_EXCLUDE
Definition: filter.h:41
@ FILTER_COMMAND_TYPE_INCLUDE
Definition: filter.h:40
void(* exit_function)(int status)
Definition: filter.h:20
#define free(a)
Definition: header.h:65
static void const char * fmt
va_end(args)
Assert(fmt[strlen(fmt) - 1] !='\n')
va_start(args, fmt)
#define pg_log_error(...)
Definition: logging.h:106
static char * filename
Definition: pg_dumpall.c:121
bool pg_get_line_buf(FILE *stream, StringInfo buf)
Definition: pg_get_line.c:95
static char * buf
Definition: pg_test_fsync.c:73
#define vsnprintf
Definition: port.h:237
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
static pg_noinline void Size size
Definition: slab.c:607
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
exit_function exit_nicely
Definition: filter.h:29
StringInfoData linebuff
Definition: filter.h:31
const char * filename
Definition: filter.h:28
FILE * fp
Definition: filter.h:27