|
18 | 18 | #include <ctype.h>
|
19 | 19 | #include <limits.h>
|
20 | 20 | #include <float.h>
|
| 21 | +#include <math.h> |
21 | 22 | #include <time.h>
|
22 | 23 |
|
23 | 24 | #include "access/xact.h"
|
@@ -1270,6 +1271,65 @@ tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result)
|
1270 | 1271 | return 0;
|
1271 | 1272 | }
|
1272 | 1273 |
|
| 1274 | +/* time_overflows() |
| 1275 | + * Check to see if a broken-down time-of-day is out of range. |
| 1276 | + */ |
| 1277 | +bool |
| 1278 | +time_overflows(int hour, int min, int sec, fsec_t fsec) |
| 1279 | +{ |
| 1280 | + /* Range-check the fields individually. */ |
| 1281 | + if (hour < 0 || hour > HOURS_PER_DAY || |
| 1282 | + min < 0 || min >= MINS_PER_HOUR || |
| 1283 | + sec < 0 || sec > SECS_PER_MINUTE || |
| 1284 | + fsec < 0 || fsec > USECS_PER_SEC) |
| 1285 | + return true; |
| 1286 | + |
| 1287 | + /* |
| 1288 | + * Because we allow, eg, hour = 24 or sec = 60, we must check separately |
| 1289 | + * that the total time value doesn't exceed 24:00:00. |
| 1290 | + */ |
| 1291 | + if ((((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE) |
| 1292 | + + sec) * USECS_PER_SEC) + fsec) > USECS_PER_DAY) |
| 1293 | + return true; |
| 1294 | + |
| 1295 | + return false; |
| 1296 | +} |
| 1297 | + |
| 1298 | +/* float_time_overflows() |
| 1299 | + * Same, when we have seconds + fractional seconds as one "double" value. |
| 1300 | + */ |
| 1301 | +bool |
| 1302 | +float_time_overflows(int hour, int min, double sec) |
| 1303 | +{ |
| 1304 | + /* Range-check the fields individually. */ |
| 1305 | + if (hour < 0 || hour > HOURS_PER_DAY || |
| 1306 | + min < 0 || min >= MINS_PER_HOUR) |
| 1307 | + return true; |
| 1308 | + |
| 1309 | + /* |
| 1310 | + * "sec", being double, requires extra care. Cope with NaN, and round off |
| 1311 | + * before applying the range check to avoid unexpected errors due to |
| 1312 | + * imprecise input. (We assume rint() behaves sanely with infinities.) |
| 1313 | + */ |
| 1314 | + if (isnan(sec)) |
| 1315 | + return true; |
| 1316 | + sec = rint(sec * USECS_PER_SEC); |
| 1317 | + if (sec < 0 || sec > SECS_PER_MINUTE * USECS_PER_SEC) |
| 1318 | + return true; |
| 1319 | + |
| 1320 | + /* |
| 1321 | + * Because we allow, eg, hour = 24 or sec = 60, we must check separately |
| 1322 | + * that the total time value doesn't exceed 24:00:00. This must match the |
| 1323 | + * way that callers will convert the fields to a time. |
| 1324 | + */ |
| 1325 | + if (((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE) |
| 1326 | + * USECS_PER_SEC) + (int64) sec) > USECS_PER_DAY) |
| 1327 | + return true; |
| 1328 | + |
| 1329 | + return false; |
| 1330 | +} |
| 1331 | + |
| 1332 | + |
1273 | 1333 | /* time2tm()
|
1274 | 1334 | * Convert time data type to POSIX time structure.
|
1275 | 1335 | *
|
@@ -1374,20 +1434,16 @@ make_time(PG_FUNCTION_ARGS)
|
1374 | 1434 | double sec = PG_GETARG_FLOAT8(2);
|
1375 | 1435 | TimeADT time;
|
1376 | 1436 |
|
1377 |
| - /* This should match the checks in DecodeTimeOnly */ |
1378 |
| - if (tm_hour < 0 || tm_min < 0 || tm_min > MINS_PER_HOUR - 1 || |
1379 |
| - sec < 0 || sec > SECS_PER_MINUTE || |
1380 |
| - tm_hour > HOURS_PER_DAY || |
1381 |
| - /* test for > 24:00:00 */ |
1382 |
| - (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0))) |
| 1437 | + /* Check for time overflow */ |
| 1438 | + if (float_time_overflows(tm_hour, tm_min, sec)) |
1383 | 1439 | ereport(ERROR,
|
1384 | 1440 | (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
|
1385 | 1441 | errmsg("time field value out of range: %d:%02d:%02g",
|
1386 | 1442 | tm_hour, tm_min, sec)));
|
1387 | 1443 |
|
1388 | 1444 | /* This should match tm2time */
|
1389 | 1445 | time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
|
1390 |
| - * USECS_PER_SEC) + rint(sec * USECS_PER_SEC); |
| 1446 | + * USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC); |
1391 | 1447 |
|
1392 | 1448 | PG_RETURN_TIMEADT(time);
|
1393 | 1449 | }
|
|
0 commit comments