PostgreSQL Source Code  git master
compression.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * compression.c
4  *
5  * Shared code for compression methods and specifications.
6  *
7  * A compression specification specifies the parameters that should be used
8  * when performing compression with a specific algorithm. The simplest
9  * possible compression specification is an integer, which sets the
10  * compression level.
11  *
12  * Otherwise, a compression specification is a comma-separated list of items,
13  * each having the form keyword or keyword=value.
14  *
15  * Currently, the only supported keywords are "level" and "workers".
16  *
17  * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
18  *
19  * IDENTIFICATION
20  * src/common/compression.c
21  *-------------------------------------------------------------------------
22  */
23 
24 #ifndef FRONTEND
25 #include "postgres.h"
26 #else
27 #include "postgres_fe.h"
28 #endif
29 
30 #include "common/compression.h"
31 
32 static int expect_integer_value(char *keyword, char *value,
34 
35 /*
36  * Look up a compression algorithm by name. Returns true and sets *algorithm
37  * if the name is recognized. Otherwise returns false.
38  */
39 bool
41 {
42  if (strcmp(name, "none") == 0)
43  *algorithm = PG_COMPRESSION_NONE;
44  else if (strcmp(name, "gzip") == 0)
45  *algorithm = PG_COMPRESSION_GZIP;
46  else if (strcmp(name, "lz4") == 0)
47  *algorithm = PG_COMPRESSION_LZ4;
48  else if (strcmp(name, "zstd") == 0)
49  *algorithm = PG_COMPRESSION_ZSTD;
50  else
51  return false;
52  return true;
53 }
54 
55 /*
56  * Get the human-readable name corresponding to a particular compression
57  * algorithm.
58  */
59 const char *
61 {
62  switch (algorithm)
63  {
65  return "none";
67  return "gzip";
68  case PG_COMPRESSION_LZ4:
69  return "lz4";
71  return "zstd";
72  /* no default, to provoke compiler warnings if values are added */
73  }
74  Assert(false);
75  return "???"; /* placate compiler */
76 }
77 
78 /*
79  * Parse a compression specification for a specified algorithm.
80  *
81  * See the file header comments for a brief description of what a compression
82  * specification is expected to look like.
83  *
84  * On return, all fields of the result object will be initialized.
85  * In particular, result->parse_error will be NULL if no errors occurred
86  * during parsing, and will otherwise contain an appropriate error message.
87  * The caller may free this error message string using pfree, if desired.
88  * Note, however, even if there's no parse error, the string might not make
89  * sense: e.g. for gzip, level=12 is not sensible, but it does parse OK.
90  *
91  * Use validate_compress_specification() to find out whether a compression
92  * specification is semantically sensible.
93  */
94 void
95 parse_compress_specification(pg_compress_algorithm algorithm, char *specification,
97 {
98  int bare_level;
99  char *bare_level_endp;
100 
101  /* Initial setup of result object. */
102  result->algorithm = algorithm;
103  result->options = 0;
104  result->level = -1;
105  result->parse_error = NULL;
106 
107  /* If there is no specification, we're done already. */
108  if (specification == NULL)
109  return;
110 
111  /* As a special case, the specification can be a bare integer. */
112  bare_level = strtol(specification, &bare_level_endp, 10);
113  if (specification != bare_level_endp && *bare_level_endp == '\0')
114  {
115  result->level = bare_level;
117  return;
118  }
119 
120  /* Look for comma-separated keyword or keyword=value entries. */
121  while (1)
122  {
123  char *kwstart;
124  char *kwend;
125  char *vstart;
126  char *vend;
127  int kwlen;
128  int vlen;
129  bool has_value;
130  char *keyword;
131  char *value;
132 
133  /* Figure start, end, and length of next keyword and any value. */
134  kwstart = kwend = specification;
135  while (*kwend != '\0' && *kwend != ',' && *kwend != '=')
136  ++kwend;
137  kwlen = kwend - kwstart;
138  if (*kwend != '=')
139  {
140  vstart = vend = NULL;
141  vlen = 0;
142  has_value = false;
143  }
144  else
145  {
146  vstart = vend = kwend + 1;
147  while (*vend != '\0' && *vend != ',')
148  ++vend;
149  vlen = vend - vstart;
150  has_value = true;
151  }
152 
153  /* Reject empty keyword. */
154  if (kwlen == 0)
155  {
156  result->parse_error =
157  pstrdup(_("found empty string where a compression option was expected"));
158  break;
159  }
160 
161  /* Extract keyword and value as separate C strings. */
162  keyword = palloc(kwlen + 1);
163  memcpy(keyword, kwstart, kwlen);
164  keyword[kwlen] = '\0';
165  if (!has_value)
166  value = NULL;
167  else
168  {
169  value = palloc(vlen + 1);
170  memcpy(value, vstart, vlen);
171  value[vlen] = '\0';
172  }
173 
174  /* Handle whatever keyword we found. */
175  if (strcmp(keyword, "level") == 0)
176  {
177  result->level = expect_integer_value(keyword, value, result);
179  }
180  else if (strcmp(keyword, "workers") == 0)
181  {
182  result->workers = expect_integer_value(keyword, value, result);
184  }
185  else
186  result->parse_error =
187  psprintf(_("unknown compression option \"%s\""), keyword);
188 
189  /* Release memory, just to be tidy. */
190  pfree(keyword);
191  if (value != NULL)
192  pfree(value);
193 
194  /*
195  * If we got an error or have reached the end of the string, stop.
196  *
197  * If there is no value, then the end of the keyword might have been
198  * the end of the string. If there is a value, then the end of the
199  * keyword cannot have been the end of the string, but the end of the
200  * value might have been.
201  */
202  if (result->parse_error != NULL ||
203  (vend == NULL ? *kwend == '\0' : *vend == '\0'))
204  break;
205 
206  /* Advance to next entry and loop around. */
207  specification = vend == NULL ? kwend + 1 : vend + 1;
208  }
209 }
210 
211 /*
212  * Parse 'value' as an integer and return the result.
213  *
214  * If parsing fails, set result->parse_error to an appropriate message
215  * and return -1.
216  */
217 static int
219 {
220  int ivalue;
221  char *ivalue_endp;
222 
223  if (value == NULL)
224  {
225  result->parse_error =
226  psprintf(_("compression option \"%s\" requires a value"),
227  keyword);
228  return -1;
229  }
230 
231  ivalue = strtol(value, &ivalue_endp, 10);
232  if (ivalue_endp == value || *ivalue_endp != '\0')
233  {
234  result->parse_error =
235  psprintf(_("value for compression option \"%s\" must be an integer"),
236  keyword);
237  return -1;
238  }
239  return ivalue;
240 }
241 
242 /*
243  * Returns NULL if the compression specification string was syntactically
244  * valid and semantically sensible. Otherwise, returns an error message.
245  *
246  * Does not test whether this build of PostgreSQL supports the requested
247  * compression method.
248  */
249 char *
251 {
252  /* If it didn't even parse OK, it's definitely no good. */
253  if (spec->parse_error != NULL)
254  return spec->parse_error;
255 
256  /*
257  * If a compression level was specified, check that the algorithm expects
258  * a compression level and that the level is within the legal range for
259  * the algorithm.
260  */
261  if ((spec->options & PG_COMPRESSION_OPTION_LEVEL) != 0)
262  {
263  int min_level = 1;
264  int max_level;
265 
266  if (spec->algorithm == PG_COMPRESSION_GZIP)
267  max_level = 9;
268  else if (spec->algorithm == PG_COMPRESSION_LZ4)
269  max_level = 12;
270  else if (spec->algorithm == PG_COMPRESSION_ZSTD)
271  max_level = 22;
272  else
273  return psprintf(_("compression algorithm \"%s\" does not accept a compression level"),
275 
276  if (spec->level < min_level || spec->level > max_level)
277  return psprintf(_("compression algorithm \"%s\" expects a compression level between %d and %d"),
279  min_level, max_level);
280  }
281 
282  /*
283  * Of the compression algorithms that we currently support, only zstd
284  * allows parallel workers.
285  */
286  if ((spec->options & PG_COMPRESSION_OPTION_WORKERS) != 0 &&
287  (spec->algorithm != PG_COMPRESSION_ZSTD))
288  {
289  return psprintf(_("compression algorithm \"%s\" does not accept a worker count"),
291  }
292 
293  return NULL;
294 }
bool parse_compress_algorithm(char *name, pg_compress_algorithm *algorithm)
Definition: compression.c:40
const char * get_compress_algorithm_name(pg_compress_algorithm algorithm)
Definition: compression.c:60
static int expect_integer_value(char *keyword, char *value, pg_compress_specification *result)
Definition: compression.c:218
void parse_compress_specification(pg_compress_algorithm algorithm, char *specification, pg_compress_specification *result)
Definition: compression.c:95
char * validate_compress_specification(pg_compress_specification *spec)
Definition: compression.c:250
#define PG_COMPRESSION_OPTION_LEVEL
Definition: compression.h:25
#define PG_COMPRESSION_OPTION_WORKERS
Definition: compression.h:26
pg_compress_algorithm
Definition: compression.h:18
@ PG_COMPRESSION_GZIP
Definition: compression.h:20
@ PG_COMPRESSION_LZ4
Definition: compression.h:21
@ PG_COMPRESSION_NONE
Definition: compression.h:19
@ PG_COMPRESSION_ZSTD
Definition: compression.h:22
#define _(x)
Definition: elog.c:89
const char * name
Definition: encode.c:561
static struct @151 value
Assert(fmt[strlen(fmt) - 1] !='\n')
char * pstrdup(const char *in)
Definition: mcxt.c:1305
void pfree(void *pointer)
Definition: mcxt.c:1175
void * palloc(Size size)
Definition: mcxt.c:1068
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
pg_compress_algorithm algorithm
Definition: compression.h:30