Locate in Delphi XE2 TADO exception with single quote in search value - delphi

Having trouble with Delphi XE2 (update2) using MS SQL 2008 R2 (sp 2) or with MS SQL 2005 (sp4).
form1.ado_m is via the TADO dataset with no parameter check using native 64 sql
var
okd:boolean;
dd:ansistring;
code snippet:
okd:=form1.ado_m.Locate('abcrow',dd,[loCaseInsensitive]);
If the value of dd ends in a single quote (it is converted to '' in the debugger) it gives a exception:
Exception class EOleException with message 'Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another'.
What is strange to me is that is a single quote character is the middle of var dd it works fine.
Workaround?

The problem is caused by the number sign (#) AND quote together in the search value.
Locate use the ADO find method: The value in Criteria may be a string, floating-point number, or date. String values are delimited with single quotes or "#" (number sign) marks (for example, "state = 'WA'" or "state = #WA#").
Try to replace the number sign OR quote character by inserting records.
Another option is to use the OnFilterRecord and filtered property:
...
FSearchValue := 'A1020778014#;]_69BO'''; // private field in TForm1
ado_m.Filtered := true;
try
okd := not ado_m.IsEmpty;
finally
ado_m.Filtered := false;
end;
...
procedure TForm1.ado_mFilterRecord(DataSet: TDataSet;
var Accept: Boolean);
begin
Accept := SameText(DataSet.FieldByName('abcrow').AsString, FSearchValue);
end;

Related

Machine dependent results for OLE check of MSWord version

With this code to retrieve the version of the installed MS Word:
uses uses oleauto;
[...]
function TForm2.GetWordVersion:string;
const
wdDoNotSaveChanges = 0;
var
WordApp: OLEVariant;
WordVersion: variant;
begin
Try
WordApp := CreateOLEObject('Word.Application');
WordVersion := WordApp.version;
WordApp.Quit(wdDoNotSaveChanges);
except
on E: Exception do
begin
WordVersion := -1;
end;
End;
Result := wordversion;
end;
I get 140 on my machine, my colleague gets 14. Both are win7/Word2010 but I am in Italy he is in India.
Anyone knows about this?
Why different values?
Thanks
I'm guessing this is a decimal separator issue. Word returns the string '14.0' and then when you convert to integer the period is treated as a positional separator on one machine, and a decimal separator on another.
The solution is to stop converting to integer which I infer that you are doing in code that you have not shown.
I am inferring that from this comment:
I can convert it to string and use the first 2 chars.
Since the code in the question operates on strings, I conclude that other code, not shown in the question, is converting to integer.

Using LIKE and '%' in an ADO dataset filter

I have a problem when trying to apply a filter to a ADO dataset in Delphi XE2 ...
Filter := ' [Name] like ''%john'' ';
It raises an exception:
Project Test.exe raised exception class EOleException with message
'Arguments are of the wrong type, are out of acceptable range,
or are in conflict with one another'. Process stopped.
but when using:
Filter := ' [Name] like ''john%'' '
it works fine!
why?
The Operator can only be one of the following:
= < > <= >= <> LIKE
If you use the LIKE operator, you can also use the * or % wildcards as the last character in the string or as the first and last character in the string.
http://www.devguru.com/technologies/ado/quickref/recordset_filter.html
You can catch the filter on the event OnFilterRecord:
procedure TForm1.ADODataSet1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
var
iPos: Integer;
begin
iPos:= pos('john',ADODataSet1name.AsString);
if (iPos>0) and
(iPos = length(ADODataSet1name.AsString)-3) then
begin
Accept:= True;
end
else
begin
Accept:= False;
end;
end;
or
function TForm1.IsLastCriteria(AText: String): Boolean;
var
iPos: Integer;
begin
iPos:= pos(AText,ADODataSet1name.AsString);
Result:= (iPos>0) and
(iPos = length(ADODataSet1name.AsString)-length(AText)-1);
end;
procedure TForm1.ADODataSet1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
Accept:= IsLastCriteria('john');
end;
AS. I still ask you to read http://www.catb.org/esr/faqs/smart-questions.html#beprecise and describe your environment accordingly.
What is database server ?
What version are MDAC/ADO components?
What the query is ?
What is Name column type in SQL ?
You are "upping" the comments, that si nice. But you don't answer the questions. And that is not nice. We are not ESPers, we canot read you mind.
I put you few suggestions in comments above. Did you tried them ? Did they worked ? To quote them:
Maybe you can update ADO/MDAC to 2.8sp1 version. Referenced MSDN KB articles are told to apply to MDAC up to 2.7 version. Maybe 2.8sp1 no more has that limitation.
Maybe you can use some data-server specific tricks like copying last 4 letters of Name into a separate column.
Maybe you can move that condition into SQL SELECT WHERE clause and re-open the query.
Maybe there is event-handler like TBDEDataSet.OnFilterRecord
There are 4 workarounds and you either did not tried them or did not reported the results. Not nice.
Idea #1 is self-explanatory
Idea #3 was detailed by #SertacAkyuz as far as he could do it, giving lack of information about your program.
Idea #4 was detailed by #Ravaut123
Idea #2 is outlined below
Assuming your "ADO database" is backed by Microsoft Access, and according to http://www.databasedev.co.uk/access-sql-string-functions.html ...
Or using MS SQL and according to http://msdn.microsoft.com/en-us/library/ms177532.aspx ...
Or...
Query.SQL.Text := 'select Right(Name, 4) as name_tail, * from table where ...'
See that extra column added to query! 4 is length for "john" there.
With query like that the following filtering conditions are to be equivalent:
Filter := ' [Name] like ''%john'' ';
Filter := ' [name_tail] = ''john'' ';
However, if on some rows Name column is shorter than 4 letters, i don't know what RIGHT function would do. Maybe it will truncate the result, or maybe throw an error and abort the query. The latter maybe - depending on the real data - maybe can be alleviated by padding with spaces to the 4 length like
LTrim(Right(' ' || Name, 4)) as name_tail
That is for you to test, since only you know the details of your environment.
The simplest answer to this question was mentioned in comments.
If you used asterisk instead of percent symbol, everything would work great
Filter := ' [Name] like ''*john'' ';

Delphi & ADO: datetime to string conversion

I use Delphi 2006 and ADO to connect to a MS Access database. Some of the fields I retrieve are Date fields (in Access formatted as "Medium Date" i.e. 20-Apr-2010) however I have to retrieve them as Strings:
FValue:=FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsString;
and then the fields are formatted as follows: 4/20/2010.
My question is: when does this formatting take place and how can I customize it? Is it ADO settings (could not find anything there) or the OS (I use Win XP ENG with US locale)? Or maybe it's Delphi?
Thanks!
Lou
the ShortDateFormat and LongTimeFormat variables are used to format an TDateTimeField to string.
you can change the value of theses variables or try something different like this :
Dt :TDateTime;
Ds :String;
begin
//FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsString
Dt:=FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsDateTime;
Ds:=FormatDateTime('dd-mmm-yyyy',dt);
end;
Ok, just found it. It's delphi general settings (if missing then the values are taken from the OS):
DateSeparator := '-';
ShortDateFormat := 'dd-mmm-yyyy';
And now the returned value is "20-Apr-2010".
You can retreive the Value as DateTime and use this function to convert it to your format
FValue:=FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsDateTime;
function DateToMediumDate(const Date: TDate): string;
var
y, m, d: Word;
begin
DecodeDate(Date, y, m , d);
Result := Format('%d-%s-%d', [d, ShortMonthNames[m], y]);
end;

Help with sending number to Excel 2007 from Delphi 2010 as a string

I'm sending a number to Excel 2007 as a string (Cell.Value := '2,5') using late binding. The actual code is more like:
var CellVal: OLEVariant;
...
CellVal := FloatToStr(2.5); // Regionally formatted.
Cell.Value := CellVal;
On my Excel 97 version, this value will be formatted as "General" by default and will be seen as a number. A customer with Excel 2007 ends up with the cell formatted as "Standard" and Excel appears to see it as a string (it's not right aligned.) Note that I am using the regional settings to format the number and that Excel appears to be using the default regional settings as well.
If the customer just types 2,5 into a cell it accepts it as a number and if he does a copy of the string '2,5' from the clipboard into a cell, it also gets accepted as a number. Does anyone know why the string value sent though the automation interface to Excel ends up as a non-number?
Thanks for any suggestions! Edited to specify the regional decimal separator for the customer is ','.
Since you cannot format comments:
I just did a little test and Excel doesn't want a regional formatted float value as string, it just want a dot as decimal separator.
procedure TForm1.Button1Click(Sender: TObject);
var
App: Variant;
Workbook: Variant;
Worksheet: Variant;
DoubleValue: Double;
begin
App := CreateOleObject('Excel.Application');
Workbook := App.Workbooks.Add;
Worksheet := Workbook.ActiveSheet;
DoubleValue := 1.2;
Worksheet.Range['A1'].Value := DoubleValue; //DoubleValue is a double, excel recognizes a double
Worksheet.Range['A2'].Value := '1.2'; //excel recognizes a double
Worksheet.Range['A3'].Value := '1,2'; //excel recognizes a string
Worksheet.Range['A4'].Value := FloatToStr(1.2); //excel recognizes a string
App.Visible := True;
end;
Keep in mind that I hava a comma as decimal separator.
Probably because you give it a string. Have you tried passing it the float value directly?
Can't explain why the behaviour is different but it would appear to be down to how Excel 2007 interprets the incoming value.
How about setting the format of the cell in code?
Worksheets("Sheet1").Range("A17").NumberFormat = "General"

Getting a Field List from a DBExpress TSQLQuery

I am having a problem getting a list of fields from a query defined at run time by the users of my program. I let my users enter a SQL query into a memo control and then I want to let them go through the fields that will return and do such things as format the output, sum column values and so forth. So, I have to get the column names so they have a place to enter the additional information.
I would do fine if there were no parameters, but I also have to let them define filter parameters for the query. So, if I want to set the parameters to null, I have to know what the parameter's datatype is.
I am using Delphi 2006. I connect to a Firebird 2.1 database using the DBExpress component TSQLConnection and TSQLQuery. Previously, I was successful using:
for i := 0 to Qry.Params.Count - 1 do Qry.Params[i].value := varNull;
I discovered I had a problem when I tried to use a date parameter. It was just a coincidence that all my parameters up until then had been integers (record IDs). It turns out that varNull is just an enumerated constant with a value of 1 so I was getting acceptable results (no records) was working okay.
I only need a list of the fields. Maybe I should just parse the SELECT clause of the SQL statement. I thought setting Qry.Prepared to True would get me a list of the fields but no such luck. It wants values for the parameters.
If you have an idea, I would sure like to hear it. Thanks for any help.
Replied again 'coz I'm interested. My methods works (with my queries) because they have been pre-defined with the params' datatypes preset to the correct type:)
I'm not sure how you are expecting the query to know or derive the datatype of the param given that you are not even selecting the field that it operates against.
So I think your query setup and user input method will need more attention. I've just looked up how I did this a while ago. I do not use a parameterised query - I just get the "parameter values" from the user and put them directly into the SQL. So your sql would then read:
SELECT s.hEmployee, e.sLastName
FROM PR_Paystub s
INNER JOIN PR_Employee e ON e.hKey = s.hEmployee
WHERE s.dtPaydate > '01/01/2008'
therefore no parameter type knowledge is necessary. Does not stop your users entering garbage but that goes back to input control :)
Although a slightly different dataset type this is what I use with TClientDataset simple and effective :)
for i := 0 to FilterDataSet.Params.Count -1 do
begin
Case FilterDataSet.Params.Items[i].Datatype of
ftString:
ftSmallint, ftInteger, ftWord:
ftFloat, ftCurrency, ftBCD:
ftDate:
ftTime:
ftDateTime:
.
.
.
end;
end;
can you not do something similar with the query?
You guys are making this way too hard:
for i := 0 to Qry.Params.Count - 1 do begin
Qry.Params[i].Clear;
Qry.Params[i].Bound := True;
end;
I'm not sure what version of Delphi you are using. In the Delphi 2006 help under Variant Types, it says:
Special conversion rules apply to the
Borland.Delphi.System.TDateTime type
declared in the System unit. When a
Borland.Delphi.System.TDateTime is
converted to any other type, it
treated as a normal Double. When an
integer, real, or Boolean is converted
to a Borland.Delphi.System.TDateTime,
it is first converted to a Double,
then read as a date-time value. When a
string is converted to a
Borland.Delphi.System.TDateTime, it is
interpreted as a date-time value using
the regional settings. When an
Unassigned value is converted to
Borland.Delphi.System.TDateTime, it is
treated like the real or integer value
0. Converting a Null value to Borland.Delphi.System.TDateTime raises
an exception.
The last sentence seems important to me. I would read that as varNull cannot be converted to a TDateTime to put into the field, and hence you get the exception that you're experiencing.
It also implies that this is the only special case.
Couldn't you do something like:
for i := 0 to Qry.Params.Count - 1 do
begin
if VarType(Qry.Params[i].value) and varTypeMask = varDate then
begin
Qry.Params[i].value := Now; //or whatever you choose as your default
end
else
begin
Qry.Params[i].value := varNull;
end;
end;
What I ended up doing was this:
sNull := 'NULL';
Qry.SQL.Add(sSQL);
for i := 0 to Qry.Params.Count - 1 do begin
sParamName := Qry.Params[i].Name;
sSQL := SearchAndReplace (sSQL, ':' + sParamName, sNull, DELIMITERS);
end;
I had to write SearchAndReplace but that was easy. Delimiters are just the characters that signal the end of a word.
TmpQuery.ParamByName('MyDateTimeParam').DataType := ftDate;
TmpQuery.ParamByName('MyDateTimeParam').Clear;
TmpQuery.ParamByName('MyDateTimeParam').Bound := True;

Resources