Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit63ca863

Browse files
committed
Fix quoted-substring handling in format parsing for to_char/to_number/etc.
This code evidently intended to treat backslash as an escape characterwithin double-quoted substrings, but it was sufficiently confused thatcases like ..."foo\\"... did not work right: the second backslashmanaged to quote the double-quote after it, despite being quoted itself.Rewrite to get that right, while preserving the existing behavioroutside double-quoted substrings, which is that backslash isn't specialexcept in the combination \".Comparing to Oracle, it seems that their version of to_char() fortimestamps allows literal alphanumerics only within double quotes, whilenon-alphanumerics are allowed outside quotes; backslashes aren't specialanywhere; there is no way at all to emit a literal double quote.(Bizarrely, their to_char() for numbers is different; it doesn't allowliteral text at all AFAICT.) The fact that they don't treat backslashas special justifies our existing behavior for backslash outside doublequotes. I considered making backslash inside double quotes act the sameway (ie, special only if before "), which in a green field would be amore consistent behavior. But that would likely break more existing SQLcode than what this patch does.Add some test cases illustrating this behavior. (Only the last newcase actually changes behavior in this commit.)Little of this behavior was documented, either, so fix that.Discussion:https://postgr.es/m/3626.1510949486@sss.pgh.pa.us
1 parent9288d62 commit63ca863

File tree

4 files changed

+104
-44
lines changed

4 files changed

+104
-44
lines changed

‎doc/src/sgml/func.sgml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6196,6 +6196,11 @@ SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
61966196
If you want to have a double quote in the output you must
61976197
precede it with a backslash, for example <literal>'\"YYYY
61986198
Month\"'</literal>. <!-- "" font-lock sanity :-) -->
6199+
Backslashes are not otherwise special outside of double-quoted
6200+
strings. Within a double-quoted string, a backslash causes the
6201+
next character to be taken literally, whatever it is (but this
6202+
has no special effect unless the next character is a double quote
6203+
or another backslash).
61996204
</para>
62006205
</listitem>
62016206

‎src/backend/utils/adt/formatting.c

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,11 +1227,7 @@ static void
12271227
parse_format(FormatNode*node,constchar*str,constKeyWord*kw,
12281228
constKeySuffix*suf,constint*index,intver,NUMDesc*Num)
12291229
{
1230-
constKeySuffix*s;
12311230
FormatNode*n;
1232-
intnode_set=0,
1233-
suffix,
1234-
last=0;
12351231

12361232
#ifdefDEBUG_TO_FROM_CHAR
12371233
elog(DEBUG_elog_output,"to_char/number(): run parser");
@@ -1241,12 +1237,14 @@ parse_format(FormatNode *node, const char *str, const KeyWord *kw,
12411237

12421238
while (*str)
12431239
{
1244-
suffix=0;
1240+
intsuffix=0;
1241+
constKeySuffix*s;
12451242

12461243
/*
12471244
* Prefix
12481245
*/
1249-
if (ver==DCH_TYPE&& (s=suff_search(str,suf,SUFFTYPE_PREFIX))!=NULL)
1246+
if (ver==DCH_TYPE&&
1247+
(s=suff_search(str,suf,SUFFTYPE_PREFIX))!=NULL)
12501248
{
12511249
suffix |=s->id;
12521250
if (s->len)
@@ -1259,8 +1257,7 @@ parse_format(FormatNode *node, const char *str, const KeyWord *kw,
12591257
if (*str&& (n->key=index_seq_search(str,kw,index))!=NULL)
12601258
{
12611259
n->type=NODE_TYPE_ACTION;
1262-
n->suffix=0;
1263-
node_set=1;
1260+
n->suffix=suffix;
12641261
if (n->key->len)
12651262
str+=n->key->len;
12661263

@@ -1273,71 +1270,56 @@ parse_format(FormatNode *node, const char *str, const KeyWord *kw,
12731270
/*
12741271
* Postfix
12751272
*/
1276-
if (ver==DCH_TYPE&&*str&& (s=suff_search(str,suf,SUFFTYPE_POSTFIX))!=NULL)
1273+
if (ver==DCH_TYPE&&*str&&
1274+
(s=suff_search(str,suf,SUFFTYPE_POSTFIX))!=NULL)
12771275
{
1278-
suffix |=s->id;
1276+
n->suffix |=s->id;
12791277
if (s->len)
12801278
str+=s->len;
12811279
}
1280+
1281+
n++;
12821282
}
12831283
elseif (*str)
12841284
{
12851285
/*
1286-
*Special characters '\' and '"'
1286+
*Process double-quoted literal string, if any
12871287
*/
1288-
if (*str=='"'&&last!='\\')
1288+
if (*str=='"')
12891289
{
1290-
intx=0;
1291-
12921290
while (*(++str))
12931291
{
1294-
if (*str=='"'&&x!='\\')
1292+
if (*str=='"')
12951293
{
12961294
str++;
12971295
break;
12981296
}
1299-
elseif (*str=='\\'&&x!='\\')
1300-
{
1301-
x='\\';
1302-
continue;
1303-
}
1297+
/* backslash quotes the next character, if any */
1298+
if (*str=='\\'&&*(str+1))
1299+
str++;
13041300
n->type=NODE_TYPE_CHAR;
13051301
n->character=*str;
13061302
n->key=NULL;
13071303
n->suffix=0;
1308-
++n;
1309-
x=*str;
1304+
n++;
13101305
}
1311-
node_set=0;
1312-
suffix=0;
1313-
last=0;
13141306
}
1315-
elseif (*str&&*str=='\\'&&last!='\\'&&*(str+1)=='"')
1316-
{
1317-
last=*str;
1318-
str++;
1319-
}
1320-
elseif (*str)
1307+
else
13211308
{
1309+
/*
1310+
* Outside double-quoted strings, backslash is only special if
1311+
* it immediately precedes a double quote.
1312+
*/
1313+
if (*str=='\\'&&*(str+1)=='"')
1314+
str++;
13221315
n->type=NODE_TYPE_CHAR;
13231316
n->character=*str;
13241317
n->key=NULL;
1325-
node_set=1;
1326-
last=0;
1318+
n->suffix=0;
1319+
n++;
13271320
str++;
13281321
}
13291322
}
1330-
1331-
/* end */
1332-
if (node_set)
1333-
{
1334-
if (n->type==NODE_TYPE_ACTION)
1335-
n->suffix=suffix;
1336-
++n;
1337-
1338-
n->suffix=0;
1339-
node_set=0;
1340-
}
13411323
}
13421324

13431325
n->type=NODE_TYPE_END;

‎src/test/regress/expected/numeric.out

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,67 @@ SELECT '' AS to_char_26, to_char('100'::numeric, 'FM999');
12171217
| 100
12181218
(1 row)
12191219

1220+
-- Check parsing of literal text in a format string
1221+
SELECT '' AS to_char_27, to_char('100'::numeric, 'foo999');
1222+
to_char_27 | to_char
1223+
------------+---------
1224+
| foo 100
1225+
(1 row)
1226+
1227+
SELECT '' AS to_char_28, to_char('100'::numeric, 'f\oo999');
1228+
to_char_28 | to_char
1229+
------------+----------
1230+
| f\oo 100
1231+
(1 row)
1232+
1233+
SELECT '' AS to_char_29, to_char('100'::numeric, 'f\\oo999');
1234+
to_char_29 | to_char
1235+
------------+-----------
1236+
| f\\oo 100
1237+
(1 row)
1238+
1239+
SELECT '' AS to_char_30, to_char('100'::numeric, 'f\"oo999');
1240+
to_char_30 | to_char
1241+
------------+----------
1242+
| f"oo 100
1243+
(1 row)
1244+
1245+
SELECT '' AS to_char_31, to_char('100'::numeric, 'f\\"oo999');
1246+
to_char_31 | to_char
1247+
------------+-----------
1248+
| f\"oo 100
1249+
(1 row)
1250+
1251+
SELECT '' AS to_char_32, to_char('100'::numeric, 'f"ool"999');
1252+
to_char_32 | to_char
1253+
------------+----------
1254+
| fool 100
1255+
(1 row)
1256+
1257+
SELECT '' AS to_char_33, to_char('100'::numeric, 'f"\ool"999');
1258+
to_char_33 | to_char
1259+
------------+----------
1260+
| fool 100
1261+
(1 row)
1262+
1263+
SELECT '' AS to_char_34, to_char('100'::numeric, 'f"\\ool"999');
1264+
to_char_34 | to_char
1265+
------------+-----------
1266+
| f\ool 100
1267+
(1 row)
1268+
1269+
SELECT '' AS to_char_35, to_char('100'::numeric, 'f"ool\"999');
1270+
to_char_35 | to_char
1271+
------------+----------
1272+
| fool"999
1273+
(1 row)
1274+
1275+
SELECT '' AS to_char_36, to_char('100'::numeric, 'f"ool\\"999');
1276+
to_char_36 | to_char
1277+
------------+-----------
1278+
| fool\ 100
1279+
(1 row)
1280+
12201281
-- TO_NUMBER()
12211282
--
12221283
SET lc_numeric = 'C';

‎src/test/regress/sql/numeric.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,18 @@ SELECT '' AS to_char_24, to_char('100'::numeric, 'FM999.9');
786786
SELECT''AS to_char_25, to_char('100'::numeric,'FM999.');
787787
SELECT''AS to_char_26, to_char('100'::numeric,'FM999');
788788

789+
-- Check parsing of literal text in a format string
790+
SELECT''AS to_char_27, to_char('100'::numeric,'foo999');
791+
SELECT''AS to_char_28, to_char('100'::numeric,'f\oo999');
792+
SELECT''AS to_char_29, to_char('100'::numeric,'f\\oo999');
793+
SELECT''AS to_char_30, to_char('100'::numeric,'f\"oo999');
794+
SELECT''AS to_char_31, to_char('100'::numeric,'f\\"oo999');
795+
SELECT''AS to_char_32, to_char('100'::numeric,'f"ool"999');
796+
SELECT''AS to_char_33, to_char('100'::numeric,'f"\ool"999');
797+
SELECT''AS to_char_34, to_char('100'::numeric,'f"\\ool"999');
798+
SELECT''AS to_char_35, to_char('100'::numeric,'f"ool\"999');
799+
SELECT''AS to_char_36, to_char('100'::numeric,'f"ool\\"999');
800+
789801
-- TO_NUMBER()
790802
--
791803
SET lc_numeric='C';

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp