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/logging.h"
17 #include "common/string.h"
18 #include "filter.h"
19 #include "lib/stringinfo.h"
20 #include "pqexpbuffer.h"
21 
22 #define is_keyword_str(cstr, str, bytes) \
23  ((strlen(cstr) == (bytes)) && (pg_strncasecmp((cstr), (str), (bytes)) == 0))
24 
25 /*
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.
30  */
31 
32 /*
33  * Opens filter's file and initialize fstate structure.
34  */
35 void
36 filter_init(FilterStateData *fstate, const char *filename, exit_function f_exit)
37 {
38  fstate->filename = filename;
39  fstate->lineno = 0;
40  fstate->exit_nicely = f_exit;
41  initStringInfo(&fstate->linebuff);
42 
43  if (strcmp(filename, "-") != 0)
44  {
45  fstate->fp = fopen(filename, "r");
46  if (!fstate->fp)
47  {
48  pg_log_error("could not open filter file \"%s\": %m", filename);
49  fstate->exit_nicely(1);
50  }
51  }
52  else
53  fstate->fp = stdin;
54 }
55 
56 /*
57  * Release allocated resources for the given filter.
58  */
59 void
61 {
62  if (!fstate)
63  return;
64 
65  free(fstate->linebuff.data);
66  fstate->linebuff.data = NULL;
67 
68  if (fstate->fp && fstate->fp != stdin)
69  {
70  if (fclose(fstate->fp) != 0)
71  pg_log_error("could not close filter file \"%s\": %m", fstate->filename);
72 
73  fstate->fp = NULL;
74  }
75 }
76 
77 /*
78  * Translate FilterObjectType enum to string. The main purpose is for error
79  * message formatting.
80  */
81 const char *
83 {
84  switch (fot)
85  {
87  return "comment or empty line";
89  return "table data";
91  return "table data and children";
93  return "database";
95  return "extension";
97  return "foreign data";
99  return "function";
101  return "index";
103  return "schema";
105  return "table";
107  return "table and children";
109  return "trigger";
110  }
111 
112  /* should never get here */
113  pg_unreachable();
114 }
115 
116 /*
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.
120  */
121 static bool
122 get_object_type(const char *keyword, int size, FilterObjectType *objtype)
123 {
124  if (is_keyword_str("table_data", keyword, size))
126  else if (is_keyword_str("table_data_and_children", keyword, size))
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))
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))
144  else if (is_keyword_str("trigger", keyword, size))
145  *objtype = FILTER_OBJECT_TYPE_TRIGGER;
146  else
147  return false;
148 
149  return true;
150 }
151 
152 
153 void
154 pg_log_filter_error(FilterStateData *fstate, const char *fmt,...)
155 {
156  va_list argp;
157  char buf[256];
158 
159  va_start(argp, fmt);
160  vsnprintf(buf, sizeof(buf), fmt, argp);
161  va_end(argp);
162 
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);
166  else
167  pg_log_error("invalid format in filter read from file \"%s\" on line %d: %s",
168  fstate->filename, fstate->lineno, buf);
169 }
170 
171 /*
172  * filter_get_keyword - read the next filter keyword from buffer
173  *
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.
178  */
179 static const char *
180 filter_get_keyword(const char **line, int *size)
181 {
182  const char *ptr = *line;
183  const char *result = NULL;
184 
185  /* Set returned length preemptively in case no keyword is found */
186  *size = 0;
187 
188  /* Skip initial whitespace */
189  while (isspace((unsigned char) *ptr))
190  ptr++;
191 
192  if (isalpha((unsigned char) *ptr))
193  {
194  result = ptr++;
195 
196  while (isalpha((unsigned char) *ptr) || *ptr == '_')
197  ptr++;
198 
199  *size = ptr - result;
200  }
201 
202  *line = ptr;
203 
204  return result;
205 }
206 
207 /*
208  * read_quoted_string - read quoted possibly multi line string
209  *
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.
212  */
213 static const char *
215  const char *str,
216  PQExpBuffer pattern)
217 {
218  appendPQExpBufferChar(pattern, '"');
219  str++;
220 
221  while (1)
222  {
223  /*
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.
226  */
227  if (*str == '\r' || *str == '\n')
228  {
229  str++;
230  continue;
231  }
232 
233  if (*str == '\0')
234  {
235  Assert(fstate->linebuff.data);
236 
237  if (!pg_get_line_buf(fstate->fp, &fstate->linebuff))
238  {
239  if (ferror(fstate->fp))
240  pg_log_error("could not read from filter file \"%s\": %m",
241  fstate->filename);
242  else
243  pg_log_filter_error(fstate, _("unexpected end of file"));
244 
245  fstate->exit_nicely(1);
246  }
247 
248  str = fstate->linebuff.data;
249 
250  appendPQExpBufferChar(pattern, '\n');
251  fstate->lineno++;
252  }
253 
254  if (*str == '"')
255  {
256  appendPQExpBufferChar(pattern, '"');
257  str++;
258 
259  if (*str == '"')
260  {
261  appendPQExpBufferChar(pattern, '"');
262  str++;
263  }
264  else
265  break;
266  }
267  else if (*str == '\\')
268  {
269  str++;
270  if (*str == 'n')
271  appendPQExpBufferChar(pattern, '\n');
272  else if (*str == '\\')
273  appendPQExpBufferChar(pattern, '\\');
274 
275  str++;
276  }
277  else
278  appendPQExpBufferChar(pattern, *str++);
279  }
280 
281  return str;
282 }
283 
284 /*
285  * read_pattern - reads on object pattern from input
286  *
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.
294  *
295  * Returns a pointer to next character after the found identifier and exits
296  * on error.
297  */
298 static const char *
299 read_pattern(FilterStateData *fstate, const char *str, PQExpBuffer pattern)
300 {
301  bool skip_space = true;
302  bool found_space = false;
303 
304  /* Skip initial whitespace */
305  while (isspace((unsigned char) *str))
306  str++;
307 
308  if (*str == '\0')
309  {
310  pg_log_filter_error(fstate, _("missing object name pattern"));
311  fstate->exit_nicely(1);
312  }
313 
314  while (*str && *str != '#')
315  {
316  while (*str && !isspace((unsigned char) *str) && !strchr("#,.()\"", *str))
317  {
318  /*
319  * Append space only when it is allowed, and when it was found in
320  * original string.
321  */
322  if (!skip_space && found_space)
323  {
324  appendPQExpBufferChar(pattern, ' ');
325  skip_space = true;
326  }
327 
328  appendPQExpBufferChar(pattern, *str++);
329  }
330 
331  skip_space = false;
332 
333  if (*str == '"')
334  {
335  if (found_space)
336  appendPQExpBufferChar(pattern, ' ');
337 
338  str = read_quoted_string(fstate, str, pattern);
339  }
340  else if (*str == ',')
341  {
342  appendPQExpBufferStr(pattern, ", ");
343  skip_space = true;
344  str++;
345  }
346  else if (*str && strchr(".()", *str))
347  {
348  appendPQExpBufferChar(pattern, *str++);
349  skip_space = true;
350  }
351 
352  found_space = false;
353 
354  /* skip ending whitespaces */
355  while (isspace((unsigned char) *str))
356  {
357  found_space = true;
358  str++;
359  }
360  }
361 
362  return str;
363 }
364 
365 /*
366  * filter_read_item - Read command/type/pattern triplet from a filter file
367  *
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:
371  *
372  * <command> <object_type> <pattern>
373  *
374  * command can be "include" or "exclude".
375  *
376  * Supported object types are described by enum FilterObjectType
377  * (see function get_object_type).
378  *
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.
382  *
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.
387  */
388 bool
390  char **objname,
391  FilterCommandType *comtype,
392  FilterObjectType *objtype)
393 {
394  if (pg_get_line_buf(fstate->fp, &fstate->linebuff))
395  {
396  const char *str = fstate->linebuff.data;
397  const char *keyword;
398  int size;
399  PQExpBufferData pattern;
400 
401  fstate->lineno++;
402 
403  /* Skip initial white spaces */
404  while (isspace((unsigned char) *str))
405  str++;
406 
407  /*
408  * Skip empty lines or lines where the first non-whitespace character
409  * is a hash indicating a comment.
410  */
411  if (*str != '\0' && *str != '#')
412  {
413  /*
414  * First we expect sequence of two keywords, {include|exclude}
415  * followed by the object type to operate on.
416  */
417  keyword = filter_get_keyword(&str, &size);
418  if (!keyword)
419  {
420  pg_log_filter_error(fstate,
421  _("no filter command found (expected \"include\" or \"exclude\")"));
422  fstate->exit_nicely(1);
423  }
424 
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;
429  else
430  {
431  pg_log_filter_error(fstate,
432  _("invalid filter command (expected \"include\" or \"exclude\")"));
433  fstate->exit_nicely(1);
434  }
435 
436  keyword = filter_get_keyword(&str, &size);
437  if (!keyword)
438  {
439  pg_log_filter_error(fstate, _("missing filter object type"));
440  fstate->exit_nicely(1);
441  }
442 
443  if (!get_object_type(keyword, size, objtype))
444  {
445  pg_log_filter_error(fstate,
446  _("unsupported filter object type: \"%.*s\""), size, keyword);
447  fstate->exit_nicely(1);
448  }
449 
450  initPQExpBuffer(&pattern);
451 
452  str = read_pattern(fstate, str, &pattern);
453  *objname = pattern.data;
454  }
455  else
456  {
457  *objname = NULL;
458  *comtype = FILTER_COMMAND_TYPE_NONE;
459  *objtype = FILTER_OBJECT_TYPE_NONE;
460  }
461 
462  return true;
463  }
464 
465  if (ferror(fstate->fp))
466  {
467  pg_log_error("could not read from filter file \"%s\": %m", fstate->filename);
468  fstate->exit_nicely(1);
469  }
470 
471  return false;
472 }
#define Assert(condition)
Definition: c.h:812
#define pg_unreachable()
Definition: c.h:315
#define _(x)
Definition: elog.c:90
static bool get_object_type(const char *keyword, int size, FilterObjectType *objtype)
Definition: filter.c:122
void filter_init(FilterStateData *fstate, const char *filename, exit_function f_exit)
Definition: filter.c:36
void filter_free(FilterStateData *fstate)
Definition: filter.c:60
static const char * read_quoted_string(FilterStateData *fstate, const char *str, PQExpBuffer pattern)
Definition: filter.c:214
#define is_keyword_str(cstr, str, bytes)
Definition: filter.c:22
bool filter_read_item(FilterStateData *fstate, char **objname, FilterCommandType *comtype, FilterObjectType *objtype)
Definition: filter.c:389
void pg_log_filter_error(FilterStateData *fstate, const char *fmt,...)
Definition: filter.c:154
static const char * filter_get_keyword(const char **line, int *size)
Definition: filter.c:180
const char * filter_object_type_name(FilterObjectType fot)
Definition: filter.c:82
static const char * read_pattern(FilterStateData *fstate, const char *str, PQExpBuffer pattern)
Definition: filter.c:299
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
const char * str
#define free(a)
Definition: header.h:65
static void const char * fmt
va_end(args)
va_start(args, fmt)
#define pg_log_error(...)
Definition: logging.h:106
static char * filename
Definition: pg_dumpall.c:119
bool pg_get_line_buf(FILE *stream, StringInfo buf)
Definition: pg_get_line.c:95
static char * buf
Definition: pg_test_fsync.c:72
#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:56
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