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
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy