Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 86eaf20

Browse files
committed
Hand code string to integer conversion for performance.
As benchmarks show, using libc's string-to-integer conversion is pretty slow. At least part of the reason for that is that strtol[l] have to be more generic than what largely is required inside pg. This patch considerably speeds up int2/int4 input (int8 already was already using hand-rolled code). Most of the existing pg_atoi callers have been converted. But as one requires pg_atoi's custom delimiter functionality, and as it seems likely that there's external pg_atoi users, it seems sensible to just keep pg_atoi around. Author: Andres Freund Reviewed-By: Robert Haas Discussion: https://postgr.es/m/20171208214437.qgn6zdltyq5hmjpk@alap3.anarazel.de
1 parent 3522d0e commit 86eaf20

File tree

13 files changed

+176
-25
lines changed

13 files changed

+176
-25
lines changed

contrib/spi/refint.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
306306
/* internal error */
307307
elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
308308

309-
nrefs = pg_atoi(args[0], sizeof(int), 0);
309+
nrefs = pg_strtoint32(args[0]);
310310
if (nrefs < 1)
311311
/* internal error */
312312
elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);

doc/src/sgml/sources.sgml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ BETTER: could not open file %s (I/O failure)
709709
not helpful information. If the error text doesn't make as much sense
710710
without the function name, reword it.
711711
<programlisting>
712-
BAD: pg_atoi: error in "z": cannot parse "z"
712+
BAD: pg_strtoint32: error in "z": cannot parse "z"
713713
BETTER: invalid input syntax for integer: "z"
714714
</programlisting>
715715
</para>

src/backend/libpq/pqmq.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,10 @@ pq_parse_errornotice(StringInfo msg, ErrorData *edata)
286286
edata->hint = pstrdup(value);
287287
break;
288288
case PG_DIAG_STATEMENT_POSITION:
289-
edata->cursorpos = pg_atoi(value, sizeof(int), '\0');
289+
edata->cursorpos = pg_strtoint32(value);
290290
break;
291291
case PG_DIAG_INTERNAL_POSITION:
292-
edata->internalpos = pg_atoi(value, sizeof(int), '\0');
292+
edata->internalpos = pg_strtoint32(value);
293293
break;
294294
case PG_DIAG_INTERNAL_QUERY:
295295
edata->internalquery = pstrdup(value);
@@ -316,7 +316,7 @@ pq_parse_errornotice(StringInfo msg, ErrorData *edata)
316316
edata->filename = pstrdup(value);
317317
break;
318318
case PG_DIAG_SOURCE_LINE:
319-
edata->lineno = pg_atoi(value, sizeof(int), '\0');
319+
edata->lineno = pg_strtoint32(value);
320320
break;
321321
case PG_DIAG_SOURCE_FUNCTION:
322322
edata->funcname = pstrdup(value);

src/backend/replication/libpqwalreceiver/libpqwalreceiver.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ libpqrcv_identify_system(WalReceiverConn *conn, TimeLineID *primary_tli,
345345
ntuples, nfields, 3, 1)));
346346
}
347347
primary_sysid = pstrdup(PQgetvalue(res, 0, 0));
348-
*primary_tli = pg_atoi(PQgetvalue(res, 0, 1), 4, 0);
348+
*primary_tli = pg_strtoint32(PQgetvalue(res, 0, 1));
349349
PQclear(res);
350350

351351
*server_version = PQserverVersion(conn->streamConn);
@@ -480,7 +480,7 @@ libpqrcv_endstreaming(WalReceiverConn *conn, TimeLineID *next_tli)
480480
if (PQnfields(res) < 2 || PQntuples(res) != 1)
481481
ereport(ERROR,
482482
(errmsg("unexpected result set after end-of-streaming")));
483-
*next_tli = pg_atoi(PQgetvalue(res, 0, 0), sizeof(uint32), 0);
483+
*next_tli = pg_strtoint32(PQgetvalue(res, 0, 0));
484484
PQclear(res);
485485

486486
/* the result set should be followed by CommandComplete */

src/backend/tsearch/wparser_def.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,13 +2460,13 @@ prsd_headline(PG_FUNCTION_ARGS)
24602460
char *val = defGetString(defel);
24612461

24622462
if (pg_strcasecmp(defel->defname, "MaxWords") == 0)
2463-
max_words = pg_atoi(val, sizeof(int32), 0);
2463+
max_words = pg_strtoint32(val);
24642464
else if (pg_strcasecmp(defel->defname, "MinWords") == 0)
2465-
min_words = pg_atoi(val, sizeof(int32), 0);
2465+
min_words = pg_strtoint32(val);
24662466
else if (pg_strcasecmp(defel->defname, "ShortWord") == 0)
2467-
shortword = pg_atoi(val, sizeof(int32), 0);
2467+
shortword = pg_strtoint32(val);
24682468
else if (pg_strcasecmp(defel->defname, "MaxFragments") == 0)
2469-
max_fragments = pg_atoi(val, sizeof(int32), 0);
2469+
max_fragments = pg_strtoint32(val);
24702470
else if (pg_strcasecmp(defel->defname, "StartSel") == 0)
24712471
prs->startsel = pstrdup(val);
24722472
else if (pg_strcasecmp(defel->defname, "StopSel") == 0)

src/backend/utils/adt/arrayutils.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,7 @@ ArrayGetIntegerTypmods(ArrayType *arr, int *n)
226226
result = (int32 *) palloc(*n * sizeof(int32));
227227

228228
for (i = 0; i < *n; i++)
229-
result[i] = pg_atoi(DatumGetCString(elem_values[i]),
230-
sizeof(int32), '\0');
229+
result[i] = pg_strtoint32(DatumGetCString(elem_values[i]));
231230

232231
pfree(elem_values);
233232

src/backend/utils/adt/int.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ int2in(PG_FUNCTION_ARGS)
6060
{
6161
char *num = PG_GETARG_CSTRING(0);
6262

63-
PG_RETURN_INT16(pg_atoi(num, sizeof(int16), '\0'));
63+
PG_RETURN_INT16(pg_strtoint16(num));
6464
}
6565

6666
/*
@@ -265,7 +265,7 @@ int4in(PG_FUNCTION_ARGS)
265265
{
266266
char *num = PG_GETARG_CSTRING(0);
267267

268-
PG_RETURN_INT32(pg_atoi(num, sizeof(int32), '\0'));
268+
PG_RETURN_INT32(pg_strtoint32(num));
269269
}
270270

271271
/*

src/backend/utils/adt/int8.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ scanint8(const char *str, bool errorOK, int64 *result)
101101

102102
if (!neg)
103103
{
104+
/* could fail if input is most negative number */
104105
if (unlikely(tmp == PG_INT64_MIN))
105106
goto out_of_range;
106107
tmp = -tmp;

src/backend/utils/adt/numutils.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <limits.h>
1919
#include <ctype.h>
2020

21+
#include "common/int.h"
2122
#include "utils/builtins.h"
2223

2324
/*
@@ -108,6 +109,154 @@ pg_atoi(const char *s, int size, int c)
108109
return (int32) l;
109110
}
110111

112+
/*
113+
* Convert input string to a signed 16 bit integer.
114+
*
115+
* Allows any number of leading or trailing whitespace characters. Will throw
116+
* ereport() upon bad input format or overflow.
117+
*
118+
* NB: Accumulate input as a negative number, to deal with two's complement
119+
* representation of the most negative number, which can't be represented as a
120+
* positive number.
121+
*/
122+
int16
123+
pg_strtoint16(const char *s)
124+
{
125+
const char *ptr = s;
126+
int16 tmp = 0;
127+
bool neg = false;
128+
129+
/* skip leading spaces */
130+
while (likely(*ptr) && isspace((unsigned char) *ptr))
131+
ptr++;
132+
133+
/* handle sign */
134+
if (*ptr == '-')
135+
{
136+
ptr++;
137+
neg = true;
138+
}
139+
else if (*ptr == '+')
140+
ptr++;
141+
142+
/* require at least one digit */
143+
if (unlikely(!isdigit((unsigned char) *ptr)))
144+
goto invalid_syntax;
145+
146+
/* process digits */
147+
while (*ptr && isdigit((unsigned char) *ptr))
148+
{
149+
int8 digit = (*ptr++ - '0');
150+
151+
if (unlikely(pg_mul_s16_overflow(tmp, 10, &tmp)) ||
152+
unlikely(pg_sub_s16_overflow(tmp, digit, &tmp)))
153+
goto out_of_range;
154+
}
155+
156+
/* allow trailing whitespace, but not other trailing chars */
157+
while (*ptr != '\0' && isspace((unsigned char) *ptr))
158+
ptr++;
159+
160+
if (unlikely(*ptr != '\0'))
161+
goto invalid_syntax;
162+
163+
if (!neg)
164+
{
165+
/* could fail if input is most negative number */
166+
if (unlikely(tmp == PG_INT16_MIN))
167+
goto out_of_range;
168+
tmp = -tmp;
169+
}
170+
171+
return tmp;
172+
173+
out_of_range:
174+
ereport(ERROR,
175+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
176+
errmsg("value \"%s\" is out of range for type %s",
177+
s, "smallint")));
178+
179+
invalid_syntax:
180+
ereport(ERROR,
181+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
182+
errmsg("invalid input syntax for type %s: \"%s\"",
183+
"smallint", s)));
184+
}
185+
186+
/*
187+
* Convert input string to a signed 32 bit integer.
188+
*
189+
* Allows any number of leading or trailing whitespace characters. Will throw
190+
* ereport() upon bad input format or overflow.
191+
*
192+
* NB: Accumulate input as a negative number, to deal with two's complement
193+
* representation of the most negative number, which can't be represented as a
194+
* positive number.
195+
*/
196+
int32
197+
pg_strtoint32(const char *s)
198+
{
199+
const char *ptr = s;
200+
int32 tmp = 0;
201+
bool neg = false;
202+
203+
/* skip leading spaces */
204+
while (likely(*ptr) && isspace((unsigned char) *ptr))
205+
ptr++;
206+
207+
/* handle sign */
208+
if (*ptr == '-')
209+
{
210+
ptr++;
211+
neg = true;
212+
}
213+
else if (*ptr == '+')
214+
ptr++;
215+
216+
/* require at least one digit */
217+
if (unlikely(!isdigit((unsigned char) *ptr)))
218+
goto invalid_syntax;
219+
220+
/* process digits */
221+
while (*ptr && isdigit((unsigned char) *ptr))
222+
{
223+
int8 digit = (*ptr++ - '0');
224+
225+
if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) ||
226+
unlikely(pg_sub_s32_overflow(tmp, digit, &tmp)))
227+
goto out_of_range;
228+
}
229+
230+
/* allow trailing whitespace, but not other trailing chars */
231+
while (*ptr != '\0' && isspace((unsigned char) *ptr))
232+
ptr++;
233+
234+
if (unlikely(*ptr != '\0'))
235+
goto invalid_syntax;
236+
237+
if (!neg)
238+
{
239+
/* could fail if input is most negative number */
240+
if (unlikely(tmp == PG_INT32_MIN))
241+
goto out_of_range;
242+
tmp = -tmp;
243+
}
244+
245+
return tmp;
246+
247+
out_of_range:
248+
ereport(ERROR,
249+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
250+
errmsg("value \"%s\" is out of range for type %s",
251+
s, "integer")));
252+
253+
invalid_syntax:
254+
ereport(ERROR,
255+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
256+
errmsg("invalid input syntax for type %s: \"%s\"",
257+
"integer", s)));
258+
}
259+
111260
/*
112261
* pg_itoa: converts a signed 16-bit integer to its string representation
113262
*

src/backend/utils/adt/varlena.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5155,8 +5155,8 @@ text_format(PG_FUNCTION_ARGS)
51555155

51565156
str = OutputFunctionCall(&typoutputinfo_width, value);
51575157

5158-
/* pg_atoi will complain about bad data or overflow */
5159-
width = pg_atoi(str, sizeof(int), '\0');
5158+
/* pg_strtoint32 will complain about bad data or overflow */
5159+
width = pg_strtoint32(str);
51605160

51615161
pfree(str);
51625162
}

src/include/utils/builtins.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ extern int namestrcmp(Name name, const char *str);
4343

4444
/* numutils.c */
4545
extern int32 pg_atoi(const char *s, int size, int c);
46+
extern int16 pg_strtoint16(const char *s);
47+
extern int32 pg_strtoint32(const char *s);
4648
extern void pg_itoa(int16 i, char *a);
4749
extern void pg_ltoa(int32 l, char *a);
4850
extern void pg_lltoa(int64 ll, char *a);

src/test/regress/expected/int2.out

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ INSERT INTO INT2_TBL(f1) VALUES ('0 ');
66
INSERT INTO INT2_TBL(f1) VALUES (' 1234 ');
77
INSERT INTO INT2_TBL(f1) VALUES (' -1234');
88
INSERT INTO INT2_TBL(f1) VALUES ('34.5');
9-
ERROR: invalid input syntax for type integer: "34.5"
9+
ERROR: invalid input syntax for type smallint: "34.5"
1010
LINE 1: INSERT INTO INT2_TBL(f1) VALUES ('34.5');
1111
^
1212
-- largest and smallest values
@@ -18,27 +18,27 @@ ERROR: value "100000" is out of range for type smallint
1818
LINE 1: INSERT INTO INT2_TBL(f1) VALUES ('100000');
1919
^
2020
INSERT INTO INT2_TBL(f1) VALUES ('asdf');
21-
ERROR: invalid input syntax for type integer: "asdf"
21+
ERROR: invalid input syntax for type smallint: "asdf"
2222
LINE 1: INSERT INTO INT2_TBL(f1) VALUES ('asdf');
2323
^
2424
INSERT INTO INT2_TBL(f1) VALUES (' ');
25-
ERROR: invalid input syntax for type integer: " "
25+
ERROR: invalid input syntax for type smallint: " "
2626
LINE 1: INSERT INTO INT2_TBL(f1) VALUES (' ');
2727
^
2828
INSERT INTO INT2_TBL(f1) VALUES ('- 1234');
29-
ERROR: invalid input syntax for type integer: "- 1234"
29+
ERROR: invalid input syntax for type smallint: "- 1234"
3030
LINE 1: INSERT INTO INT2_TBL(f1) VALUES ('- 1234');
3131
^
3232
INSERT INTO INT2_TBL(f1) VALUES ('4 444');
33-
ERROR: invalid input syntax for type integer: "4 444"
33+
ERROR: invalid input syntax for type smallint: "4 444"
3434
LINE 1: INSERT INTO INT2_TBL(f1) VALUES ('4 444');
3535
^
3636
INSERT INTO INT2_TBL(f1) VALUES ('123 dt');
37-
ERROR: invalid input syntax for type integer: "123 dt"
37+
ERROR: invalid input syntax for type smallint: "123 dt"
3838
LINE 1: INSERT INTO INT2_TBL(f1) VALUES ('123 dt');
3939
^
4040
INSERT INTO INT2_TBL(f1) VALUES ('');
41-
ERROR: invalid input syntax for type integer: ""
41+
ERROR: invalid input syntax for type smallint: ""
4242
LINE 1: INSERT INTO INT2_TBL(f1) VALUES ('');
4343
^
4444
SELECT '' AS five, * FROM INT2_TBL;

src/test/regress/expected/select_parallel.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,7 @@ ROLLBACK TO SAVEPOINT settings;
975975
SAVEPOINT settings;
976976
SET LOCAL force_parallel_mode = 1;
977977
select stringu1::int2 from tenk1 where unique1 = 1;
978-
ERROR: invalid input syntax for type integer: "BAAAAA"
978+
ERROR: invalid input syntax for type smallint: "BAAAAA"
979979
CONTEXT: parallel worker
980980
ROLLBACK TO SAVEPOINT settings;
981981
-- test interaction with set-returning functions

0 commit comments

Comments
 (0)