Reference-Only and Bind Buglist?

Posted by Tim Kuehn on 23-Apr-2018 18:24

I've found myself using BIND and REFERENCE-ONLY temp-tables, and .... I'm concerned. 

  1. I can't use them as buffer parameters which means I have to scope them to the procedure 
  2. Passing a ROWID to an IP with a DEFINE BUFFER for the TT fails to FIND the target record. 
  3. I've run into cases where FIND fails but CAN-FIND() works for the exact same WHERE and no funky pending db writes to be seen 
  4. BUFFER tt:HANDLE:BUFFER-COPY(hSrc) fails but tt.fieldName = hSrc:FieldName works. 
  5. etc. 

Is there an outstanding buglist for reference-only / BIND? I'd prefer not to find them the hard way. 

All Replies

Posted by dbeavon on 24-Apr-2018 07:48

I use REFERENCE-ONLY, BY-REFERENCE, BIND all the time.  These allow logic classes to have shared data without keeping their own internal instances of the data in memory (or overflowing them to temporary disk).  The only weird thing where classes are concerned is that you may only define/declare static TT/DS at the *class* scope.  This means you either need separate instances ({1}, {2}, {3}) of REFERENCE-ONLY datatypes that play select roles within your class or, alternatively, you need to fork a new instances of the same logic class to use the same data in a slightly different way

There was a bug in 11.6 where, if you used the same datasets and temp-tables in two procedures, you needed to make sure they were defined in the same sequence in both procedures (ie. the includes for these weren't ever rearranged).  But that was fixed in 11.6.3.x at some point.  That's the only true bug that comes to mind.  Beyond that it is a matter of trying to interpret the awful Progress messages you get at runtime if you try to re-assign a REFERENCE-ONLY static TT/DS that has previously been assigned to a (different) underlying instance of the TT/DS.

The whole ROWID thing from Progress is something to generally avoid, or use on an extremely short-term basis.

Even when working with database tables, they say to not persist any ROWID references for future use.  I suspect you need to be even more cautious with ROWID's for TT/DS data - since the data is potentially just as transient as the ROWID's.  What I typically do is use my own surrogate-integer keys as much as possible, preferably something right out of the database, or if nothing is available there, then I introduce something new into my TT's themselves.  The custom surrogate keys can be passed along with a TT to identify one row among many.

It is troubling that an ABL programmer with many years of experience can still have trouble with the way the Progress ABL manipulates temporary data tables.  I think a huge problem with Progress ABL is the language syntax surrounding static TT/DS data.  It is *truly* strange how these things are passed as parameters, and the references are transferred from one program to another.  And there is no notion of separation between "classes" of data and *instances* of them.  IE. creating a new instance of the *same* static TT/DS is not actually possible.  You basically redefine the whole thing with a different name (possibly with the help of an include file).  And then by passing a TT named one thing into a parameter accepting a diferent TT name, you rely on the runtime to do tons of on-the-fly schema comparisons between them.  It is clear that Progress developed this technology incrementally without a coherent master plan.  FYI, One alternative to static data constructs is DYNAMIC data (handles), but the language syntax for that is worse yet.   I wonder if its too late to go back to the drawing board?

Posted by Peter Judge on 24-Apr-2018 08:16

> The whole ROWID thing from Progress is something to generally avoid, or use on an extremely short-term basis.

Agree wholeheartedly on this. One place where I know that TT ROWID values change is when data is passed across an AppServer boundary. You should only ever be using ROWID to do local, in-same-session-only lookups or for db lookups and even there I’d suggest looking for alternatives -  table partitioning means that the DB ROWID can change.

 

Posted by jankeir on 24-Apr-2018 08:24
Posted by Lars Neumeier on 24-Apr-2018 08:31

[quote user="Peter Judge"]

> The whole ROWID thing from Progress is something to generally avoid, or use on an extremely short-term basis.

Agree wholeheartedly on this. One place where I know that TT ROWID values change is when data is passed across an AppServer boundary. You should only ever be using ROWID to do local, in-same-session-only lookups or for db lookups and even there I’d suggest looking for alternatives -  table partitioning means that the DB ROWID can change.

 
 

[/quote]

The help for the ROWID function states:

•If you want a called procedure to use the same record as a calling procedure, use the ROWID function to ensure that you are retrieving the same record. Use a SHARED ROWID variable or procedure parameter to communicate the ROWID of a record from one procedure to another. The second procedure can then find the same record. This is an alternative to using shared buffers or buffer parameters.

Your post and this help entry is a little bit confusing.

Posted by Tim Kuehn on 24-Apr-2018 08:46

In my case I was trying to da a DEFINE BUFFER and find-by-rowid in order to get around the inability to pass a REFERENCE-ONLY TT as a buffer parameter. That it didn't work *in the same procedure file* is concerning, particularly since I had to scope the buffer to the whole procedure in order to get the code to work.

(This is on 10.2B - we're supposed to be @ 11.7 in the near future)

Posted by dbeavon on 24-Apr-2018 08:57

If you want to use ROWID, you should think of it as a piece of data that isn't in your control.  ROWID is an implementation of the underlying storage that Progress is free to change at any time.  (They've changed it before when they deprecated RECID).  You must consider whether or not you want to rely on a specific implementation of that ROWID. Tim's original question mentioned that relying on ROWID was a problem, and there are a number of possible reasons for that (which are the fault of ROWID and would not be a problem if a custom surrogate ID was used instead).

Practically speaking, ROWID's are transient.  If I have a business document with a bunch of line-items, I may implement an update/save operation  by removing the old line items and recreating them (even if only one or two lines were modified).  If I implemented the save operation in that way, all my previous line-item ROWID's would be invalidated, despite the fact that most of those items remained unchanged from a business perspective.  

Posted by Lieven De Foor on 24-Apr-2018 09:00

Tim, a workaround for your first remark is to pass the handle of the buffer instead. It's not pretty, but it works...

And as Peter also said, passing temp-tables between AppServer and client causes the records to get new rowids (which makes perfect sense as the rowid is some sort of physical address), so be careful!

Posted by Tim Kuehn on 24-Apr-2018 09:09

I wanted static TT references in the IP not dynamic ones - while I could pass a buffer handle I'd still need to do a FIND to get the buffer in scope in the procedure, and if FIND .... WHERE ROWID() = some value doesn't work...the next logical question is "why?" Maybe it's because it's an old version of the ABL....

As for concerns about passing by-value across programs and appservers - I'm not doing that in this scenario, I'm just using it within a single program file.

Posted by dbeavon on 24-Apr-2018 09:20

Did you explicitly use the "BY-REFERENCE" option on the TT parameter?  (I don't use internal procs but if they are anything like class methods, you should still ask for "BY-REFERENCE" parameter handling).

Sometimes it is hard to tell when OE is making a new instance of the same data.  I think the trick I normally use is to create a million rows and see if there is an increase in the overhead when crossing the boundary between the caller and callee.  (There will be a performance impact if new copies of temp data are created).  As I recall there may also be some a VST that tell you details about the static TT's in your session, and you can see if the number of TT's increases at different points on your callstack.  A new reference to a temp-table shouldn't change the numbers in the VST.

Posted by dbeavon on 24-Apr-2018 09:22

There is also ABL tracing for temp tables that would tell you exactly what is happening.  But it looks like that is a feature introduced in OE 11.

knowledgebase.progress.com/.../Is-there-a-LOG-ENTRY-TYPE-to-log-TEMP-TABLE-statistics

Posted by Tim Kuehn on 24-Apr-2018 09:26

I was in a .p file and wanted to call an IP in the .p like so:

run SomeIPName(BUFFER tt_name).

Normally I'd have a

DEFINE PARAMETER BUFFER tt_name FOR tt_name.

and I'd be good to go. But not in this case where tt_name was a REFERENCE-ONLY tt because they can't be parameters.

In this case I passed a ROWID to SomeIPName and then did this:

run SomeIPName(rowid(tt_name)).

PROCEDURE SomeIPName:

define input parameter rowid_parameter_name as rowid no-undo.

DEFINE BUFFER tt_name FOR tt_name.

FIND tt_name WHERE ROWID(tt_name) = rowid_parameter_name.

and it didn't work..

Posted by Laura Stern on 24-Apr-2018 09:36

Can you please show how the TT is defined in both the caller and the callee.  I'm guessing that you are not looking at the same table (in caller and callee) and that is why the ROWID is not matching up.

Posted by Tim Kuehn on 24-Apr-2018 09:48

It's in the same program file - I'm not passing anything across programs files, so the TT definition on both sides of the call are the same since they're "supposed" to refer to the same TT instance.

Posted by Laura Stern on 24-Apr-2018 10:54

I'm trying to understand where the REFERENCE-ONLY and/or BIND are being used.  

Posted by Tim Kuehn on 24-Apr-2018 11:14

The TT is defined REFERENCE-ONLY.

The input table parameter has a BIND attribute.

When the procedure is called it uses the same TT definition with a BIND attribute.

Using REFERENCE-ONLY TTs in a BUFFER parameter is forbidden so I was trying to pass a ROWID and then find the TT record using the ROWID in order to accomplish the same thing.

The IP had a DEFINE BUFFER for the TT.

FIND ... WHERE ROWID() failed, but a CAN-FIND(table where ROWID....) worked.

My guess is that my 10.2B system was using the TT definition created for the FIND when it was compiled while CAN-FIND was using the TT reference that was BIND'd in.

(This is why I *much* prefer BY-REFERENCE calls and avoiding all this stuff... :) )

Posted by Lieven De Foor on 25-Apr-2018 08:13

Could the temp-table be passed by value somewhere? That would give the copy other rowids than the source, which would explain why the records aren't found...

You could also use the primary key instead of the rowid to find the record again. The performance hit should be minimal...

Posted by Laura Stern on 25-Apr-2018 08:27

Regardless of the rest, FIND and CAN-FIND should give you the same answer.  Please log a bug.  

This thread is closed