Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Pointer to dynamic array and vice versa


This question is answered. Helpful answers available: 1. Correct answers available: 1.


Permlink Replies: 18 - Last Post: Apr 6, 2018 10:23 AM Last Post By: Mark Williams
Mark Williams

Posts: 120
Registered: 5/8/10
Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 3, 2018 6:15 AM
The FireDAC getData and SetData functions return and set blob data as a pointer.

I am trying to save a dynamic array of integer "TIntegerArray = array of Integer" as blob data using these functions.

The functions are defined as follows:

 
GetData(AColumn: Integer; AVersion: TFDDatsRowVersion; ABuff: Pointer; ABuffLen: Cardinal; ADataLen: Cardinal; AByVal: boolean) 
SetData(AColumn: Integer; ABuff: Pointer; ADataLen: Cardinal) 


There does not appear to be any documentation relating to these methods. With the GetData function the ABuffLen parameter always returns 4, which I believe is the size of TIntegerArray. The ADataLen parameter always returns 0.

I believe ABuff is returning a pointer to the array. However, I cannot figure out how to get from ABuff back to the original TIntegerArray.

I have not yet tried the setData method. I am currently saving the array to the database using a TBlobField and createBlobStream. This works fine, however, I cannot use this elsewhere in my code. So I will need to work out how to pass in my integer array to the ABuff parameter as a pointer. Is it as simple as Pointer(MyIntArray)? Do I need to pass in a value for ADataLen and, if so, what?
Peter Below

Posts: 1,227
Registered: 12/16/99
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 3, 2018 9:37 AM   in response to: Mark Williams in response to: Mark Williams
Mark Williams wrote:

The FireDAC getData and SetData functions return and set blob data as
a pointer.

I am trying to save a dynamic array of integer "TIntegerArray = array
of Integer" as blob data using these functions.

The functions are defined as follows:

 
GetData(AColumn: Integer; AVersion: TFDDatsRowVersion; ABuff:
Pointer; ABuffLen: Cardinal; ADataLen: Cardinal; AByVal: boolean)
SetData(AColumn: Integer; ABuff: Pointer; ADataLen: Cardinal) 


There does not appear to be any documentation relating to these
methods.

You should not be using them anyway.

With the GetData function the ABuffLen parameter always
returns 4, which I believe is the size of TIntegerArray. The ADataLen
parameter always returns 0.

I believe ABuff is returning a pointer to the array. However, I
cannot figure out how to get from ABuff back to the original
TIntegerArray.

I have not yet tried the setData method. I am currently saving the
array to the database using a TBlobField and createBlobStream. This
works fine, however, I cannot use this elsewhere in my code.

Why not, pray? Storing a TIntegerArray to a stream and getting one back
from a stream is a piece of cake and the correct way to store binary
data to a BLOB database field and getting it back. This way of doing
stuff is completely independent of the database framework you use.

If you really are bent on getting close to the metal the source code
for FireDac is your friend.

--
Peter Below
TeamB
Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 4, 2018 3:50 AM   in response to: Peter Below in response to: Peter Below
Peter Below wrote:


Why not, pray? Storing a TIntegerArray to a stream and getting one back
from a stream is a piece of cake and the correct way to store binary
data to a BLOB database field and getting it back. This way of doing
stuff is completely independent of the database framework you use.

I use TBlobfield and TBlobStream for this purpose, but to get to TBlobField (as far as I can work out with FireDAC) I have to access change the dataset's RecNo to , which I don't always wish to do. That being so, I don't seem to have any other choice than the GetData/SetData. I would be very pleased to be wrong!

If you really are bent on getting close to the metal the source code
for FireDac is your friend.

I have the Professional version of Delphi. It gives you only limited access to the FireDAC source code and does not include any of the source code for these functions. Also, there does not appear to be any documentation relating to TFDDATSROW.

--
Peter Below
TeamB
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 4, 2018 9:52 AM   in response to: Mark Williams in response to: Mark Williams
Mark Williams wrote:

I use TBlobfield and TBlobStream for this purpose

Then what is the problem? TBlobStream has Read...() and Write...()
methods, several of which take a buffer Pointer and buffer Length as
input parameters.

--
Remy Lebeau (TeamB)
Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 4, 2018 11:28 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Mark Williams wrote:

I use TBlobfield and TBlobStream for this purpose

Then what is the problem? TBlobStream has Read...() and Write...()
methods, several of which take a buffer Pointer and buffer Length as
input parameters.

--
Remy Lebeau (TeamB)

As I mention in the response to Peter, to use TBlobstream I have to be able to access TBlobField, to do which I have to move the cursor to the current record in the dataset. On some occasions I want to avoid doing this whilst reading the blob data from another record. The only way I can see of doing this is via TFDATSROW type which only allows retrieval and setting of data via GetData and SetData functions as mentioned in my original post and with which I am having problems.

For the record, I have discovered that the TBlobField type also has LoadFromStream and SaveToStream functions which are simpler to use than TBlobStream.
Jeff Overcash (...

Posts: 1,529
Registered: 9/23/99
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 4, 2018 10:51 PM   in response to: Mark Williams in response to: Mark Williams
On 4/4/2018 2:28 PM, Mark Williams wrote:
Remy Lebeau (TeamB) wrote:
Mark Williams wrote:

I use TBlobfield and TBlobStream for this purpose

Then what is the problem? TBlobStream has Read...() and Write...()
methods, several of which take a buffer Pointer and buffer Length as
input parameters.

--
Remy Lebeau (TeamB)

As I mention in the response to Peter, to use TBlobstream I have to be able to access TBlobField, to do which I have to move the cursor to the current record in the dataset. On some occasions I want to avoid doing this whilst reading the blob data from another record. The only way I can see of doing this is via TFDATSROW type which only allows retrieval and setting of data via GetData and SetData functions as mentioned in my original post and with which I am having problems.

For the record, I have discovered that the TBlobField type also has LoadFromStream and SaveToStream functions which are simpler to use than TBlobStream.

I'd use CloneCursor so you have 2 cursors into the same Dataset. You would move
the second cursor to the location and work like you normally would.

--
Jeff Overcash (TeamB)
(Please do not email me directly unless asked. Thank You)
Learning is finding out what you already know. Doing is demonstrating that you
know it. Teaching is reminding others that they know it as well as you. We are
all leaners, doers, teachers. (R Bach)
Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 2:11 AM   in response to: Jeff Overcash (... in response to: Jeff Overcash (...
Jeff Overcash (TeamB) wrote:

I'd use CloneCursor so you have 2 cursors into the same Dataset. You would move
the second cursor to the location and work like you normally would.

I'm unfamiliar with CloneCursor, but it sounds like it might be useful. I'll take a look. Thanks.

Out of interest, do you have any idea what is wrong with the code I posted in terms of extracting a dynamic array from the pointer returned by GetData? The code is in another branch of this thread.
Jeff Overcash (...

Posts: 1,529
Registered: 9/23/99
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 7:50 PM   in response to: Mark Williams in response to: Mark Williams
On 4/5/2018 5:11 AM, Mark Williams wrote:
Jeff Overcash (TeamB) wrote:

I'd use CloneCursor so you have 2 cursors into the same Dataset. You would move
the second cursor to the location and work like you normally would.

I'm unfamiliar with CloneCursor, but it sounds like it might be useful. I'll take a look. Thanks.

Out of interest, do you have any idea what is wrong with the code I posted in terms of extracting a dynamic array from the pointer returned by GetData? The code is in another branch of this thread.

Very simple actually.

Just drop a FDMemTable with your normal query and you clone it like

procedure TForm6.btn1Click(Sender: TObject);
begin
mem1.CloneCursor(qry1);
mem1.Locate('emp_no', VarArrayOf([136]), []);
end;

mem1 is the FDMemTable, qry1 is the FDQuery I want to clone. Calling locate on
the memtable which is just sharing the data of the qry does not move the cursor
of the qry1, but you can pull and edit the data in the query at the other cursor
location.

I use this technique quit often with ClientDatasets, but FireDAC also has it. I
do not use FireDac so do not know why the Get and Set data are having issues for
you.

--
Jeff Overcash (TeamB)
(Please do not email me directly unless asked. Thank You)
Learning is finding out what you already know. Doing is demonstrating that you
know it. Teaching is reminding others that they know it as well as you. We are
all leaners, doers, teachers. (R Bach)
Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 3:29 AM   in response to: Jeff Overcash (... in response to: Jeff Overcash (...
Jeff Overcash (TeamB) wrote:

I'd use CloneCursor so you have 2 cursors into the same Dataset. You would move
the second cursor to the location and work like you normally would.

--

I've just done a little research on CloneCursor in Cary Jensen's "Delphi in Depth: FireDAC". He says: " ... which means it can be called by any FDDataSet, not just FDMemTables. For the most part, however, you will not call CloneCursor with any FDDataSet other than FDMemTables. Trying to do so often leads to access violations and other issues. When I asked the author of FireDAC about these issues, he explained that CloneCursor was intended for use only from FDMemTable".

The documentation on CloneCursor does not mention that it is designed for use only with FDMemTable, bit I note the example provided with the documentation is in relation to FDMemTable.

Edited by: Mark Williams on Apr 5, 2018 3:30 AM
Roy Lambert

Posts: 40
Registered: 10/21/99
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 5:12 AM   in response to: Mark Williams in response to: Mark Williams
Mark

Don;t use FireDAC but is there any reason why you can't just open a second cursor into that table?

Roy Lambert

Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 5:18 AM   in response to: Roy Lambert in response to: Roy Lambert
Roy Lambert wrote:
Mark

Don;t use FireDAC but is there any reason why you can't just open a second cursor into that table?


I'm kind of a long way down the road with FireDAC to abandon it. How do you open a second cursor without using cloneCursor?
Roy Lambert

Posts: 40
Registered: 10/21/99
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 6:01 AM   in response to: Mark Williams in response to: Mark Williams
Mark

Don;t use FireDAC but is there any reason why you can't just open a second cursor into that table?

I'm kind of a long way down the road with FireDAC to abandon it. How do you open a second cursor without using cloneCursor?

As I said I don't use FireDAC so I have no idea. In the systems I've created another table component or query and used that. You'll need to explicitly move to the row you want using the key from the "master" table.

I've just had a look at the help here

http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Browsing_Tables_(FireDAC)

and from it I'd suggest

FDTable1.TableName := 'CUSTOMERS';
FDTable1.IndexFieldNames := 'CustNo';
FDTable1.Open;
and

FDTable2.TableName := 'CUSTOMERS';
FDTable2.IndexFieldNames := 'CustNo';
FDTable2.Open;

Roy

Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 7:01 AM   in response to: Roy Lambert in response to: Roy Lambert
Roy Lambert wrote:

As I said I don't use FireDAC so I have no idea. In the systems I've created another table component or query and used that. You'll need to explicitly move to the row you want using the key from the "master" table.
I misunderstood. I thought you were advising against the user of FireDAC!

The dataset involved can be rather large. Opening it twice may be a bit overkill. But thanks for the suggestion. The CloneCursor procedure gives you a second cursor without the overhead, but it seems not to be advisable for my specific requirements.
Roy Lambert

Posts: 40
Registered: 10/21/99
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 7:30 AM   in response to: Mark Williams in response to: Mark Williams
Mark

Is it a table or a query you're using? I just googled FireDAC CloneCursor and the wiki entry I found doesn't make a great deal of sense to me.

Roy Lambert

Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 7:58 AM   in response to: Roy Lambert in response to: Roy Lambert
Roy Lambert wrote:
Mark

Is it a table or a query you're using? I just googled FireDAC CloneCursor and the wiki entry I found doesn't make a great deal of sense to me.

It's a TFDQuery. There is nothing in the documentation to suggest that CloneCursor can't be used with a TFDQuery component, but a recent book I have on the subject suggests otherwise (see above).
Roy Lambert

Posts: 40
Registered: 10/21/99
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 5, 2018 11:57 PM   in response to: Mark Williams in response to: Mark Williams
Mark

Is it a table or a query you're using? I just googled FireDAC CloneCursor and the wiki entry I found doesn't make a great deal of sense to me.
It's a TFDQuery. There is nothing in the documentation to suggest that CloneCursor can't be used with a TFDQuery component, but a recent book I have on the subject suggests otherwise (see above).

OK - opening a cursor onto a table and opening a query are generally different. The table "should" just grab a handle(s) to the file(s) a query generally has to read the data according to its WHERE clause. That would explain the amount of time table to open the TFDQuery.

What you need then is another query with the WHERE clause just referencing the record you want to update. Again I don't know how FireDAC operates but in my database system (ElevateDB) you'd need to make sure the query is sensitive - ie you can alter it.

Roy
Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 6, 2018 10:23 AM   in response to: Roy Lambert in response to: Roy Lambert
Roy Lambert wrote:
Mark

Is it a table or a query you're using? I just googled FireDAC CloneCursor and the wiki entry I found doesn't make a great deal of sense to me.
It's a TFDQuery. There is nothing in the documentation to suggest that CloneCursor can't be used with a TFDQuery component, but a recent book I have on the subject suggests otherwise (see above).

OK - opening a cursor onto a table and opening a query are generally different. The table "should" just grab a handle(s) to the file(s) a query generally has to read the data according to its WHERE clause. That would explain the amount of time table to open the TFDQuery.

What you need then is another query with the WHERE clause just referencing the record you want to update. Again I don't know how FireDAC operates but in my database system (ElevateDB) you'd need to make sure the query is sensitive - ie you can alter it.

Roy

Thanks for the suggestion. That's certainly one way to go. There is a way of getting to it behind the scenes in FireDAC, however, I am having problems working with the raw data returned (see the sub-thread in this thread). I have reposted the problem on the FireDAC group and hopefully I can get an answer that way. If not, I will probably implement your suggestion. Thanks again.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Pointer to dynamic array and vice versa
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 3, 2018 9:55 AM   in response to: Mark Williams in response to: Mark Williams
Mark Williams wrote:

The FireDAC getData and SetData functions return and set blob data as
a pointer.

RAW data, yes.

I am trying to save a dynamic array of integer "TIntegerArray = array
of Integer" as blob data using these functions.

Can you please show your actual code?

With the GetData function the ABuffLen parameter always returns 4,
which I believe is the size of TIntegerArray. The ADataLen
parameter always returns 0.

How can it return anything? The declaration you have shown for
GetData() has no output parameters or return value.

In any case, I suspect (since you didn't show your actual code) that
you are simply writing the array data pointer itself and not the
integer data that the pointer is pointing at, so you are only getting a
pointer back and not the actual integer data.

I believe ABuff is returning a pointer to the array. However, I
cannot figure out how to get from ABuff back to the original
TIntegerArray.

Try something like this (untested):

var
  MyIntArray: TIntegerArray;
 
...
 
SetData(Column, Pointer(MyIntArray), SizeOf(Integer) *
Length(MyIntArray));
 
...
 
SetLength(MyIntArray, BlobSize div SizeOf(Integer));
GetData(Column, Version, Pointer(MyIntArray), SizeOf(Integer) *
Length(MyIntArray), ?, true);


I have no idea what the ADataLen parameter is meant for.

I am currently saving the array to the database using a TBlobField and
createBlobStream.

Again, please show your actual code.

So I will need to work out how to pass in my integer array to the
ABuff parameter as a pointer. Is it as simple as Pointer(MyIntArray)?

Yes.

Do I need to pass in a value for ADataLen and, if so, what?

Yes, of course, otherwise how do you expect it to know how many bytes
the array can hold?

--
Remy Lebeau (TeamB)
Mark Williams

Posts: 120
Registered: 5/8/10
Re: Pointer to dynamic array and vice versa  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 4, 2018 4:09 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

Many thanks for the help.


Can you please show your actual code?
I didn't really have any code at the time of posting. My knowledge of pointers is limited (very) and I was getting nowhere fast. Your suggested code has helped me to take things further.

I have array data stored in a test table. I can save to and retrieve successfully from this using TBlobField and streams.

I have tried to retrieve the array using GetData:

  with FDQuery1 do begin
          setLength(MyIntArray, Integer(Table.Rows[table.Rows.Count-1].GetData(2)));  //this is the database column which stores the number of items in the array         
          Len2 := Length(MyIntArray) * sizeOf(Integer);
          if Table.Rows[table.Rows.Count-1].GetData(1, rvDefault, Pointer(MyIntArray), Len, Len2, true) then begin
             for i := 0 to  High(MyIntArray) do
                s := s + MyIntArray[i].ToString + ', ';
          Showmessage(s);          


It seems you are correct that AByVal needs to be set to true. As for the ABuffLen and ADataLen params I am still unsure what they do. However, I have tried setting ABuffLen to the value of Len2. I have also tried setting it to SizeOf(Integer) with ADataLen set to the length of the array and so on with every possible combination I can think of! Neither really seem to affect the outcome, but that may be because of the way I am trying to handle the array after. The result of my showmessage is always something like "122060280, 0, 0, 0, 0,..." (with 0s for the remaining items).

Can you see anything obviously wrong in the above code?

Many thanks
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02