*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.166 2008/09/01 20:42:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.167 2008/09/10 18:29:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Const *newcon = makeNode(Const);
Oid baseTypeId;
int32 baseTypeMod;
+ int32 inputTypeMod;
Type targetType;
ParseCallbackState pcbstate;
* what we want here. The needed check will be applied properly
* inside coerce_to_domain().
*/
- baseTypeMod = -1;
+ baseTypeMod = targetTypeMod;
baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
+ /*
+ * For most types we pass typmod -1 to the input routine, because
+ * existing input routines follow implicit-coercion semantics for
+ * length checks, which is not always what we want here. Any length
+ * constraint will be applied later by our caller. An exception
+ * however is the INTERVAL type, for which we *must* pass the typmod
+ * or it won't be able to obey the bizarre SQL-spec input rules.
+ * (Ugly as sin, but so is this part of the spec...)
+ */
+ if (baseTypeId == INTERVALOID)
+ inputTypeMod = baseTypeMod;
+ else
+ inputTypeMod = -1;
+
targetType = typeidType(baseTypeId);
newcon->consttype = baseTypeId;
- newcon->consttypmod = -1;
+ newcon->consttypmod = inputTypeMod;
newcon->constlen = typeLen(targetType);
newcon->constbyval = typeByVal(targetType);
newcon->constisnull = con->constisnull;
setup_parser_errposition_callback(&pcbstate, pstate, con->location);
/*
- * We pass typmod -1 to the input routine, primarily because existing
- * input routines follow implicit-coercion semantics for length
- * checks, which is not always what we want here. Any length
- * constraint will be applied later by our caller.
- *
* We assume here that UNKNOWN's internal representation is the same
* as CSTRING.
*/
if (!con->constisnull)
newcon->constvalue = stringTypeDatum(targetType,
DatumGetCString(con->constvalue),
- -1);
+ inputTypeMod);
else
- newcon->constvalue = stringTypeDatum(targetType, NULL, -1);
+ newcon->constvalue = stringTypeDatum(targetType,
+ NULL,
+ inputTypeMod);
cancel_parser_errposition_callback(&pcbstate);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.190 2008/06/09 19:34:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.191 2008/09/10 18:29:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static int DecodeNumberField(int len, char *str,
int fmask, int *tmask,
struct pg_tm * tm, fsec_t *fsec, bool *is2digits);
-static int DecodeTime(char *str, int fmask, int *tmask,
- struct pg_tm * tm, fsec_t *fsec);
+static int DecodeTime(char *str, int fmask, int range,
+ int *tmask, struct pg_tm * tm, fsec_t *fsec);
static int DecodeTimezone(char *str, int *tzp);
static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
break;
case DTK_TIME:
- dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
+ dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE,
+ &tmask, tm, fsec);
if (dterr)
return dterr;
case DTK_TIME:
dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
+ INTERVAL_FULL_RANGE,
&tmask, tm, fsec);
if (dterr)
return dterr;
* used to represent time spans.
*/
static int
-DecodeTime(char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec)
+DecodeTime(char *str, int fmask, int range,
+ int *tmask, struct pg_tm * tm, fsec_t *fsec)
{
char *cp;
{
tm->tm_sec = 0;
*fsec = 0;
+ /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
+ if (range == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
+ {
+ tm->tm_sec = tm->tm_min;
+ tm->tm_min = tm->tm_hour;
+ tm->tm_hour = 0;
+ }
}
else if (*cp != ':')
return DTERR_BAD_FORMAT;
* preceding an hh:mm:ss field. - thomas 1998-04-30
*/
int
-DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, fsec_t *fsec)
+DecodeInterval(char **field, int *ftype, int nf, int range,
+ int *dtype, struct pg_tm * tm, fsec_t *fsec)
{
bool is_before = FALSE;
char *cp;
switch (ftype[i])
{
case DTK_TIME:
- dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
+ dterr = DecodeTime(field[i], fmask, range,
+ &tmask, tm, fsec);
if (dterr)
return dterr;
type = DTK_DAY;
while (*cp != '\0' && *cp != ':' && *cp != '.')
cp++;
if (*cp == ':' &&
- DecodeTime(field[i] + 1, fmask, &tmask, tm, fsec) == 0)
+ DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE,
+ &tmask, tm, fsec) == 0)
{
if (*field[i] == '-')
{
type = DTK_HOUR;
}
}
- /* DROP THROUGH */
+ /* FALL THROUGH */
case DTK_DATE:
case DTK_NUMBER:
+ if (type == IGNORE_DTF)
+ {
+ /* use typmod to decide what rightmost integer field is */
+ switch (range)
+ {
+ case INTERVAL_MASK(YEAR):
+ type = DTK_YEAR;
+ break;
+ case INTERVAL_MASK(MONTH):
+ case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
+ type = DTK_MONTH;
+ break;
+ case INTERVAL_MASK(DAY):
+ type = DTK_DAY;
+ break;
+ case INTERVAL_MASK(HOUR):
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+ type = DTK_HOUR;
+ break;
+ case INTERVAL_MASK(MINUTE):
+ case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+ type = DTK_MINUTE;
+ break;
+ case INTERVAL_MASK(SECOND):
+ case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+ case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+ type = DTK_SECOND;
+ break;
+ default:
+ type = DTK_SECOND;
+ break;
+ }
+ }
+
errno = 0;
val = strtoi(field[i], &cp, 10);
if (errno == ERANGE)
return DTERR_FIELD_OVERFLOW;
- if (type == IGNORE_DTF)
- type = DTK_SECOND;
+ if (*cp == '-')
+ {
+ /* SQL "years-months" syntax */
+ int val2;
- if (*cp == '.')
+ val2 = strtoi(cp + 1, &cp, 10);
+ if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
+ return DTERR_FIELD_OVERFLOW;
+ if (*cp != '\0')
+ return DTERR_BAD_FORMAT;
+ type = DTK_MONTH;
+ val = val * MONTHS_PER_YEAR + val2;
+ fval = 0;
+ }
+ else if (*cp == '.')
{
fval = strtod(cp, &cp);
if (*cp != '\0')
#endif
}
tmask = DTK_M(HOUR);
+ type = DTK_DAY;
break;
case DTK_DAY:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.155 2008/03/25 22:42:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.156 2008/09/10 18:29:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
- dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec);
+ dterr = DecodeInterval(field, ftype, nf, INTERVAL_FULL_RANGE,
+ &dtype, tm, &fsec);
if (dterr != 0)
{
if (dterr == DTERR_FIELD_OVERFLOW)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.190 2008/07/07 18:09:46 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.191 2008/09/10 18:29:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*tm = &tt;
int dtype;
int nf;
+ int range;
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
tm->tm_sec = 0;
fsec = 0;
+ if (typmod >= 0)
+ range = INTERVAL_RANGE(typmod);
+ else
+ range = INTERVAL_FULL_RANGE;
+
dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field,
ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
- dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec);
+ dterr = DecodeInterval(field, ftype, nf, range, &dtype, tm, &fsec);
if (dterr != 0)
{
if (dterr == DTERR_FIELD_OVERFLOW)
* Unspecified range and precision? Then not necessary to adjust. Setting
* typmod to -1 is the convention for all types.
*/
- if (typmod != -1)
+ if (typmod >= 0)
{
int range = INTERVAL_RANGE(typmod);
int precision = INTERVAL_PRECISION(typmod);
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.69 2008/01/01 19:45:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.70 2008/09/10 18:29:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int nf, int *dtype,
struct pg_tm * tm, fsec_t *fsec, int *tzp);
extern int DecodeInterval(char **field, int *ftype,
- int nf, int *dtype,
+ int nf, int range, int *dtype,
struct pg_tm * tm, fsec_t *fsec);
extern void DateTimeParseError(int dterr, const char *str,
const char *datatype);
ERROR: invalid input syntax for type interval: "1:20:05 5 microseconds"
LINE 1: SELECT '1:20:05 5 microseconds'::interval;
^
+SELECT interval '1-2'; -- SQL year-month literal
+ interval
+---------------
+ 1 year 2 mons
+(1 row)
+
+-- test SQL-spec syntaxes for restricted field sets
+SELECT interval '1' year;
+ interval
+----------
+ 1 year
+(1 row)
+
+SELECT interval '2' month;
+ interval
+----------
+ 2 mons
+(1 row)
+
+SELECT interval '3' day;
+ interval
+----------
+ 3 days
+(1 row)
+
+SELECT interval '4' hour;
+ interval
+----------
+ 04:00:00
+(1 row)
+
+SELECT interval '5' minute;
+ interval
+----------
+ 00:05:00
+(1 row)
+
+SELECT interval '6' second;
+ interval
+----------
+ 00:00:06
+(1 row)
+
+SELECT interval '1' year to month;
+ interval
+----------
+ 1 mon
+(1 row)
+
+SELECT interval '1-2' year to month;
+ interval
+---------------
+ 1 year 2 mons
+(1 row)
+
+SELECT interval '1 2' day to hour;
+ interval
+----------------
+ 1 day 02:00:00
+(1 row)
+
+SELECT interval '1 2:03' day to hour;
+ interval
+----------------
+ 1 day 02:00:00
+(1 row)
+
+SELECT interval '1 2:03:04' day to hour;
+ interval
+----------------
+ 1 day 02:00:00
+(1 row)
+
+SELECT interval '1 2' day to minute;
+ interval
+----------------
+ 1 day 02:00:00
+(1 row)
+
+SELECT interval '1 2:03' day to minute;
+ interval
+----------------
+ 1 day 02:03:00
+(1 row)
+
+SELECT interval '1 2:03:04' day to minute;
+ interval
+----------------
+ 1 day 02:03:00
+(1 row)
+
+SELECT interval '1 2' day to second;
+ interval
+----------------
+ 1 day 02:00:00
+(1 row)
+
+SELECT interval '1 2:03' day to second;
+ interval
+----------------
+ 1 day 02:03:00
+(1 row)
+
+SELECT interval '1 2:03:04' day to second;
+ interval
+----------------
+ 1 day 02:03:04
+(1 row)
+
+SELECT interval '1 2' hour to minute;
+ERROR: invalid input syntax for type interval: "1 2"
+LINE 1: SELECT interval '1 2' hour to minute;
+ ^
+SELECT interval '1 2:03' hour to minute;
+ interval
+----------
+ 02:03:00
+(1 row)
+
+SELECT interval '1 2:03:04' hour to minute;
+ interval
+----------
+ 02:03:00
+(1 row)
+
+SELECT interval '1 2' hour to second;
+ERROR: invalid input syntax for type interval: "1 2"
+LINE 1: SELECT interval '1 2' hour to second;
+ ^
+SELECT interval '1 2:03' hour to second;
+ interval
+----------
+ 02:03:00
+(1 row)
+
+SELECT interval '1 2:03:04' hour to second;
+ interval
+----------
+ 02:03:04
+(1 row)
+
+SELECT interval '1 2' minute to second;
+ERROR: invalid input syntax for type interval: "1 2"
+LINE 1: SELECT interval '1 2' minute to second;
+ ^
+SELECT interval '1 2:03' minute to second;
+ interval
+----------
+ 00:02:03
+(1 row)
+
+SELECT interval '1 2:03:04' minute to second;
+ interval
+----------
+ 00:03:04
+(1 row)
+
SELECT '1 second 2 seconds'::interval; -- error
SELECT '10 milliseconds 20 milliseconds'::interval; -- error
SELECT '5.5 seconds 3 milliseconds'::interval; -- error
-SELECT '1:20:05 5 microseconds'::interval; -- error
\ No newline at end of file
+SELECT '1:20:05 5 microseconds'::interval; -- error
+SELECT interval '1-2'; -- SQL year-month literal
+
+-- test SQL-spec syntaxes for restricted field sets
+SELECT interval '1' year;
+SELECT interval '2' month;
+SELECT interval '3' day;
+SELECT interval '4' hour;
+SELECT interval '5' minute;
+SELECT interval '6' second;
+SELECT interval '1' year to month;
+SELECT interval '1-2' year to month;
+SELECT interval '1 2' day to hour;
+SELECT interval '1 2:03' day to hour;
+SELECT interval '1 2:03:04' day to hour;
+SELECT interval '1 2' day to minute;
+SELECT interval '1 2:03' day to minute;
+SELECT interval '1 2:03:04' day to minute;
+SELECT interval '1 2' day to second;
+SELECT interval '1 2:03' day to second;
+SELECT interval '1 2:03:04' day to second;
+SELECT interval '1 2' hour to minute;
+SELECT interval '1 2:03' hour to minute;
+SELECT interval '1 2:03:04' hour to minute;
+SELECT interval '1 2' hour to second;
+SELECT interval '1 2:03' hour to second;
+SELECT interval '1 2:03:04' hour to second;
+SELECT interval '1 2' minute to second;
+SELECT interval '1 2:03' minute to second;
+SELECT interval '1 2:03:04' minute to second;