Resolved Infinit Loop With For Each

JoseKreif

Member
I have a for each loop with a temp-table that just won't end. Using an incremented number on each of the records, I can see that the for each loop is starting over after hitting the last record.

Q: What are some things that would cause this?

I'll note the for each is wrapped inside of a repeat loop. The outer loop deals with input streams, and should end as soon as an EOF is reached.
Nothing below the for each loop is ever executed, and nothing before it is ever executed again, so I don't think these outer loop is the culprit.
 

TomBascom

Curmudgeon
FOR EACH does not gather a "result set" and process it as a whole unit. It simply loops maintaining a cursor and following the provided criteria and evaluating that criteria with every iteration.

Similar to what this will do:
Code:
j = 10.
do i = 1 to j:
  j = j + 1.
end.

When the code in the body changes a record so that it appears later it will be processed repeatedly. Usually this is a problem with fields in the index that the FOR EACH is using.
 

TomBascom

Curmudgeon
To process a "result set" you might do something like this:
Code:
define temp-table tt_test
  field id  as integer
  field xid as integer
  index id-idx is primary unique id
.

create tt_test.
assign
  id  = 1
  xid = id
.

create tt_test.
assign
  id  = 2
  xid = id
.

create tt_test.
assign
  id  = 3
  xid = id
.

create tt_test.
assign
  id  = 4
  xid = id
.

create tt_test.
assign
  id  = 5
  xid = id
.

repeat preselect each tt_test:

  find next tt_test no-error.
  if not available tt_test then leave.

  id = id + 10.
  display id xid.
  pause.

end.
 

JoseKreif

Member
Thanks for your response, however, it does seem irrelevant to what is happening in my case. I know it doesn't gather a "result set", and I am not trying to do so. I am sorry if I caused confusion.

I am processing records imported from a CSV. After importing CSV to a temp-table, I am running through the temp-table and updating data in our database based on data in the temp-table. Something is causing the for each to restart after reaching the end.
 

JoseKreif

Member
Here is a short example:

Code:
<gather files >
lslp:
repeat on endkey undo, leave lslp:
<gather file from collected list>
<check if file is correct format>

   repeat on endkey undo, leave on error undo, retry:
           create csvimport.
           import stream instr delimiter "," csvimport no-error.
   end.

   for each csvimport: /* This will repeat for eternity. After hitting last record, it starts over */
     <table>.<field> = csvimport.<field>
   end.
end.


If I try this before the other for each, it ends as expected.
Code:
   for each csvimport: /* This will not repeat.  */
     display csvimport.
   end.
 
Last edited:

TomBascom

Curmudgeon
As written your FOR EACH ends and then gets to the bottom of a REPEAT loop. There is no exit from the REPEAT so it is going to iterate again.

I don't know what all of the "<gather..." and "<check..." code does so I cannot say if it executes or throws an error or whatever but, from your description it seems likely that the iteration of the REPEAT is somehow coming around to the FOR EACH again.

Just guessing but somewhere there should be an INPUT FROM. If the corresponding INPUT CLOSE is missing, or being skipped, that might be part of the issue.

If you feel that I am wrong about that you might add some debugging code to help me understand the flow:

Code:
define variable ii as integer no-undo.
define variable jj as integer no-undo.
define variable kk as integer no-undo.

<gather files >

lslp:
repeat on endkey undo, leave lslp:  /* this is the wrong place for "on endkey undo, leave lslp" */

   <gather file from collected list>
   <check if file is correct format>

   message "reading CSV: " fileName.

   repeat on endkey undo, leave on error undo, retry:
           create csvimport.
           import stream instr delimiter "," csvimport no-error.   /* the endkey is going to happen here -- but you do not want to be leaving lslp now */
   end.

   message "copying TT to DB".
   ii = ii + 1.

   for each csvimport: /* This will repeat for eternity. After hitting last record, it starts over */

     jj = jj + 1.
     message ii jj kk <table>.<field>.

     <table>.<field> = csvimport.<field>

   end.

   message "done copying to db".
   kk = kk + 1.

   /* there should be a "LEAVE lslp" somewhere in this neighborhood... */

end.
 

JoseKreif

Member
Why do you say "restart after reaching the end"?

Perhaps you should show your code.

Because it by dictionary definition, restarts. Imagine having 2 cords. 1: A, and 2: B. Displaying these in the loop would result in.
Code:
[START]
A
B
A
B
A
B
A
B
....
[NEVER ENDS. ETERNITY]

when it should be
Code:
[START]
A
B
[END]

I have managed to isolate the problem. It's related to the index on the temp-table. I'll share the code (simplified). I have only been using progress for a few years, so I am not sure why this is. If you know why, I'll be happy to learn.

Code:
def var xx as int no-undo.

def temp-table myTable
field myField  as char
field myField2 as int
field myField3 as char
    index poidx is primary
         myField myField2.


create myTable.
assign
  myField = "Hello".
  myField2 = 0.

create myTable.
assign
  myField = "World"
  myField2 = 0.


for each myTable: /** This is infinite **/
  myField2 = xx.
  xx = xx + 1.
  display myField.
end.

The example above gets stuck on a single record, where as my real program will cycle through all of them. It's an infinite for each loop nonetheless.
 
Last edited:

TomBascom

Curmudgeon
That's the "for each is not dealing with a result set" problem that I first suggested.

But if that is your issue then your other sample code was not accurately reflecting your real code.
 

TomBascom

Curmudgeon
Because it by dictionary definition, restarts.

This is subtle but in the example immediately above it is not restarting. It is never reaching the end because the end keeps "moving". When you assign a new value to the key the record becomes available to the query at a position that has not been reached yet. Every iteration of the FOR EACH re-evaluates what record is "next" relative to the record that it just finished using the criteria specified in the WHERE clause. Even if that happens to be the same record. Or a record that was previously processed. There is no hidden "I've already processed this record" field.

Aside from my previously mentioned PRESELECT code another way to avoid this is to add a logical field called "processed" (or something similar), add "WHERE processed = no" to your criteria and set it to YES as you move through your data.
 

JoseKreif

Member
Thanks. Since I added a incremented number to each of my 99 imported records, and displayed this number in the loop, I was seeing it cycling from 1 to 99, and then hit back to 1, and repeat.

Since I was seeing the same 99 records repeat over and over in the same order (1 to 99), it appeared to me as it the for each was starting over apon hitting the final record.

Anyhow, I have a handle on the culprit and am fixing it.
 
Top