Skip to content

Commit cfc7191

Browse files
committed
Move scanint8() to numutils.c
Move scanint8() to numutils.c and rename to pg_strtoint64(). We already have a "16" and "32" version of that, and the code inside the functions was aligned, so this move makes all three versions consistent. The API is also changed to no longer provide the errorOK case. Users that need the error checking can use strtoi64(). Reviewed-by: John Naylor <john.naylor@enterprisedb.com> Discussion: https://www.postgresql.org/message-id/flat/b239564c-cad0-b23e-c57e-166d883cb97d@enterprisedb.com
1 parent 291ec6e commit cfc7191

File tree

7 files changed

+104
-122
lines changed

7 files changed

+104
-122
lines changed

src/backend/parser/parse_node.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
#include "parser/parse_relation.h"
2727
#include "parser/parsetree.h"
2828
#include "utils/builtins.h"
29-
#include "utils/int8.h"
3029
#include "utils/lsyscache.h"
3130
#include "utils/syscache.h"
3231
#include "utils/varbit.h"
@@ -353,7 +352,6 @@ make_const(ParseState *pstate, A_Const *aconst)
353352
{
354353
Const *con;
355354
Datum val;
356-
int64 val64;
357355
Oid typeid;
358356
int typelen;
359357
bool typebyval;
@@ -384,8 +382,15 @@ make_const(ParseState *pstate, A_Const *aconst)
384382
break;
385383

386384
case T_Float:
385+
{
387386
/* could be an oversize integer as well as a float ... */
388-
if (scanint8(aconst->val.fval.fval, true, &val64))
387+
388+
int64 val64;
389+
char *endptr;
390+
391+
errno = 0;
392+
val64 = strtoi64(aconst->val.fval.fval, &endptr, 10);
393+
if (errno == 0 && *endptr == '\0')
389394
{
390395
/*
391396
* It might actually fit in int32. Probably only INT_MIN can
@@ -425,6 +430,7 @@ make_const(ParseState *pstate, A_Const *aconst)
425430
typebyval = false;
426431
}
427432
break;
433+
}
428434

429435
case T_Boolean:
430436
val = BoolGetDatum(boolVal(&aconst->val));

src/backend/replication/pgoutput/pgoutput.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include "replication/logicalproto.h"
2222
#include "replication/origin.h"
2323
#include "replication/pgoutput.h"
24-
#include "utils/int8.h"
2524
#include "utils/inval.h"
2625
#include "utils/lsyscache.h"
2726
#include "utils/memutils.h"
@@ -207,20 +206,23 @@ parse_output_parameters(List *options, PGOutputData *data)
207206
/* Check each param, whether or not we recognize it */
208207
if (strcmp(defel->defname, "proto_version") == 0)
209208
{
210-
int64 parsed;
209+
unsigned long parsed;
210+
char *endptr;
211211

212212
if (protocol_version_given)
213213
ereport(ERROR,
214214
(errcode(ERRCODE_SYNTAX_ERROR),
215215
errmsg("conflicting or redundant options")));
216216
protocol_version_given = true;
217217

218-
if (!scanint8(strVal(defel->arg), true, &parsed))
218+
errno = 0;
219+
parsed = strtoul(strVal(defel->arg), &endptr, 10);
220+
if (errno != 0 || *endptr != '\0')
219221
ereport(ERROR,
220222
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
221223
errmsg("invalid proto_version")));
222224

223-
if (parsed > PG_UINT32_MAX || parsed < 0)
225+
if (parsed > PG_UINT32_MAX)
224226
ereport(ERROR,
225227
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
226228
errmsg("proto_version \"%s\" out of range",

src/backend/utils/adt/int8.c

Lines changed: 2 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#include "nodes/supportnodes.h"
2525
#include "optimizer/optimizer.h"
2626
#include "utils/builtins.h"
27-
#include "utils/int8.h"
2827

2928

3029
typedef struct
@@ -45,99 +44,14 @@ typedef struct
4544
* Formatting and conversion routines.
4645
*---------------------------------------------------------*/
4746

48-
/*
49-
* scanint8 --- try to parse a string into an int8.
50-
*
51-
* If errorOK is false, ereport a useful error message if the string is bad.
52-
* If errorOK is true, just return "false" for bad input.
53-
*/
54-
bool
55-
scanint8(const char *str, bool errorOK, int64 *result)
56-
{
57-
const char *ptr = str;
58-
int64 tmp = 0;
59-
bool neg = false;
60-
61-
/*
62-
* Do our own scan, rather than relying on sscanf which might be broken
63-
* for long long.
64-
*
65-
* As INT64_MIN can't be stored as a positive 64 bit integer, accumulate
66-
* value as a negative number.
67-
*/
68-
69-
/* skip leading spaces */
70-
while (*ptr && isspace((unsigned char) *ptr))
71-
ptr++;
72-
73-
/* handle sign */
74-
if (*ptr == '-')
75-
{
76-
ptr++;
77-
neg = true;
78-
}
79-
else if (*ptr == '+')
80-
ptr++;
81-
82-
/* require at least one digit */
83-
if (unlikely(!isdigit((unsigned char) *ptr)))
84-
goto invalid_syntax;
85-
86-
/* process digits */
87-
while (*ptr && isdigit((unsigned char) *ptr))
88-
{
89-
int8 digit = (*ptr++ - '0');
90-
91-
if (unlikely(pg_mul_s64_overflow(tmp, 10, &tmp)) ||
92-
unlikely(pg_sub_s64_overflow(tmp, digit, &tmp)))
93-
goto out_of_range;
94-
}
95-
96-
/* allow trailing whitespace, but not other trailing chars */
97-
while (*ptr != '\0' && isspace((unsigned char) *ptr))
98-
ptr++;
99-
100-
if (unlikely(*ptr != '\0'))
101-
goto invalid_syntax;
102-
103-
if (!neg)
104-
{
105-
/* could fail if input is most negative number */
106-
if (unlikely(tmp == PG_INT64_MIN))
107-
goto out_of_range;
108-
tmp = -tmp;
109-
}
110-
111-
*result = tmp;
112-
return true;
113-
114-
out_of_range:
115-
if (!errorOK)
116-
ereport(ERROR,
117-
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
118-
errmsg("value \"%s\" is out of range for type %s",
119-
str, "bigint")));
120-
return false;
121-
122-
invalid_syntax:
123-
if (!errorOK)
124-
ereport(ERROR,
125-
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
126-
errmsg("invalid input syntax for type %s: \"%s\"",
127-
"bigint", str)));
128-
return false;
129-
}
130-
13147
/* int8in()
13248
*/
13349
Datum
13450
int8in(PG_FUNCTION_ARGS)
13551
{
136-
char *str = PG_GETARG_CSTRING(0);
137-
int64 result;
52+
char *num = PG_GETARG_CSTRING(0);
13853

139-
(void) scanint8(str, false, &result);
140-
PG_RETURN_INT64(result);
54+
PG_RETURN_INT64(pg_strtoint64(num));
14155
}
14256

14357

src/backend/utils/adt/numutils.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,90 @@ pg_strtoint32(const char *s)
325325
return 0; /* keep compiler quiet */
326326
}
327327

328+
/*
329+
* Convert input string to a signed 64 bit integer.
330+
*
331+
* Allows any number of leading or trailing whitespace characters. Will throw
332+
* ereport() upon bad input format or overflow.
333+
*
334+
* NB: Accumulate input as a negative number, to deal with two's complement
335+
* representation of the most negative number, which can't be represented as a
336+
* positive number.
337+
*/
338+
int64
339+
pg_strtoint64(const char *s)
340+
{
341+
const char *ptr = s;
342+
int64 tmp = 0;
343+
bool neg = false;
344+
345+
/*
346+
* Do our own scan, rather than relying on sscanf which might be broken
347+
* for long long.
348+
*
349+
* As INT64_MIN can't be stored as a positive 64 bit integer, accumulate
350+
* value as a negative number.
351+
*/
352+
353+
/* skip leading spaces */
354+
while (*ptr && isspace((unsigned char) *ptr))
355+
ptr++;
356+
357+
/* handle sign */
358+
if (*ptr == '-')
359+
{
360+
ptr++;
361+
neg = true;
362+
}
363+
else if (*ptr == '+')
364+
ptr++;
365+
366+
/* require at least one digit */
367+
if (unlikely(!isdigit((unsigned char) *ptr)))
368+
goto invalid_syntax;
369+
370+
/* process digits */
371+
while (*ptr && isdigit((unsigned char) *ptr))
372+
{
373+
int8 digit = (*ptr++ - '0');
374+
375+
if (unlikely(pg_mul_s64_overflow(tmp, 10, &tmp)) ||
376+
unlikely(pg_sub_s64_overflow(tmp, digit, &tmp)))
377+
goto out_of_range;
378+
}
379+
380+
/* allow trailing whitespace, but not other trailing chars */
381+
while (*ptr != '\0' && isspace((unsigned char) *ptr))
382+
ptr++;
383+
384+
if (unlikely(*ptr != '\0'))
385+
goto invalid_syntax;
386+
387+
if (!neg)
388+
{
389+
/* could fail if input is most negative number */
390+
if (unlikely(tmp == PG_INT64_MIN))
391+
goto out_of_range;
392+
tmp = -tmp;
393+
}
394+
395+
return tmp;
396+
397+
out_of_range:
398+
ereport(ERROR,
399+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
400+
errmsg("value \"%s\" is out of range for type %s",
401+
s, "bigint")));
402+
403+
invalid_syntax:
404+
ereport(ERROR,
405+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
406+
errmsg("invalid input syntax for type %s: \"%s\"",
407+
"bigint", s)));
408+
409+
return 0; /* keep compiler quiet */
410+
}
411+
328412
/*
329413
* pg_itoa: converts a signed 16-bit integer to its string representation
330414
* and returns strlen(a).

src/bin/pgbench/pgbench.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,8 +787,8 @@ is_an_int(const char *str)
787787
/*
788788
* strtoint64 -- convert a string to 64-bit integer
789789
*
790-
* This function is a slightly modified version of scanint8() from
791-
* src/backend/utils/adt/int8.c.
790+
* This function is a slightly modified version of pg_strtoint64() from
791+
* src/backend/utils/adt/numutils.c.
792792
*
793793
* The function returns whether the conversion worked, and if so
794794
* "*result" is set to the result.

src/include/utils/builtins.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ extern int namestrcmp(Name name, const char *str);
4646
extern int32 pg_atoi(const char *s, int size, int c);
4747
extern int16 pg_strtoint16(const char *s);
4848
extern int32 pg_strtoint32(const char *s);
49+
extern int64 pg_strtoint64(const char *s);
4950
extern int pg_itoa(int16 i, char *a);
5051
extern int pg_ultoa_n(uint32 l, char *a);
5152
extern int pg_ulltoa_n(uint64 l, char *a);

src/include/utils/int8.h

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)