Inline: 2008/10/14 David E Jones <[EMAIL PROTECTED]>: > > I wouldn't say that bugs would be "created" by this change, more than > existing bugs would be exposed (that are only visible on certain databases, > etc).
You know what I mean though, whatever we call them there is going to be a lot to fix. > The service engine doesn't actually do automated mapping, is the service > event handler that does this for web requests before the actual call to the > service engine. In fact when a service is called through the service engine > it will return an error if the object types of values passed in don't match > what is in the service definition. The difference is that it was that way > from the beginning, which isn't the case with certain parts of the entity > engine (mainly in order to not cause the problem of exposing bugs that were > dormant most of the time... which isn't really good in my current opinion). The service event handler, that's what I meant :-) The guess the point I was trying to make was that with so many services being called via the event handler aren't we making a pretty huge exception to the rule that we're trying to enforce? The performFind service would need to use auto-conversion as well so add that the mix and you've got a pretty big chunk of the database operations being performed with auto-converted parameters. Regards Scott > > On Oct 14, 2008, at 2:09 AM, Scott Gray wrote: > >> Ok I see your point, thanks for explaining. >> >> #1 makes sense but it will be interesting to see how many bugs are >> created, also doesn't the service engine currently perform automatic >> conversion without any problems? >> >> Regards >> Scott >> >> 2008/10/14 David E Jones <[EMAIL PROTECTED]>: >>> >>> On Oct 13, 2008, at 10:10 PM, Scott Gray wrote: >>> >>>> Inline: >>>> >>>> 2008/10/14 David E Jones <[EMAIL PROTECTED]>: >>>>> >>>>> Scott, are you referring to the call to PreparedStatement.setString on >>>>> SQLProcessor line 553? In other words, the difference in the Postgres >>>>> JDBC >>>>> driver is that they used to auto-convert from a String to whatever the >>>>> database needs in the setString method, but now they don't? >>>> >>>> When we call PreparedStatement.setString() the JDBC driver now adds >>>> something like String:varchar to the parameter list where it used to >>>> add String:unspecified which meant the database would try and take >>>> care of it, but with varchar it just says "wrong!" and throws an >>>> error. However if we call PreparedStatement.setObject() passing in >>>> the correct sql type then we get String:timestamp and everything works >>>> fine. >>>> >>>> So I guess what I'm saying is that I think java type conversion >>>> (String vs Timestamp) is a separate topic and the problem here is >>>> simply the sql type that we are specifying (by calling the various >>>> PreparedStatement methods) does not match the column type. We could >>>> probably solve the postgresql issue by simply calling >>>> setObject(Types.OTHER) whenever the value type doesn't match entity >>>> field type. >>> >>> The real problem is we're passing bad data back to the database and >>> expecting it to deal with it. Right now the entity engine is just warning >>> about such problems in the log, allowing them to go back to the JDBC >>> driver >>> and/or database where they become a real problem. >>> >>> If we do something like always call setObject then chances are good we'll >>> have problems with other databases. If I remember right we used to do >>> that >>> generically and it caused problems and we had to move to the current >>> approach of using object time specific methods. >>> >>>>> What I'm proposing is to fix this further up the stack in the >>>>> GenericEntity.set method instead of lower down. >>>> >>>> That's fine for when we are using the GenericEntity but we'd also need >>>> to add similar code to the EntityCondition stuff for find queries >>>> (which is where Jacques noticed the problem, I haven't checked for the >>>> error on any operations involving GenericEntity). >>> >>> Good point, we'll have to make sure the object types passed into those >>> are >>> valid as well. >>> >>> -David >>> >>> >>> >>>>> On Oct 13, 2008, at 7:14 PM, Scott Gray wrote: >>>>> >>>>>> Here's the problem as I see it: >>>>>> Postgresql used to allow you to specify parameter types which did not >>>>>> match the column type, in the timestamp case if you passed in a >>>>>> parameter specified as varchar it would automatically attempt to >>>>>> convert it to a timestamp. Now Postgresql requires that you either >>>>>> pass in the correct parameter type for the column or otherwise pass >>>>>> the parameter type as unknown/unspecified. Postgresql still doesn't >>>>>> care whether you pass in a string or a timestamp but you MUST specify >>>>>> the correct sql type. >>>>>> >>>>>> If letting the database take care of the type conversion has never >>>>>> been a problem before, why do we need to worry about it now? Remember >>>>>> the only problem here is that we are passing in a string specified as >>>>>> varchar instead of a string specified as java.sql.Types.Timestamp. >>>>>> >>>>>> Regards >>>>>> Scott >>>>>> >>>>>> 2008/10/14 Adrian Crum <[EMAIL PROTECTED]>: >>>>>>> >>>>>>> I understood your alternatives. I apologize for being unclear. >>>>>>> >>>>>>> You said, "or to just throw an exception when the wrong data type is >>>>>>> passed >>>>>>> like the commented out code in GenericEntity referenced above does." >>>>>>> >>>>>>> And I'm saying we could do that, but it's going to get interesting >>>>>>> because a >>>>>>> lot of code passes Strings into the entity engine. In other words, if >>>>>>> we >>>>>>> throw a "hard" exception, there is a good chance that much of OFBiz >>>>>>> won't >>>>>>> run. >>>>>>> >>>>>>> Personally, I'd like to see better handling of object types in the >>>>>>> higher >>>>>>> level code. I fixed a problem over the weekend that was caused by >>>>>>> this >>>>>>> very >>>>>>> thing (passing Strings into the entity engine). >>>>>>> >>>>>>> -Adrian >>>>>>> >>>>>>> >>>>>>> David E Jones wrote: >>>>>>>> >>>>>>>> I'm referring to the exception I described in the GenericEntity.java >>>>>>>> file, >>>>>>>> lines 410-415. Right now it is just a warning in the log (and has >>>>>>>> been >>>>>>>> for >>>>>>>> years). The reason to do it there instead of letting the JDBC driver >>>>>>>> do >>>>>>>> it >>>>>>>> is that not all developers will test on all possible databases, and >>>>>>>> this >>>>>>>> will help avoid errors as people use different databases in >>>>>>>> development/testing or deployment. >>>>>>>> >>>>>>>> I tried to describe two possible approaches to improve this >>>>>>>> situation >>>>>>>> in >>>>>>>> my first message. Please let me know if those alternatives were not >>>>>>>> clear >>>>>>>> and I'll try to explain them better. >>>>>>>> >>>>>>>> -David >>>>>>>> >>>>>>>> >>>>>>>> On Oct 13, 2008, at 5:22 PM, Adrian Crum wrote: >>>>>>>> >>>>>>>>> Wasn't that being done already? Jacques started these changes based >>>>>>>>> on >>>>>>>>> exceptions being thrown by his JDBC driver. Other DBs seemed to be >>>>>>>>> working >>>>>>>>> okay. >>>>>>>>> >>>>>>>>> I guess we could throw our own exception to keep things consistent >>>>>>>>> across >>>>>>>>> databases. It will be interesting to see how that affects existing >>>>>>>>> code. >>>>>>>>> >>>>>>>>> -Adrian >>>>>>>>> >>>>>>>>> >>>>>>>>> David E Jones wrote: >>>>>>>>>> >>>>>>>>>> That is a very good point, and I agree. >>>>>>>>>> To me that means we go with the fail-fast approach and have it >>>>>>>>>> throw >>>>>>>>>> an >>>>>>>>>> exception if you pass in something that is not the correct object >>>>>>>>>> type. >>>>>>>>>> Is that what you meant? >>>>>>>>>> -David >>>>>>>>>> On Oct 13, 2008, at 4:57 PM, Adrian Crum wrote: >>>>>>>>>>> >>>>>>>>>>> I would prefer handling object types in higher level code - due >>>>>>>>>>> to >>>>>>>>>>> ambiguity in how some types would be converted from a String >>>>>>>>>>> (currency, >>>>>>>>>>> date, time, etc). >>>>>>>>>>> >>>>>>>>>>> -Adrian >>>>>>>>>>> >>>>>>>>>>> David E Jones wrote: >>>>>>>>>>>> >>>>>>>>>>>> This has been a potential problem for a while, but the decision >>>>>>>>>>>> was >>>>>>>>>>>> made quite a while back to not enforce the correct object type. >>>>>>>>>>>> There is code in the GenericEntity.java file (lines 410-415) to >>>>>>>>>>>> check >>>>>>>>>>>> to see if the value passed in matches the object type for the >>>>>>>>>>>> field >>>>>>>>>>>> it is >>>>>>>>>>>> being set on. >>>>>>>>>>>> The best way to solve this problem is a good question. It might >>>>>>>>>>>> be >>>>>>>>>>>> good to introduce some automatic type conversion, or to just >>>>>>>>>>>> throw >>>>>>>>>>>> an >>>>>>>>>>>> exception when the wrong data type is passed like the commented >>>>>>>>>>>> out >>>>>>>>>>>> code in >>>>>>>>>>>> GenericEntity referenced above does. >>>>>>>>>>>> The most common form of automatic conversion is from String to >>>>>>>>>>>> whatever the field's object type is, and that is done by the >>>>>>>>>>>> GenericEntity.setString method. >>>>>>>>>>>> So, what do people think about this? Should we do an automatic >>>>>>>>>>>> type >>>>>>>>>>>> conversion to try to fix programming errors automatically, or do >>>>>>>>>>>> a >>>>>>>>>>>> fail-fast >>>>>>>>>>>> by throwing an exception when the object type is wrong? I >>>>>>>>>>>> suppose >>>>>>>>>>>> the >>>>>>>>>>>> current fail-quiet (in the log) and fail loudly on certain >>>>>>>>>>>> databases >>>>>>>>>>>> later >>>>>>>>>>>> is not the best option... >>>>>>>>>>>> -David >>>>>>>>>>>> On Oct 12, 2008, at 2:20 PM, Scott Gray wrote: >>>>>>>>>>>>> >>>>>>>>>>>>> I've had a look into this and I can't see anything related to >>>>>>>>>>>>> Groovy >>>>>>>>>>>>> that is making this necessary, it appears to be entirely a >>>>>>>>>>>>> postgresql >>>>>>>>>>>>> issue. >>>>>>>>>>>>> >>>>>>>>>>>>> When executing a prepared statement postgresql seems to require >>>>>>>>>>>>> that >>>>>>>>>>>>> the parameter list sql types match the column types. So the >>>>>>>>>>>>> problem >>>>>>>>>>>>> isn't that we are passing in a string but that we are setting >>>>>>>>>>>>> the >>>>>>>>>>>>> sql >>>>>>>>>>>>> type to character varying by using >>>>>>>>>>>>> PreparedStatement.setString(). >>>>>>>>>>>>> >>>>>>>>>>>>> Here's a patch that fixes the issue but I'm not really >>>>>>>>>>>>> confident >>>>>>>>>>>>> enough to commit it, it would be great to get some comments >>>>>>>>>>>>> from >>>>>>>>>>>>> people who know more about this kind of thing: >>>>>>>>>>>>> >>>>>>>>>>>>> Index: >>>>>>>>>>>>> framework/entity/src/org/ofbiz/entity/jdbc/SQLProcessor.java >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> =================================================================== >>>>>>>>>>>>> --- >>>>>>>>>>>>> framework/entity/src/org/ofbiz/entity/jdbc/SQLProcessor.java >>>>>>>>>>>>> (revision >>>>>>>>>>>>> 703572) >>>>>>>>>>>>> +++ >>>>>>>>>>>>> framework/entity/src/org/ofbiz/entity/jdbc/SQLProcessor.java >>>>>>>>>>>>> (working copy) >>>>>>>>>>>>> @@ -592,6 +592,22 @@ >>>>>>>>>>>>> * >>>>>>>>>>>>> * @throws SQLException >>>>>>>>>>>>> */ >>>>>>>>>>>>> + public void setValueTimestampString(String field) throws >>>>>>>>>>>>> SQLException { >>>>>>>>>>>>> + if (field != null) { >>>>>>>>>>>>> + _ps.setObject(_ind, field, Types.TIMESTAMP); >>>>>>>>>>>>> + } else { >>>>>>>>>>>>> + _ps.setNull(_ind, Types.TIMESTAMP); >>>>>>>>>>>>> + } >>>>>>>>>>>>> + _ind++; >>>>>>>>>>>>> + } >>>>>>>>>>>>> + >>>>>>>>>>>>> + /** >>>>>>>>>>>>> + * Set the next binding variable of the currently active >>>>>>>>>>>>> prepared >>>>>>>>>>>>> statement. >>>>>>>>>>>>> + * >>>>>>>>>>>>> + * @param field >>>>>>>>>>>>> + * >>>>>>>>>>>>> + * @throws SQLException >>>>>>>>>>>>> + */ >>>>>>>>>>>>> public void setValue(java.sql.Time field) throws SQLException { >>>>>>>>>>>>> if (field != null) { >>>>>>>>>>>>> _ps.setTime(_ind, field); >>>>>>>>>>>>> Index: >>>>>>>>>>>>> framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> =================================================================== >>>>>>>>>>>>> --- framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java >>>>>>>>>>>>> (revision >>>>>>>>>>>>> 703572) >>>>>>>>>>>>> +++ framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java >>>>>>>>>>>>> (working copy) >>>>>>>>>>>>> @@ -731,6 +731,9 @@ >>>>>>>>>>>>> fieldClassName = "byte[]"; >>>>>>>>>>>>> } >>>>>>>>>>>>> >>>>>>>>>>>>> + if ("java.sql.Timestamp".equals(fieldType)) { >>>>>>>>>>>>> + fieldClassName = fieldType; >>>>>>>>>>>>> + } >>>>>>>>>>>>> if (Debug.verboseOn()) Debug.logVerbose("type of >>>>>>>>>>>>> field " + entityName + "." + modelField.getName() + >>>>>>>>>>>>> " is " + fieldClassName + ", was expecting " >>>>>>>>>>>>> + mft.getJavaType() + "; this may " + >>>>>>>>>>>>> "indicate an error in the configuration or in >>>>>>>>>>>>> the class, and may result " + >>>>>>>>>>>>> @@ -749,7 +752,11 @@ >>>>>>>>>>>>> break; >>>>>>>>>>>>> >>>>>>>>>>>>> case 2: >>>>>>>>>>>>> - sqlP.setValue((java.sql.Timestamp) >>>>>>>>>>>>> fieldValue); >>>>>>>>>>>>> + if (fieldValue instanceof String) { >>>>>>>>>>>>> + sqlP.setValueTimestampString((String) >>>>>>>>>>>>> fieldValue); >>>>>>>>>>>>> + } else { >>>>>>>>>>>>> + sqlP.setValue((java.sql.Timestamp) >>>>>>>>>>>>> fieldValue); >>>>>>>>>>>>> + } >>>>>>>>>>>>> break; >>>>>>>>>>>>> >>>>>>>>>>>>> case 3: >>>>>>>>>>>>> >>>>>>>>>>>>> Regards >>>>>>>>>>>>> Scott >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> 2008/10/13 Jacques Le Roux <[EMAIL PROTECTED]>: >>>>>>>>>>>>>> >>>>>>>>>>>>>> Done in revision: 703816 >>>>>>>>>>>>>> It was not possible for PackingSlip.groovy and >>>>>>>>>>>>>> FindInventoryEventPlan.groovy. Because there the date string >>>>>>>>>>>>>> is >>>>>>>>>>>>>> build >>>>>>>>>>>>>> dynamically in the Groovy file >>>>>>>>>>>>>> >>>>>>>>>>>>>> Jacques >>>>>>>>>>>>>> >>>>>>>>>>>>>> From: "Jacques Le Roux" <[EMAIL PROTECTED]> >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> Adrian, >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> Yes good idea indeed, I will do that >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> Jacques >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> From: "Adrian Crum" <[EMAIL PROTECTED]> >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> Jacques, >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> Instead of modifying the groovy files, try specifying the >>>>>>>>>>>>>>>> data >>>>>>>>>>>>>>>> type in >>>>>>>>>>>>>>>> the screen widget. >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> Example in ReportFinancialSummaryScreens.xml: >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> <set field="fromDate" from-field="parameters.fromDate" >>>>>>>>>>>>>>>> type="Timestamp"/> >>>>>>>>>>>>>>>> <set field="thruDate" from-field="parameters.thruDate" >>>>>>>>>>>>>>>> type="Timestamp"/> >>>>>>>>>>>>>>>> <script >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> location="component://accounting/webapp/accounting/WEB-INF/actions/reports/TransactionTotals.groovy"/> >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> -Adrian >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>> >>>>>>>> >>>>>>> >>>>> >>>>> >>> >>> > >