Home page Home page Home page Home page
Pixel Header R1 C1 Pixel
Pixel Header R2 C1 Pixel
Pixel Header R3 C1 Pixel
By APK | Monday 26 August 2013 16:07 | 0 Comments
We were testing an import routine the other day. It was nothing out of the ordinary, a simple tab delimited file which was being imported into a new OI table. The only tricky part (where tricky is a very loose term) is that the OI table required a multi-part key. The OI key was simply the first two columns of the imported data, and the customer wanted those fields stored in the data as well. Actually, calling it tricky is a bit dramatic. Let's say it was about as simple an import as you could want.

We test for a reason, and the initial run through of the code managed to import an empty key. Normally when this happens, a null key is created. However, because we used a multi-part key, a key of "*" was created.

Unlike working with null keys in the system tools, "*" has a length greater than 0, and could easily be examined by the system editor, which we did to ensure that the row was indeed empty. We then proceeded to use the File -> Delete menu option (or Alt-D, which was the actual keystroke hit) to remove the offending record. We then went on to check other records in the table to verify the import processes. Imagine our surprise that the entire file (about 6500 rows) was now empty.

We can only surmise the the code behind the system editor does not do something like

open fileName to handle then
   delete handle, id else
      * // Manage errors

but instead must call

call delete_row( fileName, id, FALSE$ )

which in our case resolved to

call delete_row( "TEST_FILE", "*", FALSE$ )

and proceeded to delete all the rows in the table.

Labels: , , , ,

By APK | Tuesday 13 August 2013 15:06 | 2 Comments
In a previous posting, we talked about passing by reference versus passing by value. In that article, we mentioned we'd explain how this could adversely impact your programs.

At a client site, we installed a custom index update program. This function allows the customer to run multiple indexers at a time, with each indexer updating only a subset of the indexes on each machine. Additionally, the UI displays statistics about the indexes, the time it takes to perform each update and other useful information. (If you're interested in said product, then please, by all means, let us know.)

We recently discovered that some indexes were never updating, and a check showed that the test lock we placed on the index was never being released. A quick check of the code showed that this was theoretically impossible, and a detailed check validated that assumption.

However, the theory was quite wrong since the lock was never being released. A test run through a number of indexes, checking variables after every line of code, examining the results of status(), get_status(), @FILE.ERROR and even get_EventStatus showed no errors and nothing we did not expect to occur. Finally, after close to a dozen passes, we had results. The code is basically

call set_status( FALSE$ )
if locked then
   call index.flush( fileName, indexField )
   if get_status( errorText ) then
      gosub processError
end else
   * // ZZIDX010: Table Name %1%, IndexName %2% is currently locked.  |
   * // Index update skipped
   logText =  zzx_res2str( ZZ_INDEXER_RESKEY$, "ZZIDX010", | 
               fileName : @FM : indexField, |
               TRUE$ )
   gosub updateLog
if locked then
   unlock hFileName, indexField : "*" : ZZ_INDEX_TOKEN$ else
      tableId = fileName
      rowId = indexField
      gosub setFsError
      gosub processError

One one particular index FOO, indexField was changed to BAR. As the code continued, it attempted to unlock the BAR index token, leaving the FOO index token locked. At last, we had an answer to why some locks were not being released. It did, however, open a whole new set of questions. One was why why was the variable being changed. The other was what is the relationship between FOO and BAR.

After carefully examining the index definition records, it was clear that FOO was not only a BTREE index, but was also the source of a relational index on a secondary table with BAR being the target field. Apparantly, somewhere in the index.flush code, the system is swapping out indexField from the source field to the target field, a clear case of your source variables being affected by passing by reference.

We changed the index.flush call to

call index.flush( fileName, (indexField : "") )

and the lock was released as expected.

Labels: , , , , ,

Pixel Footer R1 C1 Pixel