Sometimes it doesn't matter how esoteric your knowledge of the
internals of the product are - the only way to get to the bottom of an
obscure mystery can just be Elbow
Grease.
Recently we were
contacted by a client with a predicament. He was (finally) converting
his client's Rev G system to OpenInsight and he'd run into a snag. Every
time he quick painted a form to deal with a specific table, the form
was created with no problem. But the moment he reopened the form and
tried to look at the properties of specific controls, the controls were
no longer data-bound. And to add insult to injury, the columns they had
been associated with no longer appeared in the dropdown of selectable
columns ; they were still in the dictionary, but not selectable from
within the form designer dialog.
Our first thought was
that the column names might have retained their Rev G periods. But that
wasn't the case. "OK", we queried, "are they in %FIELDS%?". Predictably
they were. And anticipating this question the client had already forced a
rebuild by clearing the dictionary and copying everything back in.
"Can
you check the SYSREPOSWINEXE? Are the columns showing there?". At this
point the client indicated that the rows seemed to be corrupted and that
the columns weren't showing there. So we suggested REPOSITORY DELETE
the EXE and recompiling.
Still no joy.
"OK
- check SYSREPOSWINS, SYSREPOSWINEXES and SYSOBJ for GFEs". None. So we
decided to try and establish the root cause. Essentially if the design
process is removing the column from the control, it's an indicator that
the designer hasn't found the column in the dictionary. But we have
established that it IS there, so let's start an LH log and see why the
read is failing.
Shortly thereafter armed with an LH
log, we took a good look at the relevant section and quickly established
that the designer was using a TEMP table, not the actual table. Without
bothering to test further we simply instructed the client to stop using a
TEMP table, as this was likely the cause. The client remonstrated that
they WEREN'T using a TEMP table, so at this point we asked for the
dictionary and SYSREPOSWIN row to test for ourselves. We ran with the
actual table and to our surprise, the LH log showed that regardless of
what we told the form designer, a temp table was created and populated
with all of the dictionary items from the actual table. And to make
matters worse, the columns that wouldn't show up were present both in the
temp table AND in the %FIELDS% of the temp table.
Employing
Occam's Razor, we then quick painted a form with just two controls, the
key prompt and one of the disappearing columns. It quick painted fine
BUT the moment we tried to modify the disappearing column's control
properties, it was unbound from the data table. All we could gather from
that, was that the initial quick paint uses a different column list than
the form designer column selection dialog.
Working on
the hunch that something in the form read process was removing the
column, we then copied all but the two columns needed by our test form
into a separate dictionary, and developed a program to copy in a
dictionary item, REPOSITORY ACCESS the form, REPOSITORY COMPILE the
form, then ACCESS it again to see at what point the column was blanked
down. (There were over 700 dictionary items so doing this manually would
have been too tedious). To our consternation the column was never
blanked down, BUT the moment we entered the form designer it was
immediately removed.
At this point all fingers were
pointing to the form designer as the culprit, so we had to take a peek
inside to see what it was up to. In the absence of source code, the only
way to do this was to run an oeprofile.log and dissect that.
The
profile log showed that during the loading process, the form designer
called DICT_MFS_BUILD, which in turn called COMPILE.PROTECT for a number
of dictionary items that was less than the total dictionary items in the
table. So in another wild goose chase, we wrote a program to emulate
this behaviour and see which dictionary items were failing to compile.
Naturally this failed to produce the results we needed.
At
this point we were pulling our hair out with frustration, so we just
populated the dictionary with only the columns needed for the original
data entry form the client had provided. The form worked and the columns
remained intact in the controls. So we copied in the rest of the
dictionary items and again the columns were removed from the errant
controls. It was time to abandon abstruse investigative techniques and
just go back to plain old gumshoeing.
It was obvious
that at least ONE of the columns in the dictionary was causing the issue
but which one? The only way to investigate would be to copy the
dictionary items in one by one, go into the form designer, check if the
control had had the column removed and if so the last dictionary item
copied in was our culprit. The idea of doing this for over 700 columns
frankly sucked. Enter our old friend - 'binary chop'.
We
wrote a program that selected the temporary dictionary in sorted order ,
and then copied in the first 360 or so columns. We went into the form
designer and sure enough, the affected controls had their columns
removed. So we modified the program to do the first 180. Same problem.
First 90. Same problem. First 45. The controls kept their columns. First
72. Same problem. First 58. The controls kept their columns. First 65.
Same problem. First 61. The controls kept their columns. First 63. Same
Problem. First 62. The controls kept their columns.
Ladies
and Gentlemen we have a winner! Dictionary item 63 looks to be the
culprit. But one final check. We copied in ALL dictionary items apart
from that one and the form remained intact. It was the sole dictionary
item responsible. We called the client and asked them to confirm.
Delightedly they did...
Finally it was time to see WHY
this dictionary item was such bad news. Opening it in the table builder
showed no obvious reason, but reading it then dropping to the debugger so
it could be examined properly gave the game away. There was object code
on attribute 20. It was still a Rev G format dictionary item and for
reasons best known to C++ this confused the form designer.
Armed
with this knowledge we could easily inspect the table and others for
dictionary items with code on attribute 20 - and to save ourselves the
hassle of writing a program we just used a simple RLIST statement
run rlist "LIST
DICT.CONTACTS USING DICT.AVERY_LABELS WITH LABELS_PER_SHEET",1
Note
that we could have opted to create a new dict item in SYSDICT pointing
to column 20 and query on that but as the AVERY_LABELS table already
had a dictionary item pointing at column 20 we used that instead in
combination with the RLIST "USING AltDict" syntax.
Now if only
we'd gone down the simple route at the beginning. Still it's hard to
beat the adrenaline boost you get when you finally get there!