Delphi Load Blob from MySql - mysql

in my project i use mysql.pas to deal with MySql database , so i've this Table structure :
Table Name :CarsTbl
ID int 10
CarName varchar 100
Car_Img longblob 0
i use this table to save each Car with its name and its picture .
The save routine works very well .
But the problem is when getting the Car_Img in which i use the following procedure :
procedure GetCarImage(const MyCarName:String;TrgStream:TMemoryStream);
var
query_string: String;
rCount: Integer;
mySQL_Res: PMYSQL_RES;
LibH: PMYSQL;
Row: PMYSQL_ROW;
iLen:PMYSQL_LENGTHS;
begin
mySQL_Res := nil;
Try
Try
query_string := 'SELECT CarName,Car_Img FROM CarsTbl WHERE CarName="'+MyCarName+'"';
mysql_real_query(LibH, PAnsiChar(query_string), Length(query_string));
mySQL_Res := mysql_store_result(LibH);
Try
rCount := mysql_num_rows(mySQL_Res);
If rCount > 0 Then Begin
Repeat
Row := mysql_fetch_row(mySQL_Res);
iLen:= mysql_fetch_lengths(mySQL_Res);
If Row <> nil Then Begin
TrgStream.position :=0;
//Row^[1] ==== Car_Img Blob Field
TrgStream.WriteBuffer(Row^[1],iLen[1]);
End;
Until Row = nil;
end;
Finally
mysql_free_result(mySQL_Res);
mySQL_Res := nil;
Row := nil;
End;
Finally
mysql_close(LibH);
End;
Finally
LibH := nil;
End;
end;
I get the Car image but with Malformed file Header let me explain :
Audi_Car image is saved as a Png image , but when i Load its image i always get it like this :
So please how could correct this ? is there any error in my Sql query ?
P.S : i create TrgStream in an other place.
And here's my LoadCarImage Procedure :
Procedure LoadCarImage();
var
CarStrm:TMemoryStream;
begin
CarStrm:=TMemoryStream.Create;
Try
GetCarImage('Audi',CarStrm);
CarStrm.SaveToFile('audi.png');
finally
CarStrm.Free;
end;
end;
many thanks

you should not use
Repeat ... until
CarName is not unique. So if you have more than one Audi in your table you create also a not valid image file.
Try it with
Delphi5
EDIT:
With
TrgStream.WriteBuffer(Row^[1],iLen[1]);
WriteBuffer takes the first pointer and writing the content of the whole Row.
In your .png file now you have
ID00CarName00Car_Img
9Wî00f­î00Audi00‰PNG.......
I prefer the long way via the array only to see what I get.
Now that you have tested it. we have found no error in the writing of the present stream.
we use instead of
TrgStream.WriteBuffer(Row^[1],iLen[1]);
this
TrgStream.WriteBuffer(Row^[1]^,iLen[1]);
procedure GetCarImage(const MyCarName:String;TrgStream:TMemoryStream);
var
[...]
begin
[...]
query_string := 'SELECT CarName,Car_Img FROM cars WHERE CarName="Audi"';
mysql_real_query(LibH, PAnsiChar(query_string), Length(query_string));
mySQL_Res := mysql_store_result(LibH);
Try
rCount := mysql_num_rows(mySQL_Res);
If rCount > 0 Then Begin
Row := mysql_fetch_row(mySQL_Res);
iLen:= mysql_fetch_lengths(mySQL_Res);
If Row <> nil Then Begin
TrgStream.position :=0;
//Row^[1] ==== Car_Img Blob Field
TrgStream.WriteBuffer(Row^[1]^,iLen[1]);
[...]

I had the allmost same problem.
I wanted read emf (picture metafile) from mysql glob field.
And I solved it like this.
var
text_t1:string;
Stream: TStream;
begin
text_t1:='';
text_t1:=' select emf_glob from table ';
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add(text_t1);
ADOQuery1.Open;
ADOQuery1.first;
Stream:= TMemoryStream.Create;
Stream := ADOQuery1.CreateBlobStream(ADOQuery1.FieldByName('emf_glob'), bmRead);
Stream.Position:=0;
DBImage1.Picture.LoadFromStream(Stream);
Stream.Free;

Related

How to get select results using zeoslib

I am working on a project with mysql, and for this I am doing a class using zeoslib, I can make the connection, I can execute querys like insert, update, etc. But my problem is with select, I run it quietly, but how would I do to get the return? I would like something like PHP in that I inform the column name and it returns me the value of the row that is in that column.
It's important what you want to select, here is an example that uses TZQuery to get an integer value;
function TAccess.getProgramNo(aProgramName:WideString):Integer;
var
q:TZQuery;
begin
Result := -1;
q := TZQuery.Create(Self);
try
q.Connection := conn;
q.SQL.Text := ' SELECT progno FROM programs WHERE name = :name ORDER BY progno ASC ';
q.ParamByName('name').Value := aProgramName;
q.Open;
if q.RecordCount > 0 then
Result := q.FieldByName('progno').AsInteger;
finally
q.Free();
end;
end;
If you want to return a list of objects (i did not compile this);
function TAccess.getPrograms(aProgramName:WideString):TList;
var
q:TZQuery;
begin
Result := TList.Create;
q := TZQuery.Create(Self);
try
q.Connection := conn;
q.SQL.Text := ' SELECT progno FROM programs WHERE name = :name ORDER BY progno ASC ';
q.ParamByName('name').Value := aProgramName;
q.Open;
While not q.EOF do
begin
result.Add(TZoo.Create(....));
q.Next;
end;
finally
q.Free();
end;
end;

delphi mysql query result

for read data from database server im using mysql query like this:
FDQuery1.SQL.Text := 'select * from `table` WHERE dcid=3;';
FDQuery1.Open;
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
but when i have to use a loop for read data from mysql
i need a array to read and store data on it
i know in php its like this
$arr = array();
while($obj = $q->fetch_object() ){
$arr[] = $obj;
}
but how can i do this syntax in Delphi ?
You don't need to do FDQuery1.First as that is implicit in the FDQuery1.Open. However if you want to know how many records you have it's important to do a FDQuery1.Last; before using FDQuery1.RecordCount to get the true record count otherwise you might get strange results.
All you need to do get your data into a memo is this
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
memo1.Lines.clear;
FDQuery1.Open;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
FDQuery1.Close;
although a better solution with minimal exception handling is
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
memo1.Lines.clear;
try
try
begin
FDQuery1.Open;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
end;
except
on E : Exception do
begin
showmessage ('Exception class name = '+E.ClassName+ slinebreak
+ 'Exception message = '+E.Message);
end //on E
end; //try-except
finally
FDQuery1.Close;
end; //try-finally
You mention an array. This is not needed if you only want to put the data in a memo but if you did want to put the data in an array (a dynamic array of variants as you don't know at design time how many record elements you need, how many field elements you need or what type each field is) then you would use the following code.
(Note this is deliberately not optimised code as I was trying to make the process clear)
Const
FirstRecordIndex = 0;
FirstFieldIndex = 0;
Var
DataArray : Variant;
TheRecordCount, TheFieldCount,
RecordNumber, FieldNumber : integer;
Data : variant;
begin
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
FDQuery1.Open;
FDQuery1.Last; //to get correct recordcount
TheRecordCount := FDQuery1.RecordCount;
TheFieldCount := FDQuery1.FieldCount;
FDQuery1.First; //go back to the beginning of the dataset
//set the dimensions of the 2D array of variants to hold data
DataArray := VarArrayCreate([FirstRecordIndex, TheRecordCount, FirstFieldIndex, TheFieldCount], varVariant ); //element can be of any type
//fill it
RecordNumber := -1; //initialise record indexe to just before the start
While not FDQuery1.Eof do
begin
inc(RecordNumber); //point to next record element in the array
for FieldNumber := FirstFieldIndex to TheFieldCount -1 do //load all the fields of this record
begin
Data := FDQuery1.Fields[FieldNumber].asVariant; //get the data
DataArray[RecordNumber, FieldNumber] := Data; //put into array
end;
FDQuery1.next; //get next record
end; //while
end;
FDQuery1.Close;
end;
To get the data back again use
For RecordNumber := FirstRecordIndex to TheRecordCount -1 do
For FieldNumber := FirstFieldIndex to TheFieldCount -1 do
begin
Data := DataArray[RecordNumber, FieldNumber] ;
//do something with the data ie put into a memo
end;
You can use this, i hope that help u:
FDQuery1.SQL.Text := 'select * from `table` WHERE dcid=3;';
FDQuery1.Open;
if FDQuery1.Active and (FDQuery1.RecordCount > 0) then
begin
FDQuery1.First;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
you can do this with array of array consept
for this first you must create your new type
type
myArray = array [1..10] of integer;
myData= array of myArray;
and then you must define your variable
var
Records: myData;
i:integer;
on,now we have aarray of array to save database records on it so
begin
FDQuery1.SQL.Text := 'select * from table WHERE type=3;';
FDQuery1.Open;
FDQuery1.First;
SetLength(Records ,FDQuery1.recordcount);//or maybe you need a do a select count(*) query first
i:=0;
While not FDQuery1.Eof do
begin
Records[i][1]:= FDQuery1.FieldByName('value1').AsString;
Records[i][2]:= FDQuery1.FieldByName('value2').AsString;
Records[i][3]:= FDQuery1.FieldByName('value3').AsString;
Records[i][4]:= FDQuery1.FieldByName('value4').AsString;
Records[i][5]:= FDQuery1.FieldByName('value5').AsString;
Records[i][6]:= FDQuery1.FieldByName('value6').AsString;
i:=i+1;
FDQuery1.next;
end;
FDQuery1.Close;
end;
now all of your selected database records are in your local variable
you can go over the row and make new queries with FDQuery1...

Conditional Statement Not Working With Records

How do i display all customers who have purchased an item on a certain day, my code doesn't seem to work, ive tried implementing the code within the displayByDayPurchased procedure. Sorry if this is a simple question, i'm still new to programming.
type
Tday = (monday, tuesday);
Tcustomer = record
itemPurchased:string;
dayPurchased: Tday;
end;
Tcustomers = array of Tcustomer;
function readDay(prompt:string): Tday;
var
selection:Integer;
begin
writeln('1. Monday');
writeln('2. Tuesday');
selection := ReadIntegerRange('Select day purcased (1 - 3): ', 1, 3);
result := Tday(selection-1);
end;
function readCustomers(prompt:string):TCustomers;
var
numOfCustomers:integer;
i:integer;
begin
numOfCustomers := ReadInteger('Enter number of customers: ');
setLength(result, numOfCustomers);
for i := 0 to high(result) do
begin
writeln(i);
result[i].itemPurchased := ReadString('Item Purchased: ');
result[i].dayPurchased := readDay(prompt);
end;
end;
procedure displayByDayPurchased(customers:TCustomers);
var
specific_day:integer;
begin
specific_day := ReadInteger('Enter day to see items purchased');
if (specific_day = customers.dayPurchased[specific_day])then
begin
end;
end;
procedure Main();
var
customer: Tcustomers;
begin
customer := readCustomers('Read in customers');
end;
begin
Main();
end.
my code doesn't seem to work, ive tried implementing the code within the displayByDayPurchased procedure.
Well, in the code you've posted, your displayByDayPurchased doesn't actually implement anything which would result in matching records being displayed, all you have is an empty begin ... end block:
if (specific_day = customers.dayPurchased[specific_day])then
begin
end;
and a) that doesn't correctly specify the match condition anyway and b) it will not compile because a Tcustomers array does not have a dayPurchased field ( a Tcustomer record does, but not the array).
Your code depends of quite a lot of things whose definitions you have not provide (ReadString, ReadInteger, ReadIntegerRange, etc) so it is difficult to give you a tested solution.
However an implementation of your displayByDayPurchased should probably look something like this:
procedure displayByDayPurchased(customers:TCustomers);
var
specific_day:integer;
i : integer;
ADay : Tday;
begin
specific_day := ReadInteger('Enter day to see items purchased');
ADay := Tday(specific_day); // converts integer to TDay value
for i := Low(customers) to High(Customers) do begin
if customers[i].dayPurchased = ADay then begin
writenln(customers[i].itemPurchased);
end;
end;
end;
I assume your Tcustomer record actually includes the customer's name, andx your code needs modifying to handle that.
Btw, your function readDay(prompt:string): Tday is wrong; Because of your definition of Tday, the allowed values in readDay should be 0 and 1, because the lowest value of the Tday enumeration actually corresponds to zero, not 1.
Also, you did not say which Pascal compiler you are using, but most modern versions allow a call like Tday(integerValue) to convert an integer to an instance value of the enumeration.

Lazarus Pascal parameterized query causing segfault! Why?

I've only just started learning Pascal so please excuse my ineptitude if I've missed something glaringly obvious.
I have a program that connects to a DB, retrieves a list of accounts and displays them in a StringGrid. I am now trying to extend the program so that selecting a row in the grid will perform a further search using parameters returned from the grid.
I have the grid setup correctly (I think!), I've managed to write the accountID value into a variable and am using it to construct a query.
When the time comes to run the query a segfault is issued and I don't understand why, the error message is
Project SQLConnect raised exception class 'External: SIGSEGV' at address 583166
The Assembler window shows the following gobbledygook:
00583166 8b09 mov (%ecx),%ecx
Here are the procedures I'm using to return data
Initial return before selecting row, this works - on its own:
procedure TForm1.sendQueryClick(Sender: TObject);
begin
CreateConnection;
CreateTransaction;
try
Query := GetQuery;
Query.SQL.Text := 'SELECT * FROM tbl_accounts LEFT JOIN tbl_properties ON tbl_accounts.ClientID = tbl_properties.PropertyID LEFT JOIN tbl_clients ON tbl_accounts.ClientID = tbl_clients.ClientID ORDER BY tbl_accounts.AccountID DESC';
AConnection.Open;
Query.Open;
while not Query.Eof do
begin
accID := Query.FieldByName('AccountID').AsString;
accountNo := Query.FieldByName('AccountNumber').AsString;
mortgagors := Query.FieldByName('Mortgagors').AsString;
address := Query.FieldByName('Address').AsString;
accResults.RowCount := accResults.RowCount + 1;
accResults.Cells[0, accResults.RowCount - 1] := accID;
accResults.Cells[1, accResults.RowCount - 1] := accountNo;
accResults.Cells[2, accResults.RowCount - 1] := mortgagors;
accResults.Cells[3, accResults.RowCount - 1] := address;
Query.Next;
end;
finally
Query.Close;
AConnection.Close;
Query.Free;
ATransaction.Free;
AConnection.Free;
end;
end;
This is the row select query, which causes the segfault:
procedure TForm1.accResultsSelectCell(Sender: TObject; aCol, aRow: Integer; var CanSelect: Boolean);
begin
// Hide Account ID Column
accResults.ColWidths[0] := 0;
CurrCol := 0;
CurrRow := aRow;
// Grab Account ID value from cell
CellValue := accResults.Cells[CurrCol, CurrRow];
selectedRow.Text := CellValue;
// Setup Query
try
TestQuery := GetQuery;
TestQuery.SQL.Text := 'SELECT * FROM tbl_accounts LEFT JOIN tbl_properties ON tbl_accounts.ClientID = tbl_properties.PropertyID LEFT JOIN tbl_clients ON tbl_accounts.ClientID = tbl_clients.ClientID WHERE AccountID = :AccID';
TestQuery.Params.ParamByName('AccID').AsString := CellValue;
// Open Database connection
AConnection.Open;
TestQuery.Open;
begin
end;
finally
TestQuery.Close;
AConnection.Close;
TestQuery.Free;
ATransaction.Free;
AConnection.Free;
end;
end;
IIRC: Make sure that the type of the parameter is not ftunknown.

Return value of stored functions in MyDAC

I am working with Devart's MyDac and MySQL Server 5.0.41. Here is a section from the documentation on executing stored procedures with TMyConnection.ExecProc:
Note: Stored functions unlike stored procedures return result values that are obtained internally through the RESULT parameter. You will no longer have to provide anonymous value in the Params array to describe the result of the function. The stored function result is obtained from the Params[0] indexed property or with the ParamByName('RESULT') method call.
They also give an example on how to execute a stored function:
aStringVariable1 := TMyConnection.ExecProc('StoredFunctionName',['Param1','Param2']);
aStringVariable2 := TMyConnection.ParamByName('Result').AsString;
By Following these examples, my execution of the stored functions are returning Param1 in the variable aStringVariable2.The execution of the functions in the Query Browser returns the right results. Any pointers on the right way to execute stored functions in MyDAC with TMyConnection or TMyStoredProc will be appreciated.
Thanks in advance.
Here is the code we use to call stored procedures - hope it helps
function TDbControl.DatabaseStoredProc(FConnectionsAddr: integer; SpName: string;var Params: TDAParams): boolean;
var
MyStoredProc: TMyStoredProc;
PramsTxt: String;
Idx, Idx2: Integer;
begin
result := False;
MyStoredProc := nil;
try
try
MyStoredProc := TMyStoredProc.Create(nil);
MyStoredProc.Connection := TMyConnection(FConnectionsAddr);
MyStoredProc.StoredProcName := SpName;
MyStoredProc.ParamCheck := False;
if assigned(Params) then
begin
for Idx := 0 to Params.Count - 1 do
begin
MyStoredProc.ParamByName(Params[Idx].Name).DataType := Params[Idx].DataType;
MyStoredProc.ParamByName(Params[Idx].Name).Value := Params[Idx].Value;
end;
end;
MyStoredProc.Execute;
if assigned(Params) then
begin
for Idx := 0 to Params.Count - 1 do
begin
if (Params[Idx].ParamType = ptOutput ) then
Params[Idx].Value := MyStoredProc.ParamByName(Params[Idx].Name).Value;
end;
end;
result := True;
except
on E: Exception do
begin
PramsTxt := '';
if assigned(Params) then
begin
for Idx2 := 0 to Params.Count - 1 do
begin
PramsTxt := PramsTxt + Params.Items[Idx2].Name + '=' + Params[Idx2].AsString + ',';
end;
end;
LogText(FConnectionsAddr, 'DatabaseStoredProc Err:' + E.Message + ' SpName:' + SpName + ' Prams:' + PramsTxt);
raise ;
end;
end;
finally
FreeAndNil(MyStoredProc);
end;
end;