-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: Possible Bug - c3p0.maxStatements > 1
PostPosted: Mon Apr 30, 2012 5:22 am 
Regular
Regular

Joined: Sat Apr 23, 2005 7:28 am
Posts: 52
Hibernate 4.1.2
C3P0 0.9.1 or 0.9.2
PervasiveSQL

I'm raising this as an issue here as a caution to others who may be using c3p0 with maxStatements (or maxStatementsPerConnection) configured with a value > 1.

In an effort to performance tune my application, I have been experimenting with various settings of the c3p0.maxStatements parameter.

When configured with any value greater than 1, there is a danger that erroneous results will be returned when using parameterised queries.

I first found this issue when testing the following code:

Code:
        SQLQuery query =
            session.createSQLQuery(
                "select 'ST' as type, null as date1, "
                + "null as date2, null as wo_no, null as so_no,  null  as line_no, "
                + "0 AS demand, sum(st.qty_on_hand) as balance, 0 as supply from Stock st "
                + "where st.part_no = :part_no " + "union all "
                + "select 'WO' as type, mats.due_date as date1, mats.due_date as date2, "
                + "mats.wo_no as wo_no , wo.so_no as so_no, null as line_no, "
                + "sum(mats.qty_required - mats.qty_issd) as demand, 0 as balance, 0 as supply "
                + "from Materials mats, Wo wo " + "where mats.wo_no = wo.wo_no and mats.completely_issd = 'N' "
                + "and mats.part_no = :part_no "
                + "group by type, date1, date2, wo_no, so_no, line_no, balance, supply union all "
                + "select 'SO' as type, soi.due_date as date1, soi.due_date as date2, "
                + "null as wo_no, soi.so_no as so_no, soi.so_line_no as line_no, "
                + "sum(soi.qty - soi.qty_del) as demand, 0 as balance, 0 as supply " + "from So_items soi, Part p "
                + "where soi.part_no = p.part_no and p.mbf_code = 'B' "
                + "and soi.qty > soi.qty_del and soi.fully_del <> 'Y' " + "and soi.part_no = :part_no "
                + "group by type, date1, date2, wo_no, so_no, line_no, balance, supply " + "union all "
                + "select 'PO' as type, pod.due_date as date1, pod.due_date as date2, "
                + "null as wo_no, pod.po_no as so_no, pod.po_line_no as line_no, 0 as demand, 0 as balance, "
                + "if (pod.del_complete = 'Y', "
                + "sum(pod.qty_received - pod.qty_stored),  sum(pod.qty_ordered - pod.qty_stored)) as supply "
                + "from Po_detail pod "
                + "where ( pod.del_complete = 'N' or (pod.qty_stored + pod.total_qty_scrapped) < pod.qty_ordered ) "
                + "and pod.part_no = :part_no "
                + " and ((pod.del_complete = 'Y' and pod.qty_received - pod.qty_stored > 0) or "
                + " (pod.del_complete = 'N' and pod.qty_ordered - pod.qty_stored > 0)) "
                + "group by type, date1, date2, wo_no, so_no, line_no, del_complete, balance, demand order by 2 asc, 1 asc");

        query.setString("part_no", part_no);
        query.addScalar("type", StandardBasicTypes.STRING);
        query.addScalar("date1", StandardBasicTypes.DATE);
        query.addScalar("date2", StandardBasicTypes.DATE);
        query.addScalar("wo_no", StandardBasicTypes.STRING);
        query.addScalar("so_no", StandardBasicTypes.STRING);
        query.addScalar("line_no", StandardBasicTypes.INTEGER);
        query.addScalar("demand", StandardBasicTypes.DOUBLE);
        query.addScalar("balance", StandardBasicTypes.DOUBLE);
        query.addScalar("supply", StandardBasicTypes.DOUBLE);

        List<Object[]> oList = query.list();


During execution (in the same session), the above is executed multiple times (usually ~ 2000 times) with the :part_no parameter being replaced at each successive execution.
If c3p0 statement caching is enabled, the query erroneously returns the SAME result for each execution.

However, if I modify the code as follows I can safely enable c3p0 statement caching:

Code:
        String q = "select 'ST' as type, null as date1, "
                + "null as date2, null as wo_no, null as so_no,  null  as line_no, "
                + "0 AS demand, sum(st.qty_on_hand) as balance, 0 as supply from Stock st "
                + "where st.part_no = :part_no " + "union all "
                + "select 'WO' as type, mats.due_date as date1, mats.due_date as date2, "
                + "mats.wo_no as wo_no , wo.so_no as so_no, null as line_no, "
                + "sum(mats.qty_required - mats.qty_issd) as demand, 0 as balance, 0 as supply "
                + "from Materials mats, Wo wo " + "where mats.wo_no = wo.wo_no and mats.completely_issd = 'N' "
                + "and mats.part_no = :part_no "
                + "group by type, date1, date2, wo_no, so_no, line_no, balance, supply union all "
                + "select 'SO' as type, soi.due_date as date1, soi.due_date as date2, "
                + "null as wo_no, soi.so_no as so_no, soi.so_line_no as line_no, "
                + "sum(soi.qty - soi.qty_del) as demand, 0 as balance, 0 as supply from So_items soi, Part p "
                + "where soi.part_no = p.part_no and p.mbf_code = 'B' "
                + "and soi.qty > soi.qty_del and soi.fully_del <> 'Y' " + "and soi.part_no = :part_no "
                + "group by type, date1, date2, wo_no, so_no, line_no, balance, supply " + "union all "
                + "select 'PO' as type, pod.due_date as date1, pod.due_date as date2, "
                + "null as wo_no, pod.po_no as so_no, pod.po_line_no as line_no, " + "0 as demand, 0 as balance, "
                + "if (pod.del_complete = 'Y', "
                + "sum(pod.qty_received - pod.qty_stored),  sum(pod.qty_ordered - pod.qty_stored)) as supply "
                + "from Po_detail pod "
                + "where ( pod.del_complete = 'N' or (pod.qty_stored + pod.total_qty_scrapped) < pod.qty_ordered ) "
                + "and pod.part_no = :part_no "
                + " and ((pod.del_complete = 'Y' and pod.qty_received - pod.qty_stored > 0) or "
                + " (pod.del_complete = 'N' and pod.qty_ordered - pod.qty_stored > 0)) "
                + "group by type, date1, date2, wo_no, so_no, line_no, del_complete, balance, demand order by 2 asc, 1 asc";
       
       q =  q.replace(":part_no", "'"+part_no+"'");
       
        SQLQuery query = session.createSQLQuery(q);

        query.addScalar("type", StandardBasicTypes.STRING);
        query.addScalar("date1", StandardBasicTypes.DATE);
        query.addScalar("date2", StandardBasicTypes.DATE);
        query.addScalar("wo_no", StandardBasicTypes.STRING);
        query.addScalar("so_no", StandardBasicTypes.STRING);
        query.addScalar("line_no", StandardBasicTypes.INTEGER);
        query.addScalar("demand", StandardBasicTypes.DOUBLE);
        query.addScalar("balance", StandardBasicTypes.DOUBLE);
        query.addScalar("supply", StandardBasicTypes.DOUBLE);

        List<Object[]> oList = query.list()


Debugging the classes between org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler and com.mchange.v2.c3p0.stmt.GooGooStatementCache is particularly difficult as some of the methods utilise reflection but it appears that using the first scheme (query.setString("part_no", part_no);), c3p0 is acually caching the statement:

Code:
"select 'ST' as type, null as date1, null as date2, null as wo_no, null as so_no,  null  as line_no, 0 AS demand, sum(st.qty_on_hand) as balance, 0 as supply from Stock st where st.part_no = ? union all select 'WO' as type, mats.due_date as date1, mats.due_date as date2, mats.wo_no as wo_no , wo.so_no as so_no, null as line_no, sum(mats.qty_required - mats.qty_issd) as demand, 0 as balance, 0 as supply from Materials mats, Wo wo where mats.wo_no = wo.wo_no and mats.completely_issd = 'N' and mats.part_no = ? group by type, date1, date2, wo_no, so_no, line_no, balance, supply union all select 'SO' as type, soi.due_date as date1, soi.due_date as date2, null as wo_no, soi.so_no as so_no, soi.so_line_no as line_no, sum(soi.qty - soi.qty_del) as demand, 0 as balance, 0 as supply from So_items soi, Part p where soi.part_no = p.part_no and p.mbf_code = 'B' and soi.qty > soi.qty_del and soi.fully_del <> 'Y' and soi.part_no = ? group by type, date1, date2, wo_no, so_no, line_no, balance, supply union all select 'PO' as type, pod.due_date as date1, pod.due_date as date2, null as wo_no, pod.po_no as so_no, pod.po_line_no as line_no, 0 as demand, 0 as balance, if (pod.del_complete = 'Y', sum(pod.qty_received - pod.qty_stored),  sum(pod.qty_ordered - pod.qty_stored)) as supply from Po_detail pod where ( pod.del_complete = 'N' or (pod.qty_stored + pod.total_qty_scrapped) < pod.qty_ordered ) and pod.part_no = ?  and ((pod.del_complete = 'Y' and pod.qty_received - pod.qty_stored > 0) or  (pod.del_complete = 'N' and pod.qty_ordered - pod.qty_stored > 0)) group by type, date1, date2, wo_no, so_no, line_no, del_complete, balance, demand order by 2 asc, 1 asc"


NO parameters replaced!!

Curiously, the first time the query is executed is returns the correct results (presumably because the statement is not found in the cache) all susbsequent executions return the SAME results as the first because the statement has been cached.

However, if I use the second form (q = q.replace(":part_no", "'"+part_no+"'");), the parameters are replaced like:

Code:
"select 'ST' as type, null as date1, null as date2, null as wo_no, null as so_no,  null  as line_no, 0 AS demand, sum(st.qty_on_hand) as balance, 0 as supply from Stock st where st.part_no = '028975                   ' union all select 'WO' as type, mats.due_date as date1, mats.due_date as date2, mats.wo_no as wo_no , wo.so_no as so_no, null as line_no, sum(mats.qty_required - mats.qty_issd) as demand, 0 as balance, 0 as supply from Materials mats, Wo wo where mats.wo_no = wo.wo_no and mats.completely_issd = 'N' and mats.part_no = '028975                   ' group by type, date1, date2, wo_no, so_no, line_no, balance, supply union all select 'SO' as type, soi.due_date as date1, soi.due_date as date2, null as wo_no, soi.so_no as so_no, soi.so_line_no as line_no, sum(soi.qty - soi.qty_del) as demand, 0 as balance, 0 as supply from So_items soi, Part p where soi.part_no = p.part_no and p.mbf_code = 'B' and soi.qty > soi.qty_del and soi.fully_del <> 'Y' and soi.part_no = '028975                   ' group by type, date1, date2, wo_no, so_no, line_no, balance, supply union all select 'PO' as type, pod.due_date as date1, pod.due_date as date2, null as wo_no, pod.po_no as so_no, pod.po_line_no as line_no, 0 as demand, 0 as balance, if (pod.del_complete = 'Y', sum(pod.qty_received - pod.qty_stored),  sum(pod.qty_ordered - pod.qty_stored)) as supply from Po_detail pod where ( pod.del_complete = 'N' or (pod.qty_stored + pod.total_qty_scrapped) < pod.qty_ordered ) and pod.part_no = '028975                   '  and ((pod.del_complete = 'Y' and pod.qty_received - pod.qty_stored > 0) or  (pod.del_complete = 'N' and pod.qty_ordered - pod.qty_stored > 0)) group by type, date1, date2, wo_no, so_no, line_no, del_complete, balance, demand order by 2 asc, 1 asc"


The query now succeeds returning different results with each invocation.

The only conclusion I have drawn so far is that it is NOT safe to use c3p0 statement caching!!

Time permitting , I shall endeavour to debug further over the coming days.

db


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.