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