Esempio n. 1
0
/*
 * 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);
}
Esempio n. 2
0
/*
 * 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;
}
Esempio n. 3
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;
}
Esempio n. 4
0
/*
 * 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;
}
Esempio n. 5
0
/*
 * 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;
}
Esempio n. 6
0
/*
 * 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
}
Esempio n. 7
0
/*
 * 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;
}
Esempio n. 8
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);
}
Esempio n. 9
0
/*
 * 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;
}