Subject Re: [IBO] Subtle Bug in RequestLive setting
Author Helen Borrie
At 09:15 AM 15/08/2005 +0000, you wrote:
>Hi, All.
>
>There is a subtle bug in the changing of requestlive, in IBO 4.3A. It
>only seems to affect binary (AsRawString) parameters for the query, if
>there is a Character 0 in the parameter. There may be other
>circumstances as well that I am not aware of.

First of all, you shouldn't be trying to set and unset RequestLive once a query is prepared. RequestLive, if true, has one job, which is to automatically construct the XxxxxSQL property statements (Insert, Update and Delete statements) for a dataset, and only IF

a) no custom XxxxxSQL strings are already in those properties
b) it is an updatable dataset (single table query, no aggregates, joins or unions)
c) KeyLinks are accurate

If RequestLive is false at prepare time, it is finished with. However, what I can't tell you is whether meddling with the property *after* the Prepare causes something to get stuffed further down the line.

>Example, assuming statement is already prepared, and the requestlive
>was initially false for the prepare:
>
>1 q.RequestLive:=true;
>2 q.ParamByName('UOID').AsRawString:=#55#56#57#0#58#59;
>3 q.Open;
>4 Anything_you_like;
>
>After line 2 is executed, all is still fine. Reading
>'q.ParamByName('UOID').AsRawString' from the Delphi debugger reveals
>the correct value, of #55#56#57#0#58#59.
>However, as soon as line 3 is executed, the value of
>'q.ParamByName('UOID').AsRawString' has been silently changed, and now
>gets truncated at the 0, leaving a value of #55#56#57.

OK, the next thing you need to make this get treated as a binary string and be passed "raw" - that is, unmodified by trimming and what-have-you - is to make sure that it is passed in quotes. As you have it, Delphi interprets the #0 as a null, the terminator for an array of PChar. It therefore treats the preceding chars as the string and discards the rest. Enclose the string in quotes and cast it as a string:

q.ParamByName('UOID').AsRawString:=QuotedStr(#55#56#57#0#58#59);

In fact, you shouldn't actually need AsRawString. A string of OCTETS is still a string. You only need to take the necessary care to ensure that Delphi doesn't mess with it - as happens when you pass it around unquoted.

Not certain, but you might even have to go to greater lengths and construct the string byte-by-byte, by concatenating the bytes. But try QuotedStr as that's what QuotedStr does itself.

>The net result is that at line 4, you assume you are looking at a
>query that contains what you expect, but it actually contains
>something else altogether. The parameter that is sent through to the
>database engine is the truncated value.

Yup. Because you made it so.


>Although it does not affect most people (I would imagine that there is
>not a lot of binary data in most database columns outside blobs), it
>is pretty serious if you have a binary GUID, for example, in a 16 byte
>OCTETS field. This type of practice is increasing in popularity.

Ummm - it's a lot more common than you seem to think. GUIDs is not the only use for OCTETS strings, by any means. People were using OCTETS strings for years before GUIDs were invented!


>Anyone who is doing this, for now, be VERY careful, and unprepare the
>query (if the requestlive was initially false, and needs to be true),
>change the requestlive to true and reprepare the query. If you don't,
>be prepared to be bitten.

Or, rather, study up what RequestLive is actually meant to be/do. I can't tell from your code what you were trying to achieve there. Setting RequestLive true as a run-time condition, even in the correct part of the setup sequence, would not be recommended without the appropriate checks to ensure that the OCTETS strings were being passed correctly in the WHERE clauses. I'd want to make it persistent in the IDE for at least as long as it takes for me to test and monitor what is generated for the parameters for the the automatic XxxxSQL statements.

The other bad thing about making RequestLive a run-time decision at execution level is that you totally lose the benefit of reusing a prepared statement.

So, RequestLive aside, this should do it for you in BeforeOpen:
var
GuidString: string;
begin
...
with q do
begin
GuidString := QuotedStr(#55#56#57#0#58#59); // or write a function if necessary
if not Prepared then Prepare;
ParamByName('UOID').AsString := GuidString;
Open;
// anything you like
end;
end;

Helen