pgsql-5237

Version:

8.4.1

Bug Link:

http://postgresql.1045698.n5.nabble.com/BUG-5237-strange-int-gt-bit-and-bit-gt-int-conversions-td2128992.html

Symptom:

During ‘int’ to ‘bit’ conversion, postgres produces wrong results when the BIT size is more than 64 and 32 respectively, and the BIT size is not multiple of 8 (so the most significant byte of the integer value is not 0x00 or 0xff)

How it is diagnosed:

Reproduced!

How to reproduce:

/*** Background of the following ‘select’ command:

The double colons in dicate the data type associated with the value on the left. 11::int4, for example, means decimal 11 is an integer composed of 4-bytes. << is a simple left shift oprand. | is is an or oprand. The last ::bit(32)means the value should be represented in the form of a 32-bit vector.  ***/

postgres=# select (11::int4<<23 | 11::int4)::bit(32);

  00000101100000000000000000001011 //result is correct for 32 bit!

posgres=# select (11::int4<<23 | 11::int4)::bit(33);

  000001011100000000000000000001011 //weird result for 33 bit, should be

//000000101100000000000000000001011

postgres=# select (11::int4<<23 | 11::int4)::bit(39);

  000001010000101100000000000000000001011  //weird result for 39 bit,  should be

//000000000000101100000000000000000001011

postgres=# select (11::int4<<23 | 11::int4)::bit(40); //result is correct for 40 bit!

  0000000000000101100000000000000000001011

Root Cause:

In the function bitfromint4, it doesn’t handle the first fractional byte correctly when the output bit width(in the bug above, it’s 33, 39) is wider than 32 bits by something other than a multiple of 8 bits.

Datum bitfromint4(PG_FUNCTION_ARGS)

{

        /* ‘a’ is the input number. */

        int32                a = PG_GETARG_INT32(0);

        …

        // For the case of destbitleft 33 or 39, since the srcbitleft is 32,it will skip!

        while (destbitsleft >= srcbitsleft + 8)

        {

                /* in the case of 40 bit, it will simply fill the msb with sign bits

   0x00 for postive, 0xFF for negative.

                */

                *r++ = (bits8) ((a < 0) ? BITMASK : 0);

                destbitsleft -= 8;

        }

        /* store first fractional byte */

        if (destbitsleft > srcbitsleft)

        {

+                int                val = (int) (a >> (destbitsleft - 8));                

l /* Force sign-fill in case the compiler implements >> as zero-fill */
+                 if (a < 0)
+                        val |= (-1) << (srcbitsleft + 8 - destbitsleft);

        //this just truncate the first 8bits, incorrect

-                *r++ = (bits8) ((a >> (srcbitsleft - 8)) & BITMASK);

+                *r++ = (bits8) (val & BITMASK);

                destbitsleft -= 8;

        }

        ...

        PG_RETURN_VARBIT_P(result)

}

==========

assume we know plantree_list will be used for output result.

exec_simple_query

{

                ...

                plantree_list = pg_plan_queries(querytree_list, 0, NULL);

                PortalDefineQuery(portal,NULL,query_string,commandTag,plantree_list,NULL);

                …

                receiver = CreateDestReceiver(dest);

                if (dest == DestRemote)

                        SetRemoteDestReceiverParams(receiver, portal);

                ...

                (void) PortalRun(portal,...,receiver...);

                ...

}

pg_plan_queries

{

        foreach(query_list, querytrees)

        {

                ...

                else

                {

                        stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);

                }

                stmt_list = lappend(stmt_list, stmt);

        }

        return stmt_list;

}        

pg_plan_query

{

        ...

        plan = planner(querytree, cursorOptions, boundParams);

        …

        return plan;

}

planner

{

        ...

        result = standard_planner(parse, cursorOptions, boundParams);

        return result;

}

/***assume we know planTree field will be the out put field****/

standard_planner

{

        ...

        top_plan = subquery_planner(glob, parse, NULL,false, tuple_fraction, &root);

        ...

        result->planTree = top_plan;

        ...

        return result;

}

subquery_planner(...)

{

        …

        parse->targetList = (List *)

        preprocess_expression(root, (Node *) parse->targetList,

                                                          EXPRKIND_TARGET);

        …

        if (...)

                plan = inheritance_planner(root);

        else

                plan = grouping_planner(root, tuple_fraction);

        ...

        return plan;

}

preprocess_expression

{

        …

        expr = eval_const_expressions(root, expr);

        …

        return expr;

}

eval_const_expressions

{

...

return eval_const_expressions_mutator(node, &context);

}

eval_const_expressions_mutator

{

        return expression_tree_mutator(...);

}

expression_tree_mutator

{

        case T_List:

                resultlist = NIL;

                foreach(temp, (List *) node)

                {

                        resultlist = lappend(resultlist,mutator((Node *) lfirst(temp), context));

                }

                return (Node *) resultlist;

        ...

}

eval_const_expressions_mutator

{

        ...

simple = simplify_function(...);

                if (simple)                        

return (Node *) simple;

        ...

}        

ExecMakeFunctionResult

{

result = FunctionCallInvoke(fcinfo);

        *isNull = fcinfo->isnull;

        pgstat_end_function_usage(&fcusage, true);

return result;

...

}

=

ExecMakeFunctionResult (fcache=0x8bae1d0, econtext=0x8bae658, isNull=0xbfc0bebf "\b\360\247H\265\344\002", isDone=0x0) at execQual.c:1686

1686                        *isNull = fcinfo->isnull;

(gdb) n

1688                        pgstat_end_function_usage(&fcusage, true);

(gdb)

1691                return result;

(gdb)

1692        }

(gdb)

ExecEvalFunc (fcache=0x8bae1d0, econtext=0x8bae658, isNull=0xbfc0bebf "", isDone=0x0) at execQual.c:2117

2117        }

(gdb)

ExecEvalExprSwitchContext (expression=0x8bae1d0, econtext=0x8bae658, isNull=0xbfc0bebf "", isDone=0x0) at execQual.c:4096

4096                MemoryContextSwitchTo(oldContext);

(gdb)

4097                return retDatum;

(gdb)

4098        }

(gdb)

evaluate_expr (expr=0x8b80528, result_type=1560, result_typmod=33) at clauses.c:3847

3847                get_typlenbyval(result_type, &resultTypLen, &resultTypByVal);

(gdb)

3850                MemoryContextSwitchTo(oldcontext);

(gdb)

3859                if (!const_is_null)

(gdb)

3861                        if (resultTypLen == -1)

(gdb)

3862                                const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val));

(gdb)

3868                FreeExecutorState(estate);

(gdb)

3873                return (Expr *) makeConst(result_type, result_typmod, resultTypLen,

(gdb)

3876        }

(gdb)

evaluate_function (funcid=1683, result_type=1560, result_typmod=33, args=0x8b804cc, func_tuple=0xb548a810, context=0xbfc0c73c) at clauses.c:3450

3450        }

(gdb)

simplify_function (funcid=1683, result_type=1560, result_typmod=33, args=0xbfc0c070, allow_inline=1 '\001', context=0xbfc0c73c) at clauses.c:3256

3256                if (!newexpr && allow_inline)

(gdb)

3260                ReleaseSysCache(func_tuple);

(gdb)

3262                return newexpr;

(gdb)

3263        }

(gdb) n

eval_const_expressions_mutator (node=0x8b800c0, context=0xbfc0c73c) at clauses.c:2138

2138                        if (simple)                                /* successfully simplified it */

(gdb)

2139                                return (Node *) simple;

(gdb)

2953        }

(gdb)

expression_tree_mutator (node=0x8b800ec, mutator=0x826c434 <eval_const_expressions_mutator>, context=0xbfc0c73c) at nodeFuncs.c:1899

1899                                        return (Node *) newnode;

(gdb)

2026        }

(gdb)

eval_const_expressions_mutator (node=0x8b800ec, context=0xbfc0c73c) at clauses.c:2953

2953        }

(gdb)

expression_tree_mutator (node=0x8b8012c, mutator=0x826c434 <eval_const_expressions_mutator>, context=0xbfc0c73c) at nodeFuncs.c:1944

1944                                                resultlist = lappend(resultlist,

(gdb)

1942                                        foreach(temp, (List *) node)

(gdb)

1948                                        return (Node *) resultlist;

(gdb)

2026        }

(gdb)

eval_const_expressions_mutator (node=0x8b8012c, context=0xbfc0c73c) at clauses.c:2953

2953        }

Failure type:

Wrong result

Is there any log message?

No

Can ErrLog help?

No. This is simply wrong calculation. The result was wrong.

If we can locate the function “bitfromint4”, and know the execution path within ‘bitfromint4’, it would be sufficient.  Perhaps invariant checking might help, when destbitsleft is not a multiple of 8!