/* * We do handle case-insensitive matching for single-byte encodings using * fold-on-the-fly processing, however. */ static char SB_lower_char(unsigned char c, pg_locale_t locale, bool locale_is_c) { if (locale_is_c) return pg_ascii_tolower(c); #ifdef HAVE_LOCALE_T else if (locale) return tolower_l(c, locale); #endif else return pg_tolower(c); }
/* * dir_strcmp: strcmp except any two DIR_SEP characters are considered equal, * and we honor filesystem case insensitivity if known */ static int dir_strcmp(const char *s1, const char *s2) { while (*s1 && *s2) { if ( #ifndef WIN32 *s1 != *s2 #else /* On windows, paths are case-insensitive */ pg_tolower((unsigned char) *s1) != pg_tolower((unsigned char) *s2) #endif && !(IS_DIR_SEP(*s1) && IS_DIR_SEP(*s2))) return (int) *s1 - (int) *s2; s1++, s2++; } if (*s1) return 1; /* s1 longer */ if (*s2) return -1; /* s2 longer */ return 0; }
/* * pg_an_to_ln -- return the local name corresponding to an authentication * name * * XXX Assumes that the first aname component is the user name. This is NOT * necessarily so, since an aname can actually be something out of your * worst X.400 nightmare, like * ORGANIZATION=U. C. Berkeley/NAME=Paul M. [email protected] * Note that the MIT an_to_ln code does the same thing if you don't * provide an aname mapping database...it may be a better idea to use * krb5_an_to_ln, except that it punts if multiple components are found, * and we can't afford to punt. * * For WIN32, convert username to lowercase because the Win32 kerberos library * generates tickets with the username as the user entered it instead of as * it is entered in the directory. */ static char * pg_an_to_ln(char *aname) { char *p; if ((p = strchr(aname, '/')) || (p = strchr(aname, '@'))) *p = '\0'; #ifdef WIN32 for (p = aname; *p; p++) *p = pg_tolower((unsigned char) *p); #endif return aname; }
/* * Apply additional validation checks to a tzEntry * * Returns TRUE if OK, else false */ static bool validateTzEntry(tzEntry *tzentry) { unsigned char *p; /* * Check restrictions imposed by datetkntbl storage format (see * datetime.c) */ if (strlen(tzentry->abbrev) > TOKMAXLEN) { ereport(tz_elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d", tzentry->abbrev, TOKMAXLEN, tzentry->filename, tzentry->lineno))); return false; } if (tzentry->offset % 900 != 0) { ereport(tz_elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone offset %d is not a multiple of 900 sec (15 min) in time zone file \"%s\", line %d", tzentry->offset, tzentry->filename, tzentry->lineno))); return false; } /* * Sanity-check the offset: shouldn't exceed 14 hours */ if (tzentry->offset > 14 * 60 * 60 || tzentry->offset < -14 * 60 * 60) { ereport(tz_elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d", tzentry->offset, tzentry->filename, tzentry->lineno))); return false; } /* * Convert abbrev to lowercase (must match datetime.c's conversion) */ for (p = (unsigned char *) tzentry->abbrev; *p; p++) *p = pg_tolower(*p); return true; }
/* * Apply additional validation checks to a tzEntry * * Returns TRUE if OK, else false */ static bool validateTzEntry(tzEntry *tzentry) { unsigned char *p; /* * Check restrictions imposed by datetkntbl storage format (see * datetime.c) */ if (strlen(tzentry->abbrev) > TOKMAXLEN) { GUC_check_errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d", tzentry->abbrev, TOKMAXLEN, tzentry->filename, tzentry->lineno); return false; } /* * Sanity-check the offset: shouldn't exceed 14 hours */ if (tzentry->offset > 14 * 60 * 60 || tzentry->offset < -14 * 60 * 60) { GUC_check_errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d", tzentry->offset, tzentry->filename, tzentry->lineno); return false; } /* * Convert abbrev to lowercase (must match datetime.c's conversion) */ for (p = (unsigned char *) tzentry->abbrev; *p; p++) *p = pg_tolower(*p); return true; }
/* * processSQLNamePattern * * Scan a wildcard-pattern string and generate appropriate WHERE clauses * to limit the set of objects returned. The WHERE clauses are appended * to the already-partially-constructed query in buf. Returns whether * any clause was added. * * conn: connection query will be sent to (consulted for escaping rules). * buf: output parameter. * pattern: user-specified pattern option, or NULL if none ("*" is implied). * have_where: true if caller already emitted "WHERE" (clauses will be ANDed * onto the existing WHERE clause). * force_escape: always quote regexp special characters, even outside * double quotes (else they are quoted only between double quotes). * schemavar: name of query variable to match against a schema-name pattern. * Can be NULL if no schema. * namevar: name of query variable to match against an object-name pattern. * altnamevar: NULL, or name of an alternative variable to match against name. * visibilityrule: clause to use if we want to restrict to visible objects * (for example, "pg_catalog.pg_table_is_visible(p.oid)"). Can be NULL. * * Formatting note: the text already present in buf should end with a newline. * The appended text, if any, will end with one too. */ bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule) { PQExpBufferData schemabuf; PQExpBufferData namebuf; int encoding = PQclientEncoding(conn); bool inquotes; const char *cp; int i; bool added_clause = false; #define WHEREAND() \ (appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \ have_where = true, added_clause = true) if (pattern == NULL) { /* Default: select all visible objects */ if (visibilityrule) { WHEREAND(); appendPQExpBuffer(buf, "%s\n", visibilityrule); } return added_clause; } initPQExpBuffer(&schemabuf); initPQExpBuffer(&namebuf); /* * Parse the pattern, converting quotes and lower-casing unquoted letters. * Also, adjust shell-style wildcard characters into regexp notation. * * We surround the pattern with "^(...)$" to force it to match the whole * string, as per SQL practice. We have to have parens in case the string * contains "|", else the "^" and "$" will be bound into the first and * last alternatives which is not what we want. * * Note: the result of this pass is the actual regexp pattern(s) we want * to execute. Quoting/escaping into SQL literal format will be done * below using appendStringLiteralConn(). */ appendPQExpBufferStr(&namebuf, "^("); inquotes = false; cp = pattern; while (*cp) { char ch = *cp; if (ch == '"') { if (inquotes && cp[1] == '"') { /* emit one quote, stay in inquotes mode */ appendPQExpBufferChar(&namebuf, '"'); cp++; } else inquotes = !inquotes; cp++; } else if (!inquotes && isupper((unsigned char) ch)) { appendPQExpBufferChar(&namebuf, pg_tolower((unsigned char) ch)); cp++; } else if (!inquotes && ch == '*') { appendPQExpBufferStr(&namebuf, ".*"); cp++; } else if (!inquotes && ch == '?') { appendPQExpBufferChar(&namebuf, '.'); cp++; } else if (!inquotes && ch == '.') { /* Found schema/name separator, move current pattern to schema */ resetPQExpBuffer(&schemabuf); appendPQExpBufferStr(&schemabuf, namebuf.data); resetPQExpBuffer(&namebuf); appendPQExpBufferStr(&namebuf, "^("); cp++; } else if (ch == '$') { /* * Dollar is always quoted, whether inside quotes or not. The * reason is that it's allowed in SQL identifiers, so there's a * significant use-case for treating it literally, while because * we anchor the pattern automatically there is no use-case for * having it possess its regexp meaning. */ appendPQExpBufferStr(&namebuf, "\\$"); cp++; } else { /* * Ordinary data character, transfer to pattern * * Inside double quotes, or at all times if force_escape is true, * quote regexp special characters with a backslash to avoid * regexp errors. Outside quotes, however, let them pass through * as-is; this lets knowledgeable users build regexp expressions * that are more powerful than shell-style patterns. */ if ((inquotes || force_escape) && strchr("|*+?()[]{}.^$\\", ch)) appendPQExpBufferChar(&namebuf, '\\'); i = PQmblen(cp, encoding); while (i-- && *cp) { appendPQExpBufferChar(&namebuf, *cp); cp++; } } } /* * Now decide what we need to emit. Note there will be a leading "^(" in * the patterns in any case. */ if (namebuf.len > 2) { /* We have a name pattern, so constrain the namevar(s) */ appendPQExpBufferStr(&namebuf, ")$"); /* Optimize away a "*" pattern */ if (strcmp(namebuf.data, "^(.*)$") != 0) { WHEREAND(); if (altnamevar) { appendPQExpBuffer(buf, "(%s ~ ", namevar); appendStringLiteralConn(buf, namebuf.data, conn); appendPQExpBuffer(buf, "\n OR %s ~ ", altnamevar); appendStringLiteralConn(buf, namebuf.data, conn); appendPQExpBufferStr(buf, ")\n"); } else { appendPQExpBuffer(buf, "%s ~ ", namevar); appendStringLiteralConn(buf, namebuf.data, conn); appendPQExpBufferChar(buf, '\n'); } } } if (schemabuf.len > 2) { /* We have a schema pattern, so constrain the schemavar */ appendPQExpBufferStr(&schemabuf, ")$"); /* Optimize away a "*" pattern */ if (strcmp(schemabuf.data, "^(.*)$") != 0 && schemavar) { WHEREAND(); appendPQExpBuffer(buf, "%s ~ ", schemavar); appendStringLiteralConn(buf, schemabuf.data, conn); appendPQExpBufferChar(buf, '\n'); } } else { /* No schema pattern given, so select only visible objects */ if (visibilityrule) { WHEREAND(); appendPQExpBuffer(buf, "%s\n", visibilityrule); } } termPQExpBuffer(&schemabuf); termPQExpBuffer(&namebuf); return added_clause; #undef WHEREAND }
/* * PGTYPESdate_defmt_asc * * function works as follows: * - first we analyze the paramters * - if this is a special case with no delimiters, add delimters * - find the tokens. First we look for numerical values. If we have found * less than 3 tokens, we check for the months' names and thereafter for * the abbreviations of the months' names. * - then we see which parameter should be the date, the month and the * year and from these values we calculate the date */ #define PGTYPES_DATE_MONTH_MAXLENGTH 20 /* probably even less :-) */ int PGTYPESdate_defmt_asc(date * d, char *fmt, char *str) { /* * token[2] = { 4,6 } means that token 2 starts at position 4 and ends at * (including) position 6 */ int token[3][2]; int token_values[3] = {-1, -1, -1}; char *fmt_token_order; char *fmt_ystart, *fmt_mstart, *fmt_dstart; int i; int reading_digit; int token_count; char *str_copy; struct tm tm; tm.tm_year = tm.tm_mon = tm.tm_mday = 0; /* keep compiler quiet */ if (!d || !str || !fmt) { errno = PGTYPES_DATE_ERR_EARGS; return -1; } /* analyze the fmt string */ fmt_ystart = strstr(fmt, "yy"); fmt_mstart = strstr(fmt, "mm"); fmt_dstart = strstr(fmt, "dd"); if (!fmt_ystart || !fmt_mstart || !fmt_dstart) { errno = PGTYPES_DATE_ERR_EARGS; return -1; } if (fmt_ystart < fmt_mstart) { /* y m */ if (fmt_dstart < fmt_ystart) { /* d y m */ fmt_token_order = "dym"; } else if (fmt_dstart > fmt_mstart) { /* y m d */ fmt_token_order = "ymd"; } else { /* y d m */ fmt_token_order = "ydm"; } } else { /* fmt_ystart > fmt_mstart */ /* m y */ if (fmt_dstart < fmt_mstart) { /* d m y */ fmt_token_order = "dmy"; } else if (fmt_dstart > fmt_ystart) { /* m y d */ fmt_token_order = "myd"; } else { /* m d y */ fmt_token_order = "mdy"; } } /* * handle the special cases where there is no delimiter between the * digits. If we see this: * * only digits, 6 or 8 bytes then it might be ddmmyy and ddmmyyyy (or * similar) * * we reduce it to a string with delimiters and continue processing */ /* check if we have only digits */ reading_digit = 1; for (i = 0; str[i]; i++) { if (!isdigit((unsigned char) str[i])) { reading_digit = 0; break; } } if (reading_digit) { int frag_length[3]; int target_pos; i = strlen(str); if (i != 8 && i != 6) { errno = PGTYPES_DATE_ERR_ENOSHORTDATE; return -1; } /* okay, this really is the special case */ /* * as long as the string, one additional byte for the terminator and 2 * for the delimiters between the 3 fiedls */ str_copy = pgtypes_alloc(strlen(str) + 1 + 2); if (!str_copy) return -1; /* determine length of the fragments */ if (i == 6) { frag_length[0] = 2; frag_length[1] = 2; frag_length[2] = 2; } else { if (fmt_token_order[0] == 'y') { frag_length[0] = 4; frag_length[1] = 2; frag_length[2] = 2; } else if (fmt_token_order[1] == 'y') { frag_length[0] = 2; frag_length[1] = 4; frag_length[2] = 2; } else { frag_length[0] = 2; frag_length[1] = 2; frag_length[2] = 4; } } target_pos = 0; /* * XXX: Here we could calculate the positions of the tokens and save * the for loop down there where we again check with isdigit() for * digits. */ for (i = 0; i < 3; i++) { int start_pos = 0; if (i >= 1) start_pos += frag_length[0]; if (i == 2) start_pos += frag_length[1]; strncpy(str_copy + target_pos, str + start_pos, frag_length[i]); target_pos += frag_length[i]; if (i != 2) { str_copy[target_pos] = ' '; target_pos++; } } str_copy[target_pos] = '\0'; } else { str_copy = pgtypes_strdup(str); if (!str_copy) return -1; /* convert the whole string to lower case */ for (i = 0; str_copy[i]; i++) str_copy[i] = (char) pg_tolower((unsigned char) str_copy[i]); } /* look for numerical tokens */ reading_digit = 0; token_count = 0; for (i = 0; i < strlen(str_copy); i++) { if (!isdigit((unsigned char) str_copy[i]) && reading_digit) { /* the token is finished */ token[token_count][1] = i - 1; reading_digit = 0; token_count++; } else if (isdigit((unsigned char) str_copy[i]) && !reading_digit) { /* we have found a token */ token[token_count][0] = i; reading_digit = 1; } } /* * we're at the end of the input string, but maybe we are still reading a * number... */ if (reading_digit) { token[token_count][1] = i - 1; token_count++; } if (token_count < 2) { /* * not all tokens found, no way to find 2 missing tokens with string * matches */ free(str_copy); errno = PGTYPES_DATE_ERR_ENOSHORTDATE; return -1; } if (token_count != 3) { /* * not all tokens found but we may find another one with string * matches by testing for the months names and months abbreviations */ char *month_lower_tmp = pgtypes_alloc(PGTYPES_DATE_MONTH_MAXLENGTH); char *start_pos; int j; int offset; int found = 0; char **list; if (!month_lower_tmp) { /* free variables we alloc'ed before */ free(str_copy); return -1; } list = pgtypes_date_months; for (i = 0; list[i]; i++) { for (j = 0; j < PGTYPES_DATE_MONTH_MAXLENGTH; j++) { month_lower_tmp[j] = (char) pg_tolower((unsigned char) list[i][j]); if (!month_lower_tmp[j]) { /* properly terminated */ break; } } if ((start_pos = strstr(str_copy, month_lower_tmp))) { offset = start_pos - str_copy; /* * sort the new token into the numeric tokens, shift them if * necessary */ if (offset < token[0][0]) { token[2][0] = token[1][0]; token[2][1] = token[1][1]; token[1][0] = token[0][0]; token[1][1] = token[0][1]; token_count = 0; } else if (offset < token[1][0]) { token[2][0] = token[1][0]; token[2][1] = token[1][1]; token_count = 1; } else token_count = 2; token[token_count][0] = offset; token[token_count][1] = offset + strlen(month_lower_tmp) - 1; /* * the value is the index of the month in the array of months * + 1 (January is month 0) */ token_values[token_count] = i + 1; found = 1; break; } /* * evil[tm] hack: if we read the pgtypes_date_months and haven't * found a match, reset list to point to pgtypes_date_months_short * and reset the counter variable i */ if (list == pgtypes_date_months) { if (list[i + 1] == NULL) { list = months; i = -1; } } } if (!found) { free(month_lower_tmp); free(str_copy); errno = PGTYPES_DATE_ERR_ENOTDMY; return -1; } /* * here we found a month. token[token_count] and * token_values[token_count] reflect the month's details. * * only the month can be specified with a literal. Here we can do a * quick check if the month is at the right position according to the * format string because we can check if the token that we expect to * be the month is at the position of the only token that already has * a value. If we wouldn't check here we could say "December 4 1990" * with a fmt string of "dd mm yy" for 12 April 1990. */ if (fmt_token_order[token_count] != 'm') { /* deal with the error later on */ token_values[token_count] = -1; } free(month_lower_tmp); } /* terminate the tokens with ASCII-0 and get their values */ for (i = 0; i < 3; i++) { *(str_copy + token[i][1] + 1) = '\0'; /* A month already has a value set, check for token_value == -1 */ if (token_values[i] == -1) { errno = 0; token_values[i] = strtol(str_copy + token[i][0], (char **) NULL, 10); /* strtol sets errno in case of an error */ if (errno) token_values[i] = -1; } if (fmt_token_order[i] == 'd') tm.tm_mday = token_values[i]; else if (fmt_token_order[i] == 'm') tm.tm_mon = token_values[i]; else if (fmt_token_order[i] == 'y') tm.tm_year = token_values[i]; } free(str_copy); if (tm.tm_mday < 1 || tm.tm_mday > 31) { errno = PGTYPES_DATE_BAD_DAY; return -1; } if (tm.tm_mon < 1 || tm.tm_mon > MONTHS_PER_YEAR) { errno = PGTYPES_DATE_BAD_MONTH; return -1; } if (tm.tm_mday == 31 && (tm.tm_mon == 4 || tm.tm_mon == 6 || tm.tm_mon == 9 || tm.tm_mon == 11)) { errno = PGTYPES_DATE_BAD_DAY; return -1; } if (tm.tm_mon == 2 && tm.tm_mday > 29) { errno = PGTYPES_DATE_BAD_DAY; return -1; } *d = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - date2j(2000, 1, 1); return 0; }
void validate_and_adjust_options(StdRdOptions *result, relopt_value *options, int num_options, relopt_kind kind, bool validate) { int i; relopt_value *fillfactor_opt; relopt_value *appendonly_opt; relopt_value *blocksize_opt; relopt_value *comptype_opt; relopt_value *complevel_opt; relopt_value *checksum_opt; relopt_value *orientation_opt; /* fillfactor */ fillfactor_opt = get_option_set(options, num_options, SOPT_FILLFACTOR); if (fillfactor_opt != NULL) { result->fillfactor = fillfactor_opt->values.int_val; } /* appendonly */ appendonly_opt = get_option_set(options, num_options, SOPT_APPENDONLY); if (appendonly_opt != NULL) { if (!KIND_IS_RELATION(kind)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("usage of parameter \"appendonly\" in a non relation object is not supported"))); result->appendonly = appendonly_opt->values.bool_val; } /* blocksize */ blocksize_opt = get_option_set(options, num_options, SOPT_BLOCKSIZE); if (blocksize_opt != NULL) { if (!KIND_IS_RELATION(kind) && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("usage of parameter \"blocksize\" in a non relation object is not supported"))); if (!result->appendonly && validate) ereport(ERROR, (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), errmsg("invalid option 'blocksize' for base relation. " "Only valid for Append Only relations"))); result->blocksize = blocksize_opt->values.int_val; if (result->blocksize < MIN_APPENDONLY_BLOCK_SIZE || result->blocksize > MAX_APPENDONLY_BLOCK_SIZE || result->blocksize % MIN_APPENDONLY_BLOCK_SIZE != 0) { if (validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("block size must be between 8KB and 2MB and be" " an 8KB multiple. Got %d", result->blocksize))); result->blocksize = DEFAULT_APPENDONLY_BLOCK_SIZE; } } /* compression type */ comptype_opt = get_option_set(options, num_options, SOPT_COMPTYPE); if (comptype_opt != NULL) { if (!KIND_IS_RELATION(kind) && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("usage of parameter \"compresstype\" in a non relation object is not supported"))); if (!result->appendonly && validate) ereport(ERROR, (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), errmsg("invalid option \"compresstype\" for base relation." " Only valid for Append Only relations"))); result->compresstype = pstrdup(comptype_opt->values.string_val); if (!compresstype_is_valid(result->compresstype)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unknown compresstype \"%s\"", result->compresstype))); for (i = 0; i < strlen(result->compresstype); i++) result->compresstype[i] = pg_tolower(result->compresstype[i]); } /* compression level */ complevel_opt = get_option_set(options, num_options, SOPT_COMPLEVEL); if (complevel_opt != NULL) { if (!KIND_IS_RELATION(kind) && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("usage of parameter \"compresslevel\" in a non relation object is not supported"))); if (!result->appendonly && validate) ereport(ERROR, (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), errmsg("invalid option 'compresslevel' for base " "relation. Only valid for Append Only relations"))); result->compresslevel = complevel_opt->values.int_val; if (result->compresstype && pg_strcasecmp(result->compresstype, "none") != 0 && result->compresslevel == 0 && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("compresstype can\'t be used with compresslevel 0"))); if (result->compresslevel < 0) { if (validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("compresslevel=%d is out of range (should be positive)", result->compresslevel))); result->compresslevel = setDefaultCompressionLevel(result->compresstype); } /* * use the default compressor if compresslevel was indicated but not * compresstype. must make a copy otherwise str_tolower below will * crash. */ if (result->compresslevel > 0 && !result->compresstype) result->compresstype = pstrdup(AO_DEFAULT_COMPRESSTYPE); /* Check upper bound of compresslevel for each compression type */ if (result->compresstype && (pg_strcasecmp(result->compresstype, "zlib") == 0) && (result->compresslevel > 9)) { if (validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("compresslevel=%d is out of range for zlib " "(should be in the range 1 to 9)", result->compresslevel))); result->compresslevel = setDefaultCompressionLevel(result->compresstype); } if (result->compresstype && (pg_strcasecmp(result->compresstype, "zstd") == 0) && (result->compresslevel > 19)) { if (validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("compresslevel=%d is out of range for zstd " "(should be in the range 1 to 19)", result->compresslevel))); result->compresslevel = setDefaultCompressionLevel(result->compresstype); } if (result->compresstype && (pg_strcasecmp(result->compresstype, "quicklz") == 0) && (result->compresslevel != 1)) { if (validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("compresslevel=%d is out of range for quicklz " "(should be 1)", result->compresslevel))); result->compresslevel = setDefaultCompressionLevel(result->compresstype); } if (result->compresstype && (pg_strcasecmp(result->compresstype, "rle_type") == 0) && (result->compresslevel > 4)) { if (validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("compresslevel=%d is out of range for rle_type " "(should be in the range 1 to 4)", result->compresslevel))); result->compresslevel = setDefaultCompressionLevel(result->compresstype); } } /* checksum */ checksum_opt = get_option_set(options, num_options, SOPT_CHECKSUM); if (checksum_opt != NULL) { if (!KIND_IS_RELATION(kind) && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("usage of parameter \"checksum\" in a non relation " "object is not supported"))); if (!result->appendonly && validate) ereport(ERROR, (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), errmsg("invalid option \"checksum\" for base relation. " "Only valid for Append Only relations"))); result->checksum = checksum_opt->values.bool_val; } /* Disable checksum for heap relations. */ else if (result->appendonly == false) result->checksum = false; /* columnstore */ orientation_opt = get_option_set(options, num_options, SOPT_ORIENTATION); if (orientation_opt != NULL) { if (!KIND_IS_RELATION(kind) && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("usage of parameter \"orientation\" in a non " "relation object is not supported"))); if (!result->appendonly && validate) ereport(ERROR, (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), errmsg("invalid option \"orientation\" for base relation. " "Only valid for Append Only relations"))); if (!(pg_strcasecmp(orientation_opt->values.string_val, "column") == 0 || pg_strcasecmp(orientation_opt->values.string_val, "row") == 0) && validate) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid parameter value for \"orientation\": " "\"%s\"", orientation_opt->values.string_val))); } result->columnstore = (pg_strcasecmp(orientation_opt->values.string_val, "column") == 0 ? true : false); if (result->compresstype && (pg_strcasecmp(result->compresstype, "rle_type") == 0) && !result->columnstore) { if (validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("%s cannot be used with Append Only relations " "row orientation", result->compresstype))); } } if (result->appendonly && result->compresstype != NULL) if (result->compresslevel == AO_DEFAULT_COMPRESSLEVEL) result->compresslevel = setDefaultCompressionLevel(result->compresstype); }
/* * Accept a string of the form "name=value,name=value,...". Space * around ',' and '=' is allowed. Parsed values are stored in * corresponding fields of StdRdOptions object. The parser is a * finite state machine that changes states for each input character * scanned. */ Datum parseAOStorageOpts(const char *opts_str, bool *aovalue) { int dims[1]; int lbs[1]; Datum result; ArrayBuildState *astate; bool foundAO = false; const char *cp; const char *name_st = NULL; const char *value_st = NULL; char *name = NULL, *value = NULL; enum state { /* * Consume whitespace at the beginning of a name token. */ LEADING_NAME, /* * Name token is being scanned. Allowed characters are alphabets, * whitespace and '='. */ NAME_TOKEN, /* * Name token was terminated by whitespace. This state scans the * trailing whitespace after name token. */ TRAILING_NAME, /* * Whitespace after '=' and before value token. */ LEADING_VALUE, /* * Value token is being scanned. Allowed characters are alphabets, * digits, '_'. Value should be delimited by a ',', whitespace or end * of string '\0'. */ VALUE_TOKEN, /* * Whitespace after value token. */ TRAILING_VALUE, /* * End of string. This state can only be entered from VALUE_TOKEN or * TRAILING_VALUE. */ EOS }; enum state st = LEADING_NAME; /* * Initialize ArrayBuildState ourselves rather than leaving it to * accumArrayResult(). This aviods the catalog lookup (pg_type) performed * by accumArrayResult(). */ astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState)); astate->mcontext = CurrentMemoryContext; astate->alen = 10; /* Initial number of name=value pairs. */ astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum)); astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool)); astate->nelems = 0; astate->element_type = TEXTOID; astate->typlen = -1; astate->typbyval = false; astate->typalign = 'i'; cp = opts_str - 1; do { ++cp; switch (st) { case LEADING_NAME: if (isalpha(*cp)) { st = NAME_TOKEN; name_st = cp; } else if (!isspace(*cp)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid storage option name in \"%s\"", opts_str))); } break; case NAME_TOKEN: if (isspace(*cp)) st = TRAILING_NAME; else if (*cp == '=') st = LEADING_VALUE; else if (!isalpha(*cp)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid storage option name in \"%s\"", opts_str))); if (st != NAME_TOKEN) { name = palloc(cp - name_st + 1); strncpy(name, name_st, cp - name_st); name[cp - name_st] = '\0'; for (name_st = name; *name_st != '\0'; ++name_st) *(char *) name_st = pg_tolower(*name_st); } break; case TRAILING_NAME: if (*cp == '=') st = LEADING_VALUE; else if (!isspace(*cp)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid value for option \"%s\", expected \"=\"", name))); break; case LEADING_VALUE: if (isalnum(*cp)) { st = VALUE_TOKEN; value_st = cp; } else if (!isspace(*cp)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid value for option \"%s\"", name))); break; case VALUE_TOKEN: if (isspace(*cp)) st = TRAILING_VALUE; else if (*cp == '\0') st = EOS; else if (*cp == ',') st = LEADING_NAME; /* Need to check '_' for rle_type */ else if (!(isalnum(*cp) || *cp == '_')) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid value for option \"%s\"", name))); if (st != VALUE_TOKEN) { value = palloc(cp - value_st + 1); strncpy(value, value_st, cp - value_st); value[cp - value_st] = '\0'; for (value_st = value; *value_st != '\0'; ++value_st) *(char *) value_st = pg_tolower(*value_st); Assert(name); accumAOStorageOpt(name, value, astate, &foundAO, aovalue); pfree(name); name = NULL; pfree(value); value = NULL; } break; case TRAILING_VALUE: if (*cp == ',') st = LEADING_NAME; else if (*cp == '\0') st = EOS; else if (!isspace(*cp)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error after \"%s\"", value))); break; case EOS: /* * We better get out of the loop right after entering this * state. Therefore, we should never get here. */ elog(ERROR, "invalid value \"%s\" for GUC", opts_str); break; }; } while (*cp != '\0'); if (st != EOS) elog(ERROR, "invalid value \"%s\" for GUC", opts_str); if (!foundAO) { /* * Add "appendonly=true" datum if it was not explicitly specified by * user. This is needed to validate the array of datums constructed * from user specified options. */ accumAOStorageOpt(SOPT_APPENDONLY, "true", astate, NULL, NULL); } lbs[0] = 1; dims[0] = astate->nelems; result = makeMdArrayResult(astate, 1, dims, lbs, CurrentMemoryContext, false); pfree(astate->dvalues); pfree(astate->dnulls); pfree(astate); return result; }