aboutsummaryrefslogtreecommitdiff
path: root/src/fe_utils/string_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fe_utils/string_utils.c')
-rw-r--r--src/fe_utils/string_utils.c129
1 files changed, 97 insertions, 32 deletions
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 1c618404627..c3ea4fc1860 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -882,6 +882,9 @@ appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
* 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.
+ * dbnamebuf: output parameter receiving the database name portion of the
+ * pattern, if any. Can be NULL.
+ * dotcnt: how many separators were parsed from the pattern, by reference.
*
* Formatting note: the text already present in buf should end with a newline.
* The appended text, if any, will end with one too.
@@ -890,16 +893,21 @@ 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)
+ const char *altnamevar, const char *visibilityrule,
+ PQExpBuffer dbnamebuf, int *dotcnt)
{
PQExpBufferData schemabuf;
PQExpBufferData namebuf;
bool added_clause = false;
+ int dcnt;
#define WHEREAND() \
(appendPQExpBufferStr(buf, have_where ? " AND " : "WHERE "), \
have_where = true, added_clause = true)
+ if (dotcnt == NULL)
+ dotcnt = &dcnt;
+ *dotcnt = 0;
if (pattern == NULL)
{
/* Default: select all visible objects */
@@ -922,9 +930,11 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
* If the caller provided a schemavar, we want to split the pattern on
* ".", otherwise not.
*/
- patternToSQLRegex(PQclientEncoding(conn), NULL,
- (schemavar ? &schemabuf : NULL), &namebuf,
- pattern, force_escape);
+ patternToSQLRegex(PQclientEncoding(conn),
+ (schemavar ? dbnamebuf : NULL),
+ (schemavar ? &schemabuf : NULL),
+ &namebuf,
+ pattern, force_escape, true, dotcnt);
/*
* Now decide what we need to emit. We may run under a hostile
@@ -937,7 +947,7 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
* is >= v12 then we need to force it through explicit COLLATE clauses,
* otherwise the "C" collation attached to "name" catalog columns wins.
*/
- if (namebuf.len > 2)
+ if (namevar && namebuf.len > 2)
{
/* We have a name pattern, so constrain the namevar(s) */
@@ -971,7 +981,7 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
}
}
- if (schemabuf.len > 2)
+ if (schemavar && schemabuf.len > 2)
{
/* We have a schema pattern, so constrain the schemavar */
@@ -1012,8 +1022,7 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
* If the dbnamebuf and schemabuf arguments are non-NULL, and the pattern
* contains two or more dbname/schema/name separators, we parse the portions of
* the pattern prior to the first and second separators into dbnamebuf and
- * schemabuf, and the rest into namebuf. (Additional dots in the name portion
- * are not treated as special.)
+ * schemabuf, and the rest into namebuf.
*
* If dbnamebuf is NULL and schemabuf is non-NULL, and the pattern contains at
* least one separator, we parse the first portion into schemabuf and the rest
@@ -1021,24 +1030,49 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
*
* Otherwise, we parse all the pattern into namebuf.
*
+ * If the pattern contains more dotted parts than buffers to parse into, the
+ * extra dots will be treated as literal characters and written into the
+ * namebuf, though they will be counted. Callers should always check the value
+ * returned by reference in dotcnt and handle this error case appropriately.
+ *
* We surround the regexps with "^(...)$" to force them to match whole strings,
* as per SQL practice. We have to have parens in case strings contain "|",
* else the "^" and "$" will be bound into the first and last alternatives
- * which is not what we want.
+ * which is not what we want. Whether this is done for dbnamebuf is controlled
+ * by the want_literal_dbname parameter.
*
* The regexps we parse into the buffers are appended to the data (if any)
* already present. If we parse fewer fields than the number of buffers we
* were given, the extra buffers are unaltered.
+ *
+ * encoding: the character encoding for the given pattern
+ * dbnamebuf: output parameter receiving the database name portion of the
+ * pattern, if any. Can be NULL.
+ * schemabuf: output parameter receiving the schema name portion of the
+ * pattern, if any. Can be NULL.
+ * namebuf: output parameter receiving the database name portion of the
+ * pattern, if any. Can be NULL.
+ * pattern: user-specified pattern option, or NULL if none ("*" is implied).
+ * force_escape: always quote regexp special characters, even outside
+ * double quotes (else they are quoted only between double quotes).
+ * want_literal_dbname: if true, regexp special characters within the database
+ * name portion of the pattern will not be escaped, nor will the dbname be
+ * converted into a regular expression.
+ * dotcnt: output parameter receiving the number of separators parsed from the
+ * pattern.
*/
void
patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
- PQExpBuffer namebuf, const char *pattern, bool force_escape)
+ PQExpBuffer namebuf, const char *pattern, bool force_escape,
+ bool want_literal_dbname, int *dotcnt)
{
PQExpBufferData buf[3];
+ PQExpBufferData left_literal;
PQExpBuffer curbuf;
PQExpBuffer maxbuf;
int i;
bool inquotes;
+ bool left;
const char *cp;
Assert(pattern != NULL);
@@ -1046,7 +1080,9 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
/* callers should never expect "dbname.relname" format */
Assert(dbnamebuf == NULL || schemabuf != NULL);
+ Assert(dotcnt != NULL);
+ *dotcnt = 0;
inquotes = false;
cp = pattern;
@@ -1058,6 +1094,13 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
maxbuf = &buf[0];
curbuf = &buf[0];
+ if (want_literal_dbname)
+ {
+ left = true;
+ initPQExpBuffer(&left_literal);
+ }
+ else
+ left = false;
initPQExpBuffer(curbuf);
appendPQExpBufferStr(curbuf, "^(");
while (*cp)
@@ -1070,6 +1113,8 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
{
/* emit one quote, stay in inquotes mode */
appendPQExpBufferChar(curbuf, '"');
+ if (left)
+ appendPQExpBufferChar(&left_literal, '"');
cp++;
}
else
@@ -1080,32 +1125,40 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
{
appendPQExpBufferChar(curbuf,
pg_tolower((unsigned char) ch));
+ if (left)
+ appendPQExpBufferChar(&left_literal,
+ pg_tolower((unsigned char) ch));
cp++;
}
else if (!inquotes && ch == '*')
{
appendPQExpBufferStr(curbuf, ".*");
+ if (left)
+ appendPQExpBufferChar(&left_literal, '*');
cp++;
}
else if (!inquotes && ch == '?')
{
appendPQExpBufferChar(curbuf, '.');
+ if (left)
+ appendPQExpBufferChar(&left_literal, '?');
cp++;
}
-
- /*
- * When we find a dbname/schema/name separator, we treat it specially
- * only if the caller requested more patterns to be parsed than we
- * have already parsed from the pattern. Otherwise, dot characters
- * are not special.
- */
- else if (!inquotes && ch == '.' && curbuf < maxbuf)
+ else if (!inquotes && ch == '.')
{
- appendPQExpBufferStr(curbuf, ")$");
- curbuf++;
- initPQExpBuffer(curbuf);
- appendPQExpBufferStr(curbuf, "^(");
- cp++;
+ left = false;
+ if (dotcnt)
+ (*dotcnt)++;
+ if (curbuf < maxbuf)
+ {
+ appendPQExpBufferStr(curbuf, ")$");
+ curbuf++;
+ initPQExpBuffer(curbuf);
+ appendPQExpBufferStr(curbuf, "^(");
+ cp++;
+ }
+ else
+ appendPQExpBufferChar(curbuf, *cp++);
}
else if (ch == '$')
{
@@ -1117,6 +1170,8 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
* having it possess its regexp meaning.
*/
appendPQExpBufferStr(curbuf, "\\$");
+ if (left)
+ appendPQExpBufferChar(&left_literal, '$');
cp++;
}
else
@@ -1141,25 +1196,35 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
appendPQExpBufferChar(curbuf, '\\');
i = PQmblenBounded(cp, encoding);
while (i--)
+ {
+ if (left)
+ appendPQExpBufferChar(&left_literal, *cp);
appendPQExpBufferChar(curbuf, *cp++);
+ }
}
}
appendPQExpBufferStr(curbuf, ")$");
- appendPQExpBufferStr(namebuf, curbuf->data);
- termPQExpBuffer(curbuf);
-
- if (curbuf > buf)
+ if (namebuf)
{
+ appendPQExpBufferStr(namebuf, curbuf->data);
+ termPQExpBuffer(curbuf);
curbuf--;
+ }
+
+ if (schemabuf && curbuf >= buf)
+ {
appendPQExpBufferStr(schemabuf, curbuf->data);
termPQExpBuffer(curbuf);
+ curbuf--;
+ }
- if (curbuf > buf)
- {
- curbuf--;
+ if (dbnamebuf && curbuf >= buf)
+ {
+ if (want_literal_dbname)
+ appendPQExpBufferStr(dbnamebuf, left_literal.data);
+ else
appendPQExpBufferStr(dbnamebuf, curbuf->data);
- termPQExpBuffer(curbuf);
- }
+ termPQExpBuffer(curbuf);
}
}