Project

General

Profile

Feature #3940

CREATE-RESULT-LIST-ENTRY() method support

Added by Greg Shah over 5 years ago. Updated over 4 years ago.

Status:
Closed
Priority:
Normal
Target version:
-
Start date:
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
version_reported:
version_resolved:

query-entry-browse.p Magnifier (822 Bytes) Stanislav Lomany, 03/20/2019 06:21 PM

query-entry.p Magnifier (761 Bytes) Stanislav Lomany, 03/20/2019 06:21 PM

query-entry-mult.p Magnifier (1.13 KB) Stanislav Lomany, 03/20/2019 06:21 PM

crle-compound3-find-where.p Magnifier (1.06 KB) Stanislav Lomany, 08/14/2019 04:07 PM

simple-compound.p Magnifier (705 Bytes) Stanislav Lomany, 08/21/2019 07:40 AM

all-tests.ods (11.6 KB) Stanislav Lomany, 09/02/2019 08:03 PM

edit-nofocus.png (1.44 KB) Stanislav Lomany, 09/26/2019 08:55 AM


Related issues

Related to Database - Bug #4325: Not flushing the newly created record to the database New 09/14/2019
Related to User Interface - Feature #4341: CREATE-RESULT-LIST-ENTRY missing implementation features New 10/05/2019

History

#1 Updated by Greg Shah over 5 years ago

As noted in #3762, we must provide support for both BROWSE:CREATE-RESULT-LIST-ENTRY() and QUERY:CREATE-RESULT-LIST-ENTRY().

From Stanislav:

I've been wondering if the core of the implementation is the idea that the query is closed and reopened such that there is a new result list.

Unfortunately it's not the case. The new entry contains ids of the current records contained in the query's backing buffers. It can be arbitrary records not matching the original query sorting. "No record" is also a valid entry.

Note that I'm not going to implement CREATE-RESULT-LIST-ENTRY for non-scrolling queries because it seems to be inpractical: what is the sense of adding an entry if you cannot get back to it? I guess that underlying implementations for scrolling and non-scrolling queries are similar in 4GL, while in FWD it may be completely different, so that will save a lot of time.

#2 Updated by Greg Shah over 5 years ago

Stanislav: You spent some time on this recently. Please post your findings here. What branch has your code changes?

#3 Updated by Stanislav Lomany over 5 years ago

Stanislav: You spent some time on this recently. Please post your findings here. What branch has your code changes?

Which branch do you want me to commit to? The changes are small and safe.

#4 Updated by Greg Shah over 5 years ago

Use 3750b.

#5 Updated by Greg Shah over 5 years ago

Stanislav:

  1. I don't think your changes are in 3750b yet.
  2. Did you write testcases? If so, where are they?
  3. Please document a list of your findings.
  4. What aspects of the problem still need testcases? We will have external resources write these.

#6 Updated by Stanislav Lomany over 5 years ago

Greg, where I can commit to, except 3750b?

#7 Updated by Greg Shah over 5 years ago

Put it in 3908a.

#8 Updated by Stanislav Lomany over 5 years ago

Put it in 3908a.

I don't see an active branch with this number.

#9 Updated by Greg Shah over 5 years ago

Sorry, it is 3809a.

#10 Updated by Stanislav Lomany over 5 years ago

Testcases attached. Couple of notes:

  1. There is no need to implement CREATE-RESULT-LIST-ENTRY for non-scrolling queries.
  2. "No record" is a valid entry.
  3. Additional testcases should be created to check edge conditions like adding entries to the beginning or the end of the list. Also reposition TO ROW and TO ROWID should be checked.

#11 Updated by Stanislav Lomany over 5 years ago

I've committed stubs for CREATE-RESULT-LIST-ENTRY as rev 11302 in 3809a. It doesn't contain much actual code.

#12 Updated by Stanislav Lomany about 5 years ago

What aspects of the problem still need testcases? We will have external resources write these.

Testcases for QUERY:CREATE-RESULT-LIST-ENTRY():
  1. Testcases should be for queries with the single or multiple buffers.
  2. Testcases should be for all FWD query types: PreselectQuery, AdaptiveQuery, CompoundQuery etc. (we need to provide rules that will allow to create such testcases).
  3. A new entry should be added into the middle of a result set, as the first entry, as the last entry. Addition of multiple sequential entries should be checked.
  4. After the item was added, check that first/last/next/prev navigation and reposition to row/rowid/forwards/backwards works.
  5. Test empty buffer(s) in a result entry for single/multiple-buffer entries.
  6. Ideally it can be a small testcase harness that can run all related testcases at once.
  7. We may cut down the number of testcases by not implementing them for non-scrolling queries.
  8. Testcases should be for permanent and temporary buffers.
  9. Check that DELETE-RESULT-LIST-ENTRY works on the created entries.
Testcases for BROWSE:CREATE-RESULT-LIST-ENTRY():
  1. Check "standard" way of adding new rows to browse using insert-row + create-result-list-entry.
  2. All interactive testcases should display reproduction sequence in the message area.
  3. Actually the cases described above for the QUERY can be applied to test browse.

#13 Updated by Greg Shah about 5 years ago

Testcases should be for all FWD query types: PreselectQuery, AdaptiveQuery, CompoundQuery etc. (we need to provide rules that will allow to create such testcases).

  • PreselectQuery is created from DO/REPEAT PRESELECT
  • AdaptiveQuery is FOR EACH
  • CompoundQuery is a multiple-buffer query such as FOR EACH buf1 EACH buf2

Is this correct and what other 4GL query types are needed?

A new entry should be added into the middle of a result set, as the first entry, as the last entry. Addition of multiple sequential entries should be checked.

Are there scenarios where the new entry cannot be created? If so we need tests for that.

We also need testcases to show what error conditions or other boundary conditions. For example, what happens if the query is closed? Or what happens when the query handle is unknown/not initialized?

We may cut down the number of testcases by not implementing them for non-scrolling queries.

At a minimum, we need a testcase that proves that non-scrolling queries cannot work with CREATE-RESULT-LIST-ENTRY.

#14 Updated by Stanislav Lomany about 5 years ago

Addition: 10. We may also want to test EACH, FIRST/LAST/UNIQUE cases. Really not sure about cases where FIRST/LAST/UNIQUE is used as the first component.

  • PreselectQuery is created from DO/REPEAT PRESELECT
  • AdaptiveQuery is FOR EACH
  • CompoundQuery is a multiple-buffer query such as FOR EACH buf1 EACH buf2

CREATE-RESULT-LIST-ENTRY works only with query handle, so DO/REPEAT PRESELECT is not applicable. I think the list is:

  1. AdaptiveQuery is FOR EACH
  2. PreselectQuery is FOR EACH when there is no matching index.
  3. CompoundQuery is FOR EACH buf1, EACH buf2
  4. PresortQuery is FOR EACH BREAK BY
  5. PresortCompoundQuery is FOR EACH buf1, EACH buf2 BREAK BY where buf1 and buf2 belong to different databases (e.g. temporary and permanent).

Are there scenarios where the new entry cannot be created? If so we need tests for that.
We also need testcases to show what error conditions or other boundary conditions. For example, what happens if the query is closed? Or what happens when the query handle is unknown/not initialized?

The two cases you've described are the only ones that come to my mind. I'm not aware of other cases that fail.

At a minimum, we need a testcase that proves that non-scrolling queries cannot work with CREATE-RESULT-LIST-ENTRY.

Non-scrolling queries CAN work with it. I wasn't sure if we need to implement them. But you're right, why not have testcases for them?
So, 11. SCROLLING/non-SCROLLING.

#15 Updated by Marian Edu about 5 years ago

For feature 3940 we have develop following tests:
  1. CREATE-RESULT-LIST-ENTRY
    • Static temp-tables - temporary
      • Single table in query
        • For Each
          • Empty temp-tables, build query index, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, build query index, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
          • Join query between tables, create records out of where clause apply method and navigate
          • Primary key join query between tables, create records out of where clause apply method and navigate
          • Loaded temp-tables, build query index, create parent/child records, navigate at middle, apply method for several times and navigate
        • Preselect Each
          • Empty temp-tables, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
      • Multiple tables in query
        • For Each
          • Empty temp-tables, build query index, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, build query index, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
          • Join query between tables, create records out of where clause apply method and navigate
          • Primary key join query between tables, create records out of where clause apply method and navigate
        • Preselect Each
          • Empty temp-tables, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
      • Errors and Boundaries
        • Closed query
        • Empty temp-table
        • Invalid handle
        • No records in temp-table
        • Scrolling query and release temp-table
        • No-scrolling and release temp-table
        • Apply method for scrolling and non-scrolling query
        • Unopen query
    • Tables from database – permanent
      • Single table in query
        • For Each
          • Empty temp-tables, build query index, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, build query index, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
        • Preselect Each
          • Empty temp-tables, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
      • Multiple tables in query
        • For Each
          • Empty temp-tables, build query index, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, build query index, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
        • Preselect Each
          • Empty temp-tables, create parent/child records ( all combinations ), apply method and navigate
          • Empty temp-tables, no build query index, create parent/child records ( all combinations ), apply method and navigate
          • Loaded temp-tables, create parent/child records, navigate at first(last, middle, beginning), apply method and navigate
  1. DELETE-RESULT-LIST-ENTRY
    • Static temp-tables - temporary
      • Single table in query
        • Test link between temp-table and query index by call method after execute find on temp-table
        • Method behavior for table with deleted records after building query index
        • Behavior after build query index, delete elements from table, navigate on index of deleted elements and apply method
        • Behavior for released table
        • Behavior for build query index, apply reposition without next/previous and apply method
      • Multiple tables in query
        • Update record in child table and test behavior of using / not using delete-result-list-entry
File name explication:
  • for / preselect – type of open query
  • empty / load – temp-table or db-table has not / has records before build query index
  • list / no_list – build/no_build query index
  • na_na / ok_na / na_ok / ok_ok – table parent or child have/have not record after query index is build
  • result – apply method – create or delete depending of folder
  • first/last/prev/next – navigation on query before of after apply method

#16 Updated by Stanislav Lomany almost 5 years ago

At this point FWD has limited implementation of CREATE-RESULT-LIST-ENTRY: it is only implemented for AdaptiveQuery and implementation is not completed (TODO left and it looks like the code hasn't been tested thoroughly).

#17 Updated by Stanislav Lomany almost 5 years ago

I've noticed an issue that is not related to this task. Testcase:

define temp-table tt field f1 as integer.
def var h as handle.
h = buffer tt:handle.
delete object h no-error.  <-- exception here  

Exception:
Caused by: java.lang.RuntimeException: Error creating static proxy for interface com.goldencode.p2j.util.CommonSession: static method boolean isType(java.lang.String) was not found in the given classes.
        at com.goldencode.proxy.StaticProxy.init(StaticProxy.java:346)
        at com.goldencode.proxy.StaticProxy.<init>(StaticProxy.java:223)
        at com.goldencode.proxy.StaticProxy.obtain(StaticProxy.java:200)
        at com.goldencode.p2j.util.SessionUtils.asHandle(SessionUtils.java:202)
        at com.goldencode.p2j.util.HandleOps.isSystemHandle(HandleOps.java:250)
        at com.goldencode.p2j.util.HandleOps.delete(HandleOps.java:321)

This error appears after rev 11325:
revno: 11325 [merge]
  Adds many runtime/conversion improvements and fixes to the DATA-SET support in FWD:
  - OM: many changes related to DATA-SEt (conversion and runtime).  This includes BEFORE/AFTER-TABLE/BUFFER/ROWID, COPY-DATASET, JSON/XML/XMLSCHEMA serialization, MERGE-CHANGES, ORIGIN-ROWID and many more. Refs #3809
  - AVK:  #4078 issue with Hotel demo login in Swing mode
  - HC: adds JSON support via runtime implementation of legacy (previously skeleton) classes. Refs #3751

#18 Updated by Greg Shah almost 5 years ago

I think this is already fixed in 4124a, please check your testcase in that branch.

#19 Updated by Stanislav Lomany almost 5 years ago

I think this is already fixed in 4124a, please check your testcase in that branch.

Yes, it is fixed.

#20 Updated by Stanislav Lomany almost 5 years ago

I want to clarify if we need to implement CREATE-RESULT-LIST-ENTRY for non-scrolling queries? Do you see any practical use in it? If not, we can postpone it until we meet a real case where it is used. That'll save us a lot of time.

#21 Updated by Stanislav Lomany almost 5 years ago

As far as I get buffer::extent-field = variable is not supported at runtime. Correct me if I'm wrong.

#22 Updated by Stanislav Lomany almost 5 years ago

Greg, when CREATE-RESULT-LIST-ENTRY inserts the last entry in the result set and we continue to iterate the query, things become messy in 4GL. It continues to walk by index which produces a kind of mixed results. Specifically, if we have record X Y Z inserted after the record A B C then the A B C will be "snapshot" records which drive the order, but X and Y will remain in buffers until they're gone by iteration. An example (A+1 notation means "the next record after A"):

A   B   C
X   Y   Z
X   Y   C+1
..........
X   B+1 ..
..........
A+1 ..  ..

4GL in this case can produce records that do NOT match WHERE condition of the query. But, as well, in some cases, WHERE condition is properly applied.
Testcase is attached, sample output:
1 1 2
1 1 3
1 1 4
1 2 1
1 2 2

3 3 4 <-- inserted entry

3 3 3 <-- doesn't match WHERE condition   
3 3 4
3 3 1
3 3 2
     <--- why "3 3 3" is missing if we can see it above?  
3 3 4
3 4 1
3 4 2
3 4 3
3 4 4
     ------ end of disturbance caused by the insertion,
     ------ entries from "1 2 3" to "1 4 4" are missing 
2 1 1
2 1 2
2 1 3
2 1 4
.....

The point is that if user inserts an entry during query iteration, results can be unexpected.
I'm not 100% sure, I'll check it tomorrow again, but I haven't seen this kind of behavior when CREATE-RESULT-LIST-ENTRY is used by a browse, which is interesting.

#23 Updated by Stanislav Lomany almost 5 years ago

Couple of notes:
  1. If a query is bound to the browse then CREATE-RESULT-LIST-ENTRY cannot be used without creating a new row in the browse: "Selected row must be new row when using CREATE-RESULT-LIST-ENTRY for QUERY widget. (9483)" error occurs in this case.
  2. In the previous note I noted that 4GL has messy behavior when a query is iterated after the last entry has been created by CREATE-RESULT-LIST-ENTRY. In browse I haven't seen this behavior. Why? 4GL uses a trick. If a new row is the last row in the view, then right after ROW-ENTRY event is called for this row, 4GL fetches an extra entry for the backing query. Which means that CREATE-RESULT-LIST-ENTRY will create non-last entry and messy behavior doesn't occur.

#24 Updated by Stanislav Lomany almost 5 years ago

I've attached a simple testcase that shows that after a record has been added during iteration of a compound query, compound query results can be unexpected. That influences my work only indirectly: I have to check if a bug was added by me or if it is an existing bug.

         1                1  
         1                2
         1                3
         2                1  
         2                2
         2                3
         2                7
         3                1  <-- missing in FWD
         3                2  <-- missing in FWD
         3                3
         3                7
         5                1  <-- missing in FWD
         5                2  <-- missing in FWD
         5                3
         5                7

#25 Updated by Stanislav Lomany almost 5 years ago

Rebased task branch 3816a from P2J trunk revision 11328.

#26 Updated by Stanislav Lomany almost 5 years ago

I've attached a document with the status of CREATE-RESULT-LIST-ENTRY testcases. So we have:
  1. Fix few issues for adaptive queries.
  2. Looks like there are two types of compound query errors (not sure what lies beyond these errors).
  3. CREATE-RESULT-LIST-ENTRY should be implemented for preselect queries.

#27 Updated by Greg Shah almost 5 years ago

In which branch are your changes?

I assume you are implementing the DELETE-RESULT-LIST-ENTRY as well? It is used in the testcases.

#28 Updated by Stanislav Lomany almost 5 years ago

In which branch are your changes?

3816a

I assume you are implementing the DELETE-RESULT-LIST-ENTRY as well? It is used in the testcases.

Now I assume so too.

#29 Updated by Stanislav Lomany almost 5 years ago

Greg, P2JQuery.first/next/previous/last function are all declared with @throws QueryOffEndException. Is that valid? GET NEXT/PREV/FIRST/LAST statements which are converted to these function do not raise error:

If you execute a GET NEXT statement after the last record of the query has been fetched or you execute a GET PREV statement after the first record of the query has been fetched, the ERROR condition is not raised. 

Fortunately, internals of these functions do not actually throw QueryOffEndException, so everything is OK. But I found the case which I need to fix and which throws QueryOffEndException. So the question is: should I remove @throws QueryOffEndException and handle QueryOffEndException internally for the case I need to fix?

#30 Updated by Eric Faulhaber almost 5 years ago

QueryOffEndException is thrown by some implementations of these methods, not all. It is needed by the BlockManager to terminate loop processing in some cases, for example. This is why it is annotated with @throws in the P2JQuery javadoc. As long as a single case exists that needs it, we should leave the annotation in place, I think.

If you change a particular concrete implementation which currently throws it under some conditions, such that it can no longer be thrown from that implementation, I guess you can remove it from that particular implementation's comments. I'm not sure if javadoc will complain about that.

In any case, this is an unchecked exception, and there is no requirement to catch it based on a javadoc annotation. So, what are you aiming to correct?

#31 Updated by Stanislav Lomany almost 5 years ago

In any case, this is an unchecked exception, and there is no requirement to catch it based on a javadoc annotation. So, what are you aiming to correct?

Here is a testcase which demonstrates the problem:

def temp-table tt field f1 as integer index idx1 f1.

create tt. tt.f1 = 2.

def var i as integer.

def query q for tt scrolling.
open query q for each tt.

query q:get-first().         
message query q:NUM-RESULTS.

find first tt. delete tt.

query q:get-last().    // QueryOffEndException stops the testcase      
message query q:NUM-RESULTS.

And here is the stack. I'm not sure at what point it goes wrong. So far I suspect DynamicResults.

setRecord:8827, RecordBuffer (com.goldencode.p2j.persist)         <- raises QueryOffEndException
updateBuffer:3278, RandomAccessQuery (com.goldencode.p2j.persist)
last:1629, RandomAccessQuery (com.goldencode.p2j.persist)
last:1511, RandomAccessQuery (com.goldencode.p2j.persist)
last:4085, AdaptiveQuery$DynamicResults (com.goldencode.p2j.persist)
last:144, ResultsAdapter (com.goldencode.p2j.persist)
last:2402, PreselectQuery (com.goldencode.p2j.persist)
last:1698, AdaptiveQuery (com.goldencode.p2j.persist)
last:2369, PreselectQuery (com.goldencode.p2j.persist)
getLast:2244, AbstractQuery (com.goldencode.p2j.persist)
getLast:4826, QueryWrapper (com.goldencode.p2j.persist)
lambda$execute$0:32, Switch1d (com.goldencode.testcases)

#32 Updated by Eric Faulhaber almost 5 years ago

When we assign the PreselectQuery (AdaptiveQuery) delegate to the QueryWrapper, we probably need to call delegate.setLenientOffEnd(true). I guess this is true for all PreselectQuery delegates, unless we use QueryWrapper for some other case I can't remember right now.

#33 Updated by Stanislav Lomany almost 5 years ago

In this case lenientOffEnd is already set in PreselectQuery.open. But I have another idea. lenientOffEnd doc says than

If set to lenient mode, invocations of next and previous which do not find a record do not raise a QueryOffEndException.

We don't invoke next/prev, we invoke first/last which go off-end because the result set is empty. Does this case in your opinion fit in the cases which lenient off-end should handle? If yes, then I should add conditional re-throw of QueryOffEndException in PreselectQuery.first/last.

#34 Updated by Eric Faulhaber almost 5 years ago

Well, the fact that I differentiated the off-end behavior between first/last and next/previous tells me I needed it work this way to handle a specific case, though I can't remember what that might be now. I developed PreselectQuery and AdaptiveQuery originally to handle the basic REPEAT PRESELECT and FOR EACH semantics, respectively. The requirements of OPEN QUERY (which introduced QueryWrapper) came later. So, the lenientOffEnd behavior probably was biased toward the former contructs' semantics.

I'm thinking we don't want to change the core PreselectQuery behavior, but maybe we honor it for QueryWrapper.first|last by catching the QOEE and checking the PreselectQuery delegate's lenientOffEnd flag. If true, we eat the QOEE, otherwise we rethrow it. That should cover the GET FIRST|LAST cases needing not to raise the exception, while preserving the original behavior for the core query implementations.

We really should have a test case to verify the need for the QOEE on PreselectQuery|AdaptiveQuery.first|last. You'll need to look into the TRPL which emits these to determine what 4GL constructs result in those methods being used.

#35 Updated by Stanislav Lomany almost 5 years ago

Guys, this abend

Caused by: java.lang.NullPointerException: Table must be non-null
        at com.goldencode.p2j.persist.RecordIdentifier.<init>(RecordIdentifier.java:103)
        at com.goldencode.p2j.persist.dirty.DefaultDirtyShareManager.lockSingle(DefaultDirtyShareManager.java:1706)
        at com.goldencode.p2j.persist.dirty.DefaultDirtyShareManager.getDirtyInfo(DefaultDirtyShareManager.java:1351)
        at com.goldencode.p2j.persist.dirty.DefaultDirtyShareContext.getDirtyInfo(DefaultDirtyShareContext.java:1123)
        at com.goldencode.p2j.persist.RandomAccessQuery.executeImpl(RandomAccessQuery.java:4340)
        at com.goldencode.p2j.persist.RandomAccessQuery.execute(RandomAccessQuery.java:3415)
        at com.goldencode.p2j.persist.RandomAccessQuery.findNext(RandomAccessQuery.java:3803)
        at com.goldencode.p2j.persist.RandomAccessQuery.next(RandomAccessQuery.java:1818)
        at com.goldencode.p2j.persist.RandomAccessQuery.next(RandomAccessQuery.java:1697)
        at com.goldencode.p2j.persist.AdaptiveQuery$DynamicResults.next(AdaptiveQuery.java:4097)
        at com.goldencode.p2j.persist.ResultsAdapter.next(ResultsAdapter.java:159)
        at com.goldencode.p2j.persist.PreselectQuery.peekNext(PreselectQuery.java:3449)
        at com.goldencode.p2j.persist.AdaptiveQuery.peekNext(AdaptiveQuery.java:2116)
        at com.goldencode.p2j.persist.CompoundQuery.processComponent(CompoundQuery.java:2928)
        at com.goldencode.p2j.persist.CompoundQuery.retrieveImpl(CompoundQuery.java:2558)
        at com.goldencode.p2j.persist.CompoundQuery.retrieve(CompoundQuery.java:1976)
        at com.goldencode.p2j.persist.CompoundQuery.peekNext(CompoundQuery.java:1327)
        at com.goldencode.p2j.persist.Cursor.reposition(Cursor.java:348)
        at com.goldencode.p2j.persist.Cursor.repositionByID(Cursor.java:292)
        at com.goldencode.p2j.persist.DynamicQuery.repositionByID(DynamicQuery.java:221)
        at com.goldencode.p2j.persist.CompoundQuery.repositionByID(CompoundQuery.java:353)
        at com.goldencode.p2j.persist.QueryWrapper.repositionByID(QueryWrapper.java:2806)
        at com.goldencode.p2j.persist.QueryWrapper.queryRepositionByID(QueryWrapper.java:3597)
        at com.goldencode.tests.query.db.methods.create_result_list_entry.multiple.ForLoadListBeginingOkOkResult.lambda$null$40(ForLoadListBeginingOkOkResult.java:154)

happens inside this call:

// check the dirty share manager for an overriding result
// TODO: checking the number of referenced buffers is a temporary workaround;
// a query with server-side joins will not return the right information from the
// dirty database and may cause a fatal error, if the joined table does not yet
// exist in the dirty database; however, this is not a correct solution!
if (info == null && getReferencedBuffers().length == 1)
{
   info = dirtyContext.getDirtyInfo(buffer,
                                    index,
                                    activeBundleKey,
                                    activeBundle,
                                    allArgs,
                                    allTypes,
                                    dmo,
                                    hqlPreproc.isFindByRowid());
}

What kind of tactics should be applied here?

#36 Updated by Eric Faulhaber almost 5 years ago

Is this the first exception? Because this

Caused by: java.lang.NullPointerException: Table must be non-null
        at com.goldencode.p2j.persist.RecordIdentifier.<init>(RecordIdentifier.java:103)
        at com.goldencode.p2j.persist.dirty.DefaultDirtyShareManager.lockSingle(DefaultDirtyShareManager.java:1706)
        at com.goldencode.p2j.persist.dirty.DefaultDirtyShareManager.getDirtyInfo(DefaultDirtyShareManager.java:1351)
        ...

...is happening when we are trying to unlock an index. I would have expected this to fail earlier, when we are trying to acquire the lock, at DefaultDirtyShareManager:1242.

Anyway, this stack trace tells me that index we are passing into the DefaultDirtyShareContext.getDirtyInfo call is null. This indicates we could not match the sort clause used for the RandomAccessQuery. This happens in the RAQ.initialize method:

         // Get index name, which is needed for dirty checking work.
         try
         {
            IndexHelper indexHelper = buffer.getIndexHelper();
            this.index = indexHelper.getIndexForSort(buffer, sort);
            this.dmoSorter = indexHelper.getSorterForSortPhrase(buffer, sort);
         }
         catch (PersistenceException exc)
         {
            if (LOG.isLoggable(Level.SEVERE))
            {
               LOG.log(Level.SEVERE, "Error locating index for sort phrase [" + sort + "]", exc);
            }
         }

Do you see that, "Error locating index for sort phrase..." error in the log?

What does the business logic for the CompoundQuery setup look like (i.e., from ForLoadListBeginingOkOkResult)?

#37 Updated by Stanislav Lomany almost 5 years ago

Do you see that, "Error locating index for sort phrase..." error in the log?

No, index field just gets null value here: this.index = indexHelper.getIndexForSort(buffer, sort); I suppose we should raise Error locating index for sort phrase... in this case too.

So, the permanent H2 database got queried for the index with no result. Are indexes supposed to be carried from .df into H2 database?

#38 Updated by Stanislav Lomany almost 5 years ago

I'm thinking we don't want to change the core PreselectQuery behavior, but maybe we honor it for QueryWrapper.first|last by catching the QOEE and checking the PreselectQuery delegate's lenientOffEnd flag. If true, we eat the QOEE, otherwise we rethrow it.

Do you think that QueryWrapper.first|last should throw QOEE? It is used in:
  1. get first|last
  2. get-first|last
  3. reposition
  4. browse logic

Browse is aware of this specificity of first|last, in other cases throw of QOEE looks like a bug.

#39 Updated by Eric Faulhaber almost 5 years ago

Stanislav Lomany wrote:

I'm thinking we don't want to change the core PreselectQuery behavior, but maybe we honor it for QueryWrapper.first|last by catching the QOEE and checking the PreselectQuery delegate's lenientOffEnd flag. If true, we eat the QOEE, otherwise we rethrow it.

Do you think that QueryWrapper.first|last should throw QOEE? It is used in:
  1. get first|last
  2. get-first|last
  3. reposition
  4. browse logic

Browse is aware of this specificity of first|last, in other cases throw of QOEE looks like a bug.

If you have found through your testing that throwing QOEE appears to be a bug in these cases, then it probably is. We can eat it at the QueryWrapper level if you can find no OPEN QUERY case where it should be thrown.

#40 Updated by Eric Faulhaber almost 5 years ago

Stanislav Lomany wrote:

Do you see that, "Error locating index for sort phrase..." error in the log?

No, index field just gets null value here: this.index = indexHelper.getIndexForSort(buffer, sort); I suppose we should raise Error locating index for sort phrase... in this case too.

Probably, though I suspect this error is due to an improper environment (indices were not migrated to the database).

So, the permanent H2 database got queried for the index with no result. Are indexes supposed to be carried from .df into H2 database?

Indices are normally created during data import. If you ran conversion and then just used the table DDL to create an H2 database for testing, but never imported anything into it, then it has no indices. This is not a "normal" application migration scenario, but you can create the indices separately by running the generated index DDL on your test database. They are absolutely necessary for converted code to operate correctly, not just for performance.

#41 Updated by Stanislav Lomany almost 5 years ago

Guys, here is a simple testcase:

def temp-table tt field f1 as integer index idx1 f1.

create tt. tt.f1 = 1.

open query q preselect each tt.
message avail(tt).

In FWD the record is available, in 4GL it is not.
Should I fix it by releasing backing buffers on open of a QueryWrapped- preselect query?

#42 Updated by Eric Faulhaber almost 5 years ago

Yes.

#43 Updated by Stanislav Lomany almost 5 years ago

FYI, I made harness which sequentially runs CRLE tests. I had to split the set of tests in half, otherwise this error occurs:

Caused by: java.lang.RuntimeException: Unexpected code path
        at org.h2.message.DbException.throwInternalError(DbException.java:254)
        at org.h2.message.DbException.throwInternalError(DbException.java:267)
        at org.h2.engine.Session.unlockAll(Session.java:985)
        at org.h2.engine.Session.endTransaction(Session.java:760)
        at org.h2.engine.Session.commit(Session.java:708)
        at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:46)
        at org.h2.command.CommandContainer.update(CommandContainer.java:102)
        at org.h2.command.Command.executeUpdate(Command.java:261)
        at org.h2.jdbc.JdbcConnection.commit(JdbcConnection.java:497)

#44 Updated by Eric Faulhaber almost 5 years ago

We've seen this before with H2. Don't know what it is, but in every case we've hit this so far, doing the same thing with PostgreSQL does not cause a problem. If it's important to avoid this, I would recommend switching over to PostgreSQL. Otherwise, go with your approach, as long as it meets your needs.

#45 Updated by Stanislav Lomany almost 5 years ago

Guys, in the following sequence

open query q preselect each tt.

create tt. tt.f1 = 1.
query q:create-result-list-entry.
get first q.

first fails to load the recently created record into the buffer. PreselectQuery.coreFetch -> Persistence.load tries to load the record into the buffer which already contains the target record. It fails because Session.get returns null. Any ideas?

This sequence works:

...
release tt.
get first q.

#46 Updated by Eric Faulhaber almost 5 years ago

Stanislav Lomany wrote:

Guys, in the following sequence
[...]

first fails to load the recently created record into the buffer. PreselectQuery.coreFetch -> Persistence.load tries to load the record into the buffer which already contains the target record. It fails because Session.get returns null. Any ideas?

I suspect we are not flushing the newly created record to the database properly in this sequence, whereas the release forces a flush.

This sequence works:
[...]

Is this blocking you? Can you use the latter sequence for your testing and open a new bug for the failing one?

#47 Updated by Stanislav Lomany almost 5 years ago

Is this blocking you? Can you use the latter sequence for your testing and open a new bug for the failing one?

OK, I'll modify failing testcase(s) so they don't fail and open a new bug.

BTW AdaptiveQuery fails on this part too, it just has an extra way to re-find record.

#48 Updated by Stanislav Lomany almost 5 years ago

  • Related to Bug #4325: Not flushing the newly created record to the database added

#49 Updated by Stanislav Lomany almost 5 years ago

Caused by: java.lang.RuntimeException: Unexpected code path

I got rid of this by setting MVCC=false.

#50 Updated by Stanislav Lomany almost 5 years ago

We have the following issues with INSERT-ROW:
  1. If a button trigger executes INSERT-ROW, then focus is changed to the target browse. Even the following trigger sets the focus to the browse:
    on choose of btn
    DO:
      browse br:INSERT-ROW("After").
      apply "entry" to other-widget.
    END. 
    
  2. If a trigger is fired from a fill-n, then focus is not changed, but browse draws the in-row fill-in, so we have an in-row fill-in while the browse is not focused.
  3. Haven't checked the other types of widgets yet.

I think that the item 1 should be fixed because it is typical to have an "Add record" button. Do you agree?

#51 Updated by Greg Shah almost 5 years ago

I think that the item 1 should be fixed because it is typical to have an "Add record" button. Do you agree?

Yes. Is issue 1 explaining how it should work in the 4GL?

Can you explain issue 2 a bit more?

#52 Updated by Stanislav Lomany almost 5 years ago

Yes. Is issue 1 explaining how it should work in the 4GL?

Yes.

Can you explain issue 2 a bit more?

Here is a screenshot of the result of this trigger which has been fired while the lower fill-in was focused:

ON i anywhere 
DO:
  browse br:INSERT-ROW("After"). 
END.

We have an in-row fill-in which is drawn, but not focused.

#53 Updated by Greg Shah almost 5 years ago

Is issue 2 really just the same problem as issue 1 ("we must set focus to the browse on INSERT-ROW")?

#54 Updated by Stanislav Lomany almost 5 years ago

Issue 1 (button) is about delayed focus change.
Issue 2 (fill-in) is about maintaining an in-row fill-in when browse is not focused.

#55 Updated by Greg Shah almost 5 years ago

Can you fix issue 2 quickly?

#56 Updated by Stanislav Lomany almost 5 years ago

Can you fix issue 2 quickly?

It looks like the mechanics is the following: focus is temporary changed to the in-row editor and it fires ENTRY event. Then the focus is silently returned to the original widget without popping any ENTRY or LEAVE events. Also it is required to check other types of widgets and events which fire the trigger. So I don't think I can do it quickly.

#57 Updated by Stanislav Lomany almost 5 years ago

Rebased task branch 3816a from P2J trunk revision 11333.

#58 Updated by Stanislav Lomany over 4 years ago

  • Status changed from New to WIP

Rebased task branch 3816a from P2J trunk revision 11334.
Please review it.

#59 Updated by Greg Shah over 4 years ago

Is there anything left open on this task?

#60 Updated by Ovidiu Maxiniuc over 4 years ago

I have just reviewed the 11342 revision of branch 3816a. I found nothing wrong with it.

#61 Updated by Stanislav Lomany over 4 years ago

Is there anything left open on this task?

I'm working on the issues with CRLE when the created row is out of view.

Other than that there's nothing left. Some issues were intentionally ignored/postponed:
  1. Implementation for CRLE/DRLE for non-scrolling queries - needs much effort to implement and is not practically used.
  2. CRLE for the last entry in the result list - 4GL behavior is messy.
  3. Browse mode when INSERT-ROW creates an in-row fill-in which is active even when the browse is not focused. Done by a trigger with INSERT-ROW if the current widget is not a button.

#62 Updated by Greg Shah over 4 years ago

Create tasks for the 3 issues, link them here and make me a watcher.

Can you finish today?

#63 Updated by Stanislav Lomany over 4 years ago

Can you finish today?

I'm not sure, I'll try.

#64 Updated by Stanislav Lomany over 4 years ago

Rebased task branch 3816a from P2J trunk revision 11335.

I've committed the fix for the CRLE "out of view" issue, so I'm done with this task.

Other than that, I noted although we recently had changes in row entry/leave events, there are (old or new?) unwanted entry events and duplicate entry events in FWD. I couldn't fix it quickly because it requires more solid approach.

#65 Updated by Greg Shah over 4 years ago

Did you update the gap marking rules to match your changes? I assume that CRLE, DRLE and INSERT-ROW are now considered fully supported at conversion and "full restricted" runtime. Please make a note about the restrictions and point to the task number (of the task you are going to add to list the remaining items).

We could list it as partial, but my understanding is that the remaining items are not core. Is that correct?

#66 Updated by Stanislav Lomany over 4 years ago

We could list it as partial, but my understanding is that the remaining items are not core. Is that correct?

Yes. I'll shortly mark them as you suggested.

#67 Updated by Greg Shah over 4 years ago

Is see the primary functional change is in Browse.insertRow(). How much testing have you done on this? I guess it would not get hit in ChUI regression testing (it has no updatable browses, I think). Have you tried it in one of the large GUI apps which have updatable browse?

Are there changes in query/persistence/browse that can be hit by ChUI regression testing?

#68 Updated by Stanislav Lomany over 4 years ago

Have you tried it in one of the large GUI apps which have updatable browse?

I'll test tomorrow and let you know the results.

Are there changes in query/persistence/browse that can be hit by ChUI regression testing?

Yes, query changes related to cursor need good review and regression testing.

#69 Updated by Stanislav Lomany over 4 years ago

The remaining issues were moved to #4341.

#70 Updated by Stanislav Lomany over 4 years ago

  • Related to Feature #4341: CREATE-RESULT-LIST-ENTRY missing implementation features added

#71 Updated by Greg Shah over 4 years ago

Yes, query changes related to cursor need good review and regression testing.

OK, please get ChUI regression testing done.

#72 Updated by Constantin Asofiei over 4 years ago

Stanislav, a quick note about the SharedVariableManager.lookupTempTable(String legacyName). Please make sure the legacy name is really the legacy name, with case-sensitivity preserved.

OTOH, the query changes I think they do need ETF testing... I'll work on this; I'm starting conversion, but before running the tests, please fix any MAJIC regressions you find (and let me know if you find any).

#73 Updated by Stanislav Lomany over 4 years ago

What are the know failing regression tests except tc_job_002?

#74 Updated by Constantin Asofiei over 4 years ago

Stanislav Lomany wrote:

What are the know failing regression tests except tc_job_002?

The 'clock violation approval' tests (and some employee reports), which are dependent on the time of day the testing is done.

#75 Updated by Stanislav Lomany over 4 years ago

Then we have two failing tests. I'm looking into it.

#76 Updated by Stanislav Lomany over 4 years ago

Then we have two failing tests. I'm looking into it.

Never mind. Regression testing passed. I just got two identical runs, the third one is good.

#77 Updated by Stanislav Lomany over 4 years ago

Stanislav, a quick note about the SharedVariableManager.lookupTempTable(String legacyName). Please make sure the legacy name is really the legacy name, with case-sensitivity preserved.

Search shouldn't be case-sensitive. E.g. it is used by ADD-FIELDS-FROM which is not case-sensitive.

#78 Updated by Constantin Asofiei over 4 years ago

Stanislav Lomany wrote:

Stanislav, a quick note about the SharedVariableManager.lookupTempTable(String legacyName). Please make sure the legacy name is really the legacy name, with case-sensitivity preserved.

Search shouldn't be case-sensitive. E.g. it is used by ADD-FIELDS-FROM which is not case-sensitive.

I understand that, what I meant is this: if the legacy name is w-t1, and a call is done via W-t1, this will resolve the same table, right?

#79 Updated by Greg Shah over 4 years ago

Constantin: Are we OK to merge to trunk?

We are from my perspective.

#80 Updated by Constantin Asofiei over 4 years ago

Yes, go ahead and merge.

#81 Updated by Stanislav Lomany over 4 years ago

Rebased task branch 3816a from P2J trunk revision 11336.

#82 Updated by Stanislav Lomany over 4 years ago

  • Status changed from WIP to Review

3816a has been merged into the trunk as bzr revision 11337.

#84 Updated by Greg Shah over 4 years ago

  • Assignee set to Stanislav Lomany
  • Status changed from Review to Closed
  • % Done changed from 0 to 100

Also available in: Atom PDF