From f0774abde868e0b5a2acbe75b5028884752f739d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 27 Dec 2016 15:43:54 -0500 Subject: Fix interval_transform so it doesn't throw away non-no-op casts. interval_transform() contained two separate bugs that caused it to sometimes mistakenly decide that a cast from interval to restricted interval is a no-op and throw it away. First, it was wrong to rely on dt.h's field type macros to have an ordering consistent with the field's significance; in one case they do not. This led to mistakenly treating YEAR as less significant than MONTH, so that a cast from INTERVAL MONTH to INTERVAL YEAR was incorrectly discarded. Second, fls(1<constisnull) { Node *source = (Node *) linitial(expr->args); - int32 old_typmod = exprTypmod(source); int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); - int old_range; - int old_precis; - int new_range = INTERVAL_RANGE(new_typmod); - int new_precis = INTERVAL_PRECISION(new_typmod); - int new_range_fls; - int old_range_fls; - - if (old_typmod < 0) - { - old_range = INTERVAL_FULL_RANGE; - old_precis = INTERVAL_FULL_PRECISION; - } + bool noop; + + if (new_typmod < 0) + noop = true; else { - old_range = INTERVAL_RANGE(old_typmod); - old_precis = INTERVAL_PRECISION(old_typmod); - } + int32 old_typmod = exprTypmod(source); + int old_least_field; + int new_least_field; + int old_precis; + int new_precis; + + old_least_field = intervaltypmodleastfield(old_typmod); + new_least_field = intervaltypmodleastfield(new_typmod); + if (old_typmod < 0) + old_precis = INTERVAL_FULL_PRECISION; + else + old_precis = INTERVAL_PRECISION(old_typmod); + new_precis = INTERVAL_PRECISION(new_typmod); - /* - * Temporally-smaller fields occupy higher positions in the range - * bitmap. Since only the temporally-smallest bit matters for length - * coercion purposes, we compare the last-set bits in the ranges. - * Precision, which is to say, sub-second precision, only affects - * ranges that include SECOND. - */ - new_range_fls = fls(new_range); - old_range_fls = fls(old_range); - if (new_typmod < 0 || - ((new_range_fls >= SECOND || new_range_fls >= old_range_fls) && - (old_range_fls < SECOND || new_precis >= MAX_INTERVAL_PRECISION || - new_precis >= old_precis))) + /* + * Cast is a no-op if least field stays the same or decreases + * while precision stays the same or increases. But precision, + * which is to say, sub-second precision, only affects ranges that + * include SECOND. + */ + noop = (new_least_field <= old_least_field) && + (old_least_field > 0 /* SECOND */ || + new_precis >= MAX_INTERVAL_PRECISION || + new_precis >= old_precis); + } + if (noop) ret = relabel_to_typmod(source, new_typmod); } -- cgit v1.2.3