# AbstractQuery.java

    /*
    ** Module   : AbstractQuery.java
    ** Abstract : Abstract base class for all query implementations
    **
    ** Copyright (c) 2004-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
    ** 001 ECF 20060102   @23793 Created initial version. Retrofitted existing
    **                           query classes to extend this class. Provides
    **                           basic accumulation architecture.
    ** 002 ECF 20060118   @23988 Modified to accommodate Accumulator changes.
    **                           Accumulator break group categories are now
    **                           managed internally by the Accumulator class.
    ** 003 ECF 20060221   @24714 Added validateSubstitutionArguments. Ensures
    **                           all arguments are either BaseDataTypes or
    **                           Resolvables.
    ** 004 ECF 20060404   @25365 Implement WrappedResource. Required for
    **                           method and attribute support.
    ** 005 ECF 20060419   @25613 Added support for deferred mode accumulators.
    **                           In this mode, the query signals to the
    **                           accumulator that a loop iteration has
    **                           occurred, and that the query has found a new
    **                           value to process, but accumulation is
    **                           deferred until there is an explicit trigger
    **                           to process accumulation, typically invoked by
    **                           business logic.
    ** 006 ECF 20060503   @25960 Added support for accumulation using external
    **                           data sources. Enhanced integration of break
    **                           group information.
    ** 007 ECF 20060504   @25972 Removed redundant interface. P2JQuery now
    **                           extends WrappedResource, so it is no longer
    **                           necessary to declare that we implement it
    **                           here.
    ** 008 ECF 20060912   @29597 Minor debugging improvement. Added index of
    **                           invalid argument to IllegalArgumentException
    **                           message in validateSubstitutionArguments().
    ** 009 ECF 20060925   @29960 Added safety check to argument validation.
    **                           If an argument is null and an error is
    **                           pending with the ErrorManager, an exception
    **                           is not raised. Added checkPendingError()
    **                           method for subclasses.
    ** 010 ECF 20061003   @30150 Rewrote checkPendingError() to release
    **                           buffers in the event of a pending error.
    **                           Renamed method to prepareFetch().
    ** 011 ECF 20061023   @30605 Abstracted errorIfNull logic up from
    **                           subclasses. Implement setErrorIfNull() from
    **                           P2JQuery interface. Added isErrorIfNull()
    **                           method for subclass use.
    ** 012 ECF 20061102   @30956 Implement new P2JQuery methods which support
    **                           browse capability. Specifically, a query
    **                           associated with a browse widget must fetch
    **                           the next record automatically when it is
    **                           repositioned. Also, a browse must be notified
    **                           of "asynchronous" reposition events.
    ** 013 ECF 20061119   @31322 Additional reposition support. Added methods
    **                           isFetchOnReposition() and
    **                           hasRepositionListener().
    ** 014 ECF 20061204   @31572 Added browsed flag and is/setBrowsed methods.
    **                           Indicates if query is associated with a UI
    **                           browse widget. This is necessary because the
    **                           query must behave differently when attached
    **                           to a browse.
    ** 015 ECF 20061206   @31584 Added parameter to afterReposition(). Allows
    **                           caller to override fetchOnReposition behavior
    **                           to expressly disallow record fetch.
    ** 016 SIY 20070502   @33428 Added notifyRepositionListeners() method.
    ** 017 ECF 20070716   @34506 Implement new methods required by P2JQuery
    **                           interface:
    **                              isStandalone()
    **                              setStandalone(boolean)
    **                              cleanup()
    ** 018 ECF 20070820   @34904 Added support for temporary silent error
    **                           mode. This allows query substitution parms to
    **                           be processed safely during query construction
    **                           and initialization.
    ** 019 SVL 20080418   @38037 Added the inverseSorting flag.
    ** 020 ECF 20080509   @38240 Added open() method. Necessary for converted
    **                           OPEN QUERY statements.
    ** 021 ECF 20080509   @38250 Fixed regression in open(). Removed check for
    **                           standalone flag.
    ** 022 SVL 20080514   @38276 reposition(1) if the query is browsed was
    **                           removed from open().
    ** 023 ECF 20080515   @38287 Rolled back #022 (@38276). The reposition is
    **                           necessary in cases where the query is
    **                           assigned to a QueryWrapper after that wrapper
    **                           was already registered to a browse widget. In
    **                           that case, we cannot rely on the browse
    **                           driving the reposition.
    ** 024 CA  20080624   @38891 Minor javadoc changes regarding standalone
    **                           field and accessors.
    ** 025 ECF 20080630   @39032 Minor memory optimization. Use Boolean
    **                           constants instead of instantiating new
    **                           Boolean objects.
    ** 026 ECF 20081106   @40319 Added default implementation of isOffEnd().
    **                           Uses _isOffEnd() as its backing worker.
    ** 027 CA  20081210   @40835 Implemented CLOSE QUERY statement.
    ** 028 CA  20090227   @41390 Added WrappedResources.isUnknown method; this
    **                           implementation returns false.
    ** 029 ECF 20090307   @42982 Fixed converted OPEN QUERY behavior. Added
    **                           loadRowAtCursor() and getOffEnd(), changed
    **                           afterReposition() to only fetch a record if the
    **                           query is browsed.
    ** 030 ECF 20090707   @43103 Added isRepositionNotificationActive(). Reports
    **                           whether we are currently notifying listeners of a
    **                           reposition event.
    ** 031 SVL 20091030   @44291 getOffEnd() function was made public in order to
    **                           support API change into P2JQuery.
    ** 032 SIY 20100303   @44701 Added hasAccumulators() helper method.
    ** 033 VMN 20130207          Added support for handle-based methods:
    **                           GET-BUFFER-HANDLE (KW_GET_BUFH)
    **                           GET-CURRENT (KW_GET_CUR),
    **                           QUERY-CLOSE (KW_QRY_CLOS),
    **                           REPOSITION-FORWARD (KW_REPOS_F),
    **                           REPOSITION-TO-ROW (KW_REPOS_2R)
    **                           REPOSITION-TO-ROWID (KW_REPOS_2I),
    **                           and attributes
    **                           NUM-RESULTS (KW_NUM_RES)
    ** 034 OM  20130212          Added implementation stubs for some dynamic methods.
    ** 035 VMN 20130213          Added support for attributes
    **                           INDEX-INFORMATION (KW_IDX_INFO)
    **                           FORWARD-ONLY (KW_FWD_ONLY)
    **                           PREPARE_STRING (KW_PREP_STR)
    ** 036 VMN 20130219          Added support for attributes
    **                           IS-OPEN (KW_IS_OPEN)
    ** 037 CA  20130221          Added support for UNIQUE-ID, ADM-DATA. Implemented Deletable. Added
    **                           HandleChain support.
    ** 038 OM  20130304          Refactored isValid and isUnknown of WrappedResource to valid
    **                           and unknown.
    ** 039 VMN 20130306          Added addBuffer(handle bufHandle) and corrected support
    **                           for handle-based methods:
    **                           GET-NEXT (KW_GET_NEXT)
    **                           GET-PREV (KW_GET_PREV)
    **                           GET-FIRST (KW_GET_1ST)
    **                           GET-LAST (KW_GET_LAST)
    **                           GET-CURRENT (KW_GET_CUR)
    **                           Implemented
    **                           getFirst(),getNext(),getLast(),getPrevious(),getCurrent()
    ** 040 CA  20130311          Added bufferHandle(integer).
    ** 041 VMN 20130311          Implemented queryClose, queryForward,
    **                           queryReposition, queryRepositionByID,
    **                           removed static methods
    ** 042 VMN 20130312          Corrected conversion and runtime for
    **                           REPOSITION-TO-ROW (KW_REPOS_2R)
    ** 043 CA  20130315          Added missing INDEX-INFORMATION API.
    ** 044 OM  20130320          Added implementation for ResultListHandler interface.
    ** 045 VMN 20130321          Corrected conversion and runtime for
    **                           NUM-BUFFERS attribute(KW_NUM_BUFF)
    ** 046 CA  20130322          Added missing queryForward(integer) API.
    ** 047 SVL 20130325          Added include and exclude functions.
    ** 048 CA  20130603          resourceDelete must return a boolean value.
    ** 049 CA  20130918          Added widget-pool support. The pool name must be validated before
    **                           anything else, and a null name means the default pool is used.
    ** 050 CA  20130927          Removed unknown() API, as by default the resource is known.
    ** 051 ECF 20130405          Moved createQuery static methods to QueryWrapper. Cleaned up some
    **                           return values.
    ** 052 CA  20131024          The legacy QUERY resource is implemented by QueryWrapper in P2J.
    **                           A block-level query is not compatible with the legacy QUERY resource,
    **                           thus the implementation for almost all QUERY-related APIs is moved to
    **                           QueryWrapper (what was not moved yet are the GET-FIRST/LAST/NEXT/PREV
    **                           /CURRENT implementations).
    ** 053 ECF 20131028          Import change.
    ** 054 VMN 20140330          Added LockType errors processing.
    ** 055 SVL 20140604          Implemented common logic for indexInformation() and
    **                           indexInformation(int).
    ** 056 SVL 20140925          Added deleteResultListEntry.
    ** 057 ECF 20141124          Added queryBackward methods.
    ** 058 SVL 20150331          Added deleteResultListEntry(boolean).
    ** 059 SVL 20150722          Fix related to current row selection for browsed queries.
    ** 060 OM  20150727          Fixed implementation of get(Next/Last/Prev/First/Current) methods.
    ** 061 ECF 20150701          Added optimize method; enabled database statistics collection;
    **                           replaced Apache commons logging with J2SE logging.
    ** 062 SVL 20150821          Added removeRepositionListener.
    ** 063 ECF 20150907          Added logic to preprocess query substitution parameters within
    **                           proper error handling and to detect query nesting.
    ** 064 OM  20150915          Added stub for SKIP-DELETED-RECORD attribute and NUM-RESULTS
    **                           function.
    ** 065 EVL 20160217          Clean up comments from symbols invalid for Solaris to compile.
    ** 066 SVL 20160314          Added parameterFilter.
    ** 067 ECF 20160725          Track external buffers referenced within a query. Implement Joinable
    **                           instead of P2JQuery, provide unsupported operation implementations
    **                           of some Joinable methods and some new P2JQuery methods.
    ** 068 IAS 20161012          Resolve XXXExpr instances (marked with QueryParam)
    ** 069 SVL 20170126          Added bufferHandle(), changed signature of
    **                           queryForward/queryBackward.
    ** 070 ECF 20171128          When collecting referenced buffers, include inverse buffer of a
    **                           natural join, if present.
    ** 071 GES 20171207          Switch to ErrorManager.silent().
    **     ECF 20171212          Rework query substitution parameter error handling.
    ** 072 OM  20171129          releaseBuffers() is implemented from P2JQuery.
    **     ECF 20171202          Added flag to optionally omit DMO aliases from the query's HQL.
    ** 073 ECF 20180201          Removed optimize method. Cleaned up inheritance hierarchy.
    ** 074 OM  20180220          Added outer-join attribute.
    ** 075 ECF 20180418          Added getExternalBuffers.
    ** 076 OM  20181031          Added BufferManager-level unknown mode support.
    ** 077 OM  20181206          Added support for dynamic evaluations and QueryOpen.
    ** 078 ECF 20190302          Renamed setExternalBuffers to addExternalBuffers and changed
    **                           implementation, since it may be called multiple times on a query.
    ** 079 SVL 20190320          Added createResultListEntry.
    ** 080 OM  20190327          Renamed DataSource to avoid conflicts with DataSet source.
    ** 081 CA  20190811          Do not use WeakList for openListeners - as they may get garbage
    **                           collected BEFORE they are used; and they also must live for the
    **                           duration of the query.
    **                           A dynamic query can be closed and then re-opened.
    ** 082 CA  20191009          Added where and sort clause translation in case of bound buffers.
    ** 083 SVL 20191218          Handled case when REPOSITION TO ROWID fails.
    ** 084 CA  20200428          Added 'missing' for exclude() and include() APIs (FIELDS/EXCEPT for
    **                           record-phrase).
    **     CA  20200503          Added QUERY:CACHE runtime stub.
    ** 085 ECF 20200906          New ORM implementation.
    ** 086 GES 20200731          Eliminated context-local lookups by using ErrorHelper.
    **     OM  20200915          Added reason of query navigation fatal error cases to log.
    **     CA  20200918          INDEX-INFORMATION requires to interpret the 'execute' method for a dynamic query
    **                           without evaluating the dynamic calls in the WHERE.
    **     CA  20210216          Arithmetic operations are always int64.
    **     OM  20210219          bufferHandle() accepts int64 expression parameters.
    **     CA  20210310          A dynamic predicate must set the default lock to NONE, instead of SHARE.
    **     ECF 20210723          Javadoc fix.
    **     ECF 20210806          Fixed nested query (7254) error handling.
    **     AL2 20210909          Added support for indexed reposition.
    **     OM  20210917          Code maintenance.
    **     OM  20220112          Added implementation of SKIP-DELETED-RECORD attribute.
    **     AL2 20220412          Do proxy checks for values used as query parameters.
    **     CA  20221109          Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded
    **                           as 'partial/incomplete'.
    **     OM  20221103          New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
    **     CA  20221130          Refactored the WHERE clause translation (when the bound and definition buffers
    **                           are not the same), to be aware of the external buffers (i.e. added for CAN-FIND
    **                           sub-select clauses).  The translate will be performed before the query is being
    **                           executed.
    **     CA  20221109          Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded
    **                           as 'partial/incomplete'.
    ** 087 CA  20230227          Fixed where clause translation when the external buffer's binding and definition
    **                           does not match.   Also, buffer match check must include the DMO alias.
    ** 088 CA  20230323          Prevent NPE if the field in EXCEPT/FIELDS can't be resolved in that buffer.
    ** 089 IAS 20230405          Fixed FILL support for recursive DATA-RELATION
    ** 090 AL2 20230411          Support query close listeners.
    ** 091 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
    ** 092 OM  20230511          Changed signature of FQLPreprocessor.translate().
    ** 093 RAA 20231010          Removed DatabaseStatistics usage.
    ** 094 AL2 20230828          Removed QueryOffEndException.
    ** 095 CA  20240320          Avoid BDTs within internal FWD runtime.  Replaced iterators with Java 'for',
    **                           where it applies.
    ** 096 CA  20240324          Use logical constants for TRUE, FALSE, UNKNOWN, within internal FWD runtime.
    ** 097 ES  20240404          Added new setIteration, isIteration, setPresort and isPresort methods.
    **                           A query has presort flag set if it a PresortQuery or it is a
    **                           component PresortCompoundQuery.
    ** 098 OM  20240424          Allow the expanded fields to be collected from FIELDS query option.
    ** 099 AL2 20240625          Query parameters that throw an exception on evaluation will now return ?.
    **     AL2 20240626          preprocessSubstitutionArguments should return true even if the parameter
    **                           evaluation fails.
    ** 100 CA  20240809          Removed capturing lambda from 'setUnknownMode'.
    ** 101 DDF 20240828          Added silent method equivalents to first, next, previous, last, unique, current
    **                           to avoid using ErrorManager.silent() which requires a lambda. Added errorHelper
    **                           property.
    ** 102 SP  20241016          Added caching of translateSort() output.
    ** 103 DDF 20230713          Added setForwardOnly() and isForwardOnly() methods.
    **     DDF 20230717          Added missing @Override annotation.
    ** 104 OM  20241128          Multi-tenant runtime support: selected the proper persistence context, eventually
    **                           based on [sharedDb] parameter.
    **                           Implementation of [setMultiTenantAlternative] method.
    ** 105 SP  20250108          Added dmo interfaces to the SortCacheKey.
    ** 106 AL2 20250416          Added state support for force-database-join.
    ** 107 CA  20250422          Added support for REPOSITION-BY-ID(rowid[]), where the arg is an extent.
    ** 108 AL2 20250521          Fetch on reposition due to browse should be done with NO_LOCK.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.persist;

    import java.util.*;
    import java.util.function.*;
    import java.util.logging.*;

    import com.goldencode.cache.*;
    import com.goldencode.p2j.persist.event.*;
    import com.goldencode.p2j.persist.lock.*;
    import com.goldencode.p2j.persist.orm.*;
    import com.goldencode.p2j.util.*;
    import com.goldencode.p2j.util.ErrorManager;
    import com.goldencode.p2j.util.ErrorManager.ErrorHelper;
    import com.goldencode.p2j.util.logging.*;

    /**
     * Abstract base class for all query implementation classes.  Implements the {@code Joinable}
     * interface and provides base services common to all query implementations.
     */
    public abstract class AbstractQuery
    implements P2JQuery
    {
       /** Logger */
       private static final CentralLogger LOG = CentralLogger.get(AbstractQuery.class.getName());

       /**
        * Indicates whether the query has the inverse sorting direction against
        * the original sorting. E.g.,
        * <pre>
        *  find next tt use-index si-name.
        *  find prev tt use-index si-name.
        * </pre>
        * will be converted to the code having a couple of AdaptiveFind queries:
        * <pre>
        *  AdaptiveFind query1 = new AdaptiveFind(tt, null, "tt.name asc");
        *  AdaptiveFind query2 = new AdaptiveFind(tt, null, "tt.name desc", true);
        * </pre>
        * and the second query will have the inverse sorting direction.
        */
       protected boolean inverseSorting = false;

       /** Flag indicating if this query was explicitly closed. */
       protected boolean closed = false;

       /** The filter that handles parameters passed to the query. */
       protected ParameterFilter parameterFilter = null;

       /** Flag indicating this query is associated with a dynamically prepared query resource. */
       protected boolean dynamicPredicate = false;

       /** Flag to indicate if this query is optimized using indexed reposition */
       protected boolean indexedReposition = false;

       /** Flag to indicate if this query should enforce database joins and bypass internal algorithm. */
       protected boolean forceDatabaseJoin = false;

       /** The included properties for each DMO (FIELDS option). */
       protected Map<DataModelObject, Property[]> included = null;

       /** The excluded properties for each DMO (EXCEPT option). */
       protected Map<DataModelObject, Property[]> excluded = null;

       /** Flag indicating fatal error was encountered processing query substitution parameters */
       private boolean fatalError = false;

       /** Should the failed first/last/unique retrievals raise error? */
       private boolean errorIfNull = true;

       /** <code>false</code> if running off end of results should raise error */
       protected boolean lenientOffEnd = false;

       /** Marks this as a left outer join component of the query. */
       protected boolean outer = false;

       /** Context-local error helper used for silent mode operations. */
       protected ErrorHelper errorHelper = null;

       /** Does this query stand alone or is it contained by a query wrapper ? */
       private boolean standalone = true;

       /** Should the next row be fetched after a reposition? */
       private boolean fetchOnReposition = false;

       /** Is query associated with a UI browse widget? */
       private boolean browsed = false;

       /** Corresponds FORWARD-ONLY attribute. */
       private boolean forwardOnly = false;

       /** Internal value of SKIP-DELETED-RECORD. */
       private boolean skipDeletedRecord = true;

       /** Flag indicating whether the query is used inside a FOR EACH block. */
       private boolean iterating = false;

       /** Flag indicating whether the BREAK BY statement is present */
       private boolean presort = false;

       /**
        * This flags brackets the code which is invoked by browse widgets for populating its rows. When active,
        * the deleted rows are not returned.
        */
       private boolean fillingBrowseRows = false;

       /** Accumulators added to this query, mapped to their deferred status */
       private LinkedHashMap<Accumulator, Boolean> accumulators = null;

       /** Registry of listeners who are notified of query repositions */
       private WeakList<RepositionListener> repoRegistry = null;

       /** Registry of listeners who are notified of query is about to (re-)open. */
       private List<QueryEventListener> eventListeners = null;

       /** Flag to avoid infinite recursion in reposition notification */
       private boolean notificationActive = false;

       /** Set of external buffers, if any, referenced by this query */
       private RecordBuffer[] externalBuffers = null;

       /** Set of external buffer definitions, if any, referenced by this query */
       private RecordBuffer[] externalBuffersDefs = null;

       /** Flag indicating that a DMO alias should not be used in this query's FQL statement */
       private boolean omitAlias = false;

       /** Flag indicating if {@link #afterReposition(boolean)} calls are skipped. */
       private boolean ignoreAfterRepo = false;

       /** The recursive DATA-RELATION the query is to FILL for */
       private DataRelation relation = null;

       /** Cache of translateSort output, indexed by bound/definition aliases and sort clause */
       private static LRUCache<AbstractQuery.SortCacheKey, String> sortCache;

       /**
        * Default constructor.
        */
       protected AbstractQuery()
       {

       }

       /**
        * Initializes the cache using CacheManager. The default value of the cache
        * is used when there is no size available from the configuration.
        */
       public static void initializeCache()
       {
          sortCache = CacheManager.createLRUCache(AbstractQuery.class, null, 2048);
       }

       /**
        * Implementation of NUM-RESULTS P4GL function.
        *
        * @param   queryName
        *          A {@link character} expression that evaluates to the name of a currently open,
        *          scrolling query.
        *
        * @return  the number of rows currently in the results list of a scrolling query. If {@code
        *          queryName} does not resolve to the name of a query, or if the query is not open or
        *          not scrolling, then the function returns the Unknown value ({@code ?}).
        */
       public static integer numResults(character queryName)
       {
          UnimplementedFeature.missing("NUM-RESULTS not implemented.");
          return new integer();
       }

       /**
        * Implementation of NUM-RESULTS P4GL function.
        *
        * @param   query
        *          A {@link character} expression that evaluates to the name of a currently open,
        *          scrolling query.
        *
        * @return  the number of rows currently in the results list of a scrolling query. If {@code
        *          queryName} does not resolve to the name of a query, or if the query is not open or
        *          not scrolling, then the function returns the Unknown value ({@code ?}).
        */
       public static integer numResults(P2JQuery query)
       {
          return query.size(); // getNumResults();
          // TODO: should return query.getNumResults(), query.size() returns current size of the
          //       cached results list, not the number of rows currently in the results list of a
          //       scrolling query.
       }

       /**
        * Iterate the array of query substitution parameters, if any, and resolve any which are
        * instances of <code>P2JQuery.Parameter</code> in silent error mode.
        * Those one which are instances of Resolvable are resolved in normal mode
        * <p>
        * An important secondary purpose of this method is to check whether we are processing a query
        * within the context of another query and raising an error condition if this is the case.
        * <p>
        * If an error is encountered resolving a parameter, the method will abort and set all values
        * in the {@code args} array to {@code null}. In this case, the method will return {@code
        * false} instead of {@code true}, and it is up to the caller to handle the {@code null}
        * arguments appropriately.
        * <p>
        * This error handling behavior differs from the 4GL, in which an error may preempt processing
        * some elements of the where clause, but continue evaluating others. This difference can
        * become significant if the processing of certain parameters has side effects (for example,
        * a user-defined function, which can do almost anything). This difference is a current
        * limitation of the FWD runtime.
        *
        * @param   bufMgr
        *          {@code BufferManager} instance for the current user context.
        * @param   isFindByRowid
        *          A lambda expression which should return {@code true} if the current database operation
        *          represents a legacy FIND by RECID/ROWID, else {@code false}.
        * @param   suppressNestedQueryError
        *          {@code true} to suppress (i.e., handle as a warning only, but do not alter control flow) a
        *          nested query error; {@code false} to allow such an error to alter control flow by permitting
        *          an {@code ErrorConditionException} thrown during nested query detection to propagate upward
        *          from this method.
        * @param   args
        *          Array of query substitution arguments, or {@code null} if there are none.
        *
        * @return  {@code true} if all arguments were resolved without error, {@code false} if any
        *          error occurred.
        *
        * @throws  ErrorConditionException
        *          if a query is executed within the WHERE clause of another query and such an error is not
        *          suppressed.
        */
       protected static boolean preprocessSubstitutionArguments(BufferManager bufMgr,
                                                                Supplier<Boolean> isFindByRowid,
                                                                boolean suppressNestedQueryError,
                                                                Object[] args)
       {
          // verify that we are not processing a query within another query; if not in silent error
          // mode and we are nested, this will raise an error condition (unless the inner query is a FIND by
          // RECID/ROWID
          try
          {
             bufMgr.checkNestedQuery(isFindByRowid);
          }
          catch (ErrorConditionException exc)
          {
             if (!suppressNestedQueryError)
             {
                throw exc;
             }
             else
             {
                return false;
             }
          }

          if (args == null)
          {
             return true;
          }

          boolean success = true;

          ErrorManager.ErrorHelper errHlp = bufMgr.getTxHelper().errHlp;

          int len = args.length;
          boolean silent = len > 0 && errHlp.isSilent();

          for (int i = 0; i < len; i++)
          {
             Object next = args[i];

             final int idx = i;

             bufMgr.incrementDepth();
             boolean unknownModeRestore = bufMgr.setUnknownMode(true);

             // make sure we work with the proper value and not a proxy
             Function<Object, Object> unwrapProxy = (value) ->
             {
                if (value instanceof BaseDataType && BaseDataType.isProxy((BaseDataType) value))
                {
                   return ((BaseDataType) value).val();
                }
                return value;
             };

             try
             {
                Runnable parm = null;

                if (next instanceof P2JQuery.Parameter)
                {
                   parm = () -> args[idx] = unwrapProxy.apply(((Parameter) next).resolve());
                }
                else if (next instanceof QueryParam && next instanceof Resolvable)
                {
                   parm = () -> args[idx] = unwrapProxy.apply(((Resolvable) next).resolve());
                }

                if (parm != null)
                {
                   if (!silent)
                   {
                      // implicitly run in silent mode to mimic the 4GL's eating of errors while
                      // processing the where clause
                      success = !errHlp.silent(parm);
                   }
                   else
                   {
                      try
                      {
                         // already in silent error mode; error may throw ErrorConditionException
                         parm.run();
                      }
                      catch (ErrorConditionException exc)
                      {
                         success = false;
                      }
                   }

                   if (bufMgr.isNestedQueryError())
                   {
                      if (silent)
                      {
                         // this will be recorded, not thrown; it represents a weird error state after a nested
                         // query error is raised when running silent
                         errHlp.addError(0, "", true, false);
                      }
                      success = false;
                   }

                   if (!success)
                   {
                      // clear pending error status -- we just need to eat the error and abort;
                      // this will not clear a 565 FIND error, which may happen after this point
                      errHlp.clearPending();

                      // continue processing; the 4GL engine not showing errors for bad constructs:
                      // i.e. f1 = entry(...) will compare f1 with ? even if the entry is using a bad index
                      args[idx] = new unknown();
                   }
                }
             }
             finally
             {
                bufMgr.setUnknownMode(unknownModeRestore);
                bufMgr.decrementDepth();
             }
          }

          // the processing was successful as the failed evaluation of arguments resulted in unknown values.
          // therefore, the query should still execute with unknown arguments
          return true;
       }

       /**
        * Translate this <code>sort</code> clause to use the bound DMO's alias and property names,
        * instead the definition (conversion-time) alias and property names.
        *
        * @param    bound
        *           The runtime-bound buffer.
        * @param    definition
        *           The buffer as it was used to generate the <code>sort</code> clause at conversion
        *           time.
        * @param    sort
        *           The sort clause to be translated.
        *
        * @return   The translated clause.
        */
       protected static String translateSort(RecordBuffer bound, RecordBuffer definition, String sort)
       {
          if (sort == null)
          {
             return null;
          }

          SortCacheKey sortCacheKey = new SortCacheKey(bound.getDMOAlias(),
                                                       definition.getDMOAlias(),
                                                       bound.getDMOInterface(),
                                                       definition.getDMOInterface(),
                                                       sort);
          String translatedSort;

          synchronized (sortCache)
          {
             translatedSort = sortCache.get(sortCacheKey);
          }

          if (translatedSort == null)
          {
             List<SortCriterion> defSort;
             try
             {
                defSort = SortCriterion.parse(sort, definition, false);
             }
             catch (PersistenceException e)
             {
                throw new RuntimeException(e);
             }

             translatedSort = translateSort(new RecordBuffer[]{bound},
                                            new RecordBuffer[]{definition},
                                            defSort);
             synchronized (sortCache)
             {
                sortCache.put(sortCacheKey, translatedSort);
             }
          }
          return translatedSort;
       }

       /**
        * Translate this <code>sort</code> clause to use the bound DMOs' alias and property names,
        * instead the definition (conversion-time) alias and property names.
        *
        * @param    bound
        *           The runtime-bound buffers.
        * @param    definition
        *           The buffers as it was used to generate the <code>sort</code> clause at conversion
        *           time.
        * @param    defSort
        *           The sort clause to be translated as a list of {@code SortCriterion}.
        *
        * @return   The translated clause.
        */
       protected static String translateSort(RecordBuffer[]      bound,
                                             RecordBuffer[]      definition,
                                             List<SortCriterion> defSort)
       {
          Map<String, Map<String, String>> mappingsByDefAlias = new HashMap<>();
          Map<String, String> boundAliasByDefAlias = new HashMap<>();

          for (int i = 0; i < definition.length; i++)
          {
             String defAlias = definition[i].getDMOAlias();
             mappingsByDefAlias.put(defAlias, definition[i].getBoundPropertyMappings());
             boundAliasByDefAlias.put(defAlias, bound[i].getDMOAlias());
          }

          StringBuilder boundSort = new StringBuilder();
          for (SortCriterion sc : defSort)
          {
             String alias;
             String prop;

             if (boundAliasByDefAlias.containsKey(sc.getAlias()))
             {
                prop = mappingsByDefAlias.get(sc.getAlias()).get(sc.getUnqualifiedName());
                alias = boundAliasByDefAlias.get(sc.getAlias());
             }
             else
             {
                prop = sc.getUnqualifiedName();
                alias = sc.getAlias();
             }

             String originalName = sc.getOriginalName();
             String subscript = "";
             int subscriptIndex = originalName.indexOf('[');
             if (subscriptIndex > 0)
             {
                subscript = originalName.substring(subscriptIndex, originalName.indexOf(']') + 1);
             }

             if (boundSort.length() != 0)
             {
                boundSort.append(", ");
             }
             boundSort.append(alias).append(".")
                      .append(prop)
                      .append(subscript)
                      .append(sc.isAscending() ? "" : " desc");
          }

          return boundSort.toString();
       }

       /**
        * Translate this <code>where</code> clause to use the bound DMO's alias and property names,
        * instead the definition (conversion-time) alias and property names.
        *
        * @param    bound
        *           The runtime-bound buffers.
        * @param    definition
        *           The buffers as it was used to generate the <code>where</code> clause at conversion
        *           time.
        * @param    where
        *           The HQL to be translated.
        *
        * @return   The translated HQL.
        */
       protected String translateWhere(List<RecordBuffer> bound, List<RecordBuffer> definition, String where)
       {
          if (where == null)
          {
             return null;
          }

          if (this.externalBuffers != null)
          {
             for (int i = 0; i < this.externalBuffers.length; i++)
             {
                bound.add(this.externalBuffers[i]);
                definition.add(this.externalBuffersDefs[i]);
             }
          }

          return FQLPreprocessor.translate(bound, definition, where, false);
       }

       /**
        * Check if the two buffers differ.
        *
        * @param    buf
        *           The first buffer.
        * @param    defBuf
        *           The second buffer.
        *
        * @return   <code>true</code> if the buffers are different instances or have different
        *           backing DMO interface.
        */
       protected static boolean buffersMatch(RecordBuffer buf, RecordBuffer defBuf)
       {
          return buf == defBuf ||
                 (buf.getDMOInterface() == defBuf.getDMOInterface() &&
                  buf.getDMOAlias().equals(defBuf.getDMOAlias()));
       }

       /**
        * Return the {@link LockType} instance for the given internal lock and nowait with corresponding query
        * processing.
        * <p>
        *
        * @param   lock
        *          The lock type as an integer value (see lock type constants in {@code LockType}).
        * @param   nowait
        *          The wait type as an integer value (see wait type constants in {@code LockType}).
        *
        * @return  {@code LockType} instance.
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       private static LockType fromLockNowait(long lock, long nowait)
       throws ErrorConditionException
       {
          LockType lockType = null;
          try
          {
             lockType = LockType.fromLockNowait(lock, nowait);
          }
          catch (LockTypeException e)
          {
             String err;
             int num;
             if (e.isNowaitCorrect())
             {
                err = "The first argument to QUERY GET methods must be NO-LOCK, SHARE-LOCK or EXCLUSIVE-LOCK";
                num = 7314;
             }
             else
             {
                err = "QUERY GET second argument must be NO-WAIT or 0";
                num = 7360;
             }
             ErrorManager.recordOrThrowError(num, err);
          }
          return lockType;
       }

       /**
        * Set this query in a 'dynamic' mode, to mark the fact that it was used as a dynamic delegate in a query
        * resource.
        *
        * @param    dynamic
        *           Flag indicating that this is a dynamic delegate for a query resource.
        */
       @Override
       public void setDynamicPredicate(boolean dynamic)
       {
          this.dynamicPredicate = dynamic;
       }

       /**
        * Check if this query is originating from a dynamic predicate.
        *
        * @return   The {@link #dynamicPredicate} value.
        */
       @Override
       public boolean isDynamicPredicate()
       {
          return dynamicPredicate;
       }

       /**
        * Enable results list scrolling/repositioning.  This default
        * implementation does nothing.
        */
       public void setScrolling()
       {
       }

       /**
        * Set the behavior of this query as a record retrieval request moves off
        * the end of its results.  If set to lenient mode, invocations of
        * <code>next</code> and <code>previous</code> which do not find a record
        * do not raise a <code>QueryOffEndException</code>.  By default, this
        * would normally raise the exception.
        *
        * @param   lenientOffEnd
        *          <code>true</code> to suppress <code>QueryOffEndException</code>
        *          for <code>next</code> and <code>previous</code> requests;
        *          <code>false</code> to permit these exceptions.
        */
       public void setLenientOffEnd(boolean lenientOffEnd)
       {
          this.lenientOffEnd = lenientOffEnd;
       }

       /**
        * Indicate whether this query will throw a
        * <code>QueryOffEndException</code> when it runs off the end of its
        * results.
        *
        * @return  <code>true</code> if an exception will be thrown, else
        *          <code>false</code>.
        */
       protected boolean isLenientOffEnd()
       {
          return lenientOffEnd;
       }

       /**
        * Enforce a database join if possible. By default, multi-table queries rely on
        * algorithm to decide whether a database join is optimal or not. With such setter,
        * the database join is always preferred.
        *
        * @param   forceDatabaseJoin
        *          {@code true} if the query should always use DB joins.
        */
       @Override
       public void setForceDatabaseJoin(logical forceDatabaseJoin)
       {
          this.forceDatabaseJoin = forceDatabaseJoin.booleanValue();
       }

       /**
        * Check if the database join enforcing is active.
        *
        * @return   {@code true} if the query will always use DB joins.
        */
       @Override
       public logical isForceDatabaseJoin()
       {
          return logical.of(this.forceDatabaseJoin);
       }

       /**
        * This flag is used in order to allow reposition to unflushed records (ones
        * which were not in the database when the query was opened). This can be set
        * only when the query is scrolling. When a reposition to an unflushed record
        * is made, the result list is reset.
        * This method must be invoked before any results are retrieved.
        */
       @Override
       public void setIndexedReposition()
       {
          indexedReposition = true;
       }

       /**
        * Disable indexed reposition optimization to disallow repositioning to
        * unflushed records.
        * This method must be invoked before any results are retrieved.
        */
       @Override
       public boolean isIndexedReposition()
       {
          return indexedReposition;
       }

       /**
        * Reposition the prepared query to the first result if the query is being
        * browsed.
        */
       public void open()
       {
          closed = false;

          if (browsed)
          {
             reposition(1);
          }
       }

       /**
        * Indicate whether the cursor has run off one or the other end of the
        * associated query's list of available results.  If <code>true</code>,
        * this indicates that the query cannot produce any more results in the
        * current scroll direction.
        *
        * @return  <code>true</code> if the cursor is off either end of its
        *          query's results list.
        */
       public logical isOffEnd()
       {
          boolean result = _isOffEnd();

          return (logical.of(result));
       }

       /**
        * Add an accumulator to this query.  The accumulator expects to be
        * notified whenever a new result row is accessed (during sequential row
        * access).
        * <p>
        * This implementation assumes the accumulator is not associated with a
        * break group.
        *
        * @param   accum
        *          Accumulator to be registered with this query.
        */
       public void addAccumulator(Accumulator accum)
       {
          addAccumulator(accum, true);
       }

       /**
        * Add an accumulator to this query.  The accumulator expects to be notified whenever a new result row is
        * accessed (during sequential row access).
        * <p>
        * This implementation assumes the accumulator is not associated with a break group.
        *
        * @param   accum
        *          Accumulator to be registered with this query.
        * @param   deferred
        *          If {@code true}, calls to {@link #accumulate()} will not cause the accumulator to recalculate
        *          immediately, but instead to update the accumulator's state such that the next invocation of
        *          {@link Accumulator#accumulate()} will trigger the actual accumulation update.
        *          If {@code false}, the accumulator recalculates its internal state immediately.
        */
       public void addAccumulator(Accumulator accum, boolean deferred)
       {
          if (accumulators == null)
          {
             // Use sequential map to preserve add order.
             accumulators = new LinkedHashMap<>();
          }

          accumulators.put(accum, deferred ? Boolean.TRUE : Boolean.FALSE);
       }

       /**
        * Is this query set to fetch the next record after a reposition?
        *
        * @return  <code>true</code> if set to fetch after reposition, else
        *          <code>false</code>.
        */
       public boolean isFetchOnReposition()
       {
          return fetchOnReposition;
       }

       /**
        * Set this query to automatically fetch the next record after a
        * reposition, or turn this feature off.
        *
        * @param   enable
        *          <code>true</code> to enable automatic fetching;
        *          <code>false</code> to disable.
        */
       public void setFetchOnReposition(boolean enable)
       {
          fetchOnReposition = enable;
       }

       /**
        * Adds a listener to be notified when this query is open.
        * <p>
        * Note that this method is only called for dynamic queries only (for the moment).
        *
        * @param   listener
        *          The listener to be notified.
        */
       @Override
       public void addQueryEventListener(QueryEventListener listener)
       {
          if (eventListeners == null)
          {
             eventListeners = new LinkedList<>();
          }

          eventListeners.add(listener);
       }

       /**
        * Notifies the registered QUERY-OPEN listeners that the event is about to happen.
        *
        * @param    evaluate
        *           Flag indicating that the dynamic query must evaluate its dynamic calls (like DYNAMIC-FUNCTION)
        *           in the WHERE clause.
        */
       @Override
       public void notifyQueryOpenListeners(boolean evaluate)
       {
          if (eventListeners != null)
          {
             Iterator<QueryEventListener> iter = eventListeners.iterator();
             while (iter.hasNext())
             {
                iter.next().onQueryOpen(evaluate);
             }
          }
       }

       /**
        * Notifies the registered QUERY-CLOSE listeners that the event is about to happen.
        */
       @Override
       public void notifyQueryCloseListeners()
       {
          if (eventListeners != null)
          {
             Iterator<QueryEventListener> iter = eventListeners.iterator();
             while (iter.hasNext())
             {
                iter.next().onQueryClose();
             }
          }
       }

       /**
        * Register a listener to receive notifications reposition events for this
        * query.
        *
        * @param   listener
        *          Reposition listener to be registered.
        */
       public void addRepositionListener(RepositionListener listener)
       {
          if (repoRegistry == null)
          {
             repoRegistry = new WeakList<>();
          }

          repoRegistry.add(listener);
       }

       /**
        * Unregister a listener that was receiving notifications on reposition events for this
        * query.
        *
        * @param   listener
        *          Reposition listener to be unregistered.
        */
       public void removeRepositionListener(RepositionListener listener)
       {
          if (repoRegistry != null)
          {
             repoRegistry.remove(listener);
          }
       }

       /**
        * Indicate to this query that whether it is associated with a browse widget or not.
        *
        * @param   browsed
        *          {@code true} to indicate the query has an associated browse widget, else {@code false}.
        */
       public void setBrowsed(boolean browsed)
       {
          this.browsed = browsed;
          if (browsed)
          {
             skipDeletedRecord = false;
          }
       }

       /**
        * Informs the query that the next operations will be performed for filling browse rows, so it should not
        * return deleted rows/records (if argument is {@code true}). That is, temporarily overwrite the
        * SKIP-DELETED-RECORDS until the flag is turned off.
        *
        * @param   on
        *          if {@code true} the deleted rows will not be returned because the query is managed by the
        *          browse widget.
        */
       @Override
       public void setFillingBrowseRows(boolean on)
       {
          fillingBrowseRows = on;
       }

       /**
        * Set the forward-only attribute to the given value.
        *
        * @param   value
        *          <code>true</code> to enable forward-only, <code>false</code> to disable it.
        */
       @Override
       public void setForwardOnly(boolean value)
       {
          forwardOnly = value;
       }

       /**
        * Method that returns the value of the forwardOnly flag.
        *
        * @return  <code>true</code> if forward-only is enabled, <code>false</code> otherwise.
        */
       @Override
       public boolean isForwardOnly()
       {
          return forwardOnly;
       }

       /**
        * Notify all registered listeners that query is repositioned or closed.
        *
        * @param   closed
        *          A <code>true</code> value will inform all listeners that the query was explicitly
        *          closed.
        * @param   error
        *          <code>true</code> if an error happened during reposition.
        * @param   targetRepositionRow
        *          Row used as the target for reposition. If it was deleted, reposition with fetching
        *          may end up on a different row.
        */
       @Override
       public void notifyRepositionListeners(boolean closed, boolean error, int targetRepositionRow)
       {
          if (repoRegistry != null)
          {
             Iterator<RepositionListener> iter = repoRegistry.iterator();
             while (iter.hasNext())
             {
                RepositionListener next = iter.next();
                if (closed)
                {
                   next.queryClosed();
                }
                else
                {
                   next.queryRepositioned(error, targetRepositionRow);
                }
             }
          }
       }

       /**
        * Indicate whether this query stands alone, or is wrapped by a query
        * wrapper.
        *
        * @return  <code>true</code> if standalone, else <code>false</code>.
        */
       public boolean isStandalone()
       {
          return standalone;
       }

       /**
        * Change the standalone nature of this query.  A query is standalone by
        * default, but can be changed to non-standalone by the wrapper which
        * contains it.
        *
        * @param   standalone
        *          <code>true</code> to set query as standalone;
        *          <code>false</code> to set query as contained.
        */
       public void setStandalone(boolean standalone)
       {
          this.standalone = standalone;
       }

       /**
        * Clean up resources associated with this query when its scope ends.
        * <p>
        * This default implementation does nothing.
        */
       public void cleanup()
       {
       }

       /**
        * Explicitly close the prepared query.
        * <p>
        * This default implementation calls {@link #cleanup()}.
        */
       public void close()
       {
          cleanup();
          closed = true;
       }

       /**
        * Sets the left-outer-join property for this query.
        *
        * @param   outer
        *          {@code true} iif this is a left outer join component of the query.
        */
       public void setOuter(boolean outer)
       {
          this.outer = outer;
       }

       /**
        * Indicate whether the query is of presort type.
        *
        * @return  <code>true</code> if the query is of presort type..
        */
       public boolean isPresort()
       {
          return presort;
       }

       /**
        * Indicate whether the query is of presort type.
        *
        * @param   presort
        *          <code>true</code> indicating that a query of presort type.
        *          <code>false</code> is not of presort type.
        */
       public void setPresort(boolean presort)
       {
          this.presort = presort;
       }

       /**
        * Indicate whether the query is defined inside the scope of FOR EACH block.
        *
        * @param   iterating
        *          <code>true</code> indicating that a query is inside FOR EACH block;
        *          <code>false</code> is defined outside of a FOR EACH.
        */
       public void setIterating(boolean iterating)
       {
          this.iterating = iterating;
       }

       /**
        * Indicate whether the query is defined inside the scope of FOR EACH block.
        *
        * @return  <code>true</code> if the query was defined inside a FOR EACH block.
        */
       public boolean isIterating()
       {
          return iterating;
       }

       /**
        * Set a mode such that setting a null record into a backing buffer may or
        * may not result in an error condition.  This applies only to requests to
        * retrieve <code>FIRST</code>, <code>LAST</code>, and <code>UNIQUE</code>
        * records.
        *
        * @param   errorIfNull
        *          If <code>true</code>, setting a <code>null</code> value as the
        *          current record in an underlying buffer will raise an error
        *          condition (if not in silent error mode); if <code>false</code>,
        *          this action will raise an end condition instead.
        */
       public void setErrorIfNull(boolean errorIfNull)
       {
          this.errorIfNull = errorIfNull;
       }

       /**
        * Report whether this query is associated with a browse widget.
        *
        * @return  <code>true</code> if associated with a browse, else
        *          <code>false</code>.
        */
       public boolean isBrowsed()
       {
          return browsed;
       }

       /**
        * Add one or more buffers to the list of those which this query references externally. These
        * typically will be buffers referenced by converted CAN-FIND expressions embedded in the
        * query's FQL expression, which are not the primary buffers associated with the query.
        *
        * @param   dmos
        *          One or more external buffers.
        *
        * @return  This query instance.
        */
       public P2JQuery addExternalBuffers(DataModelObject... dmos)
       {
          int len;
          if (dmos != null && (len = dmos.length) > 0)
          {
             int offset = 0;

             if (externalBuffers != null)
             {
                offset = externalBuffers.length;
                RecordBuffer[] list = new RecordBuffer[offset + len];
                RecordBuffer[] listDef = new RecordBuffer[offset + len];
                for (int i = 0; i < offset; i++)
                {
                   list[i] = externalBuffers[i].buffer();
                   listDef[i] = externalBuffersDefs[i].definition();
                }
                externalBuffers = list;
                externalBuffersDefs = listDef;
             }
             else
             {
                externalBuffers = new RecordBuffer[len];
                externalBuffersDefs = new RecordBuffer[len];
             }

             for (int i = 0; i < len; i++)
             {
                externalBuffers[i + offset] = ((BufferReference) dmos[i]).buffer();
                externalBuffersDefs[i + offset] = ((BufferReference) dmos[i]).definition();
             }
          }

          return this;
       }

       /**
        * Get an array of all record buffers referenced by this query, including those directly
        * involved in the query, as well as those (if any) indirectly referenced by inlined,
        * converted, CAN-FIND expressions, and that which represents the inverse buffer of a natural
        * join.
        *
        * @return  All record buffers referenced by this query.
        */
       public RecordBuffer[] getReferencedBuffers()
       {
          RecordBuffer[] qbufs = getRecordBuffers();

          AbstractJoin join = getJoin();

          if (externalBuffers == null && join == null)
          {
             return qbufs;
          }

          int len = qbufs.length;

          if (externalBuffers != null)
          {
             len += externalBuffers.length;
          }

          if (join != null)
          {
             len++;
          }

          RecordBuffer[] all = new RecordBuffer[len];

          int limit = qbufs.length;

          int i = 0;
          while (i < limit)
          {
             all[i] = qbufs[i];
             i++;
          }

          if (externalBuffers != null)
          {
             int start = i;
             limit += externalBuffers.length;
             while (i < limit)
             {
                all[i] = externalBuffers[i - start];
                i++;
             }
          }

          if (join != null)
          {
             all[i] = join.getInverse().buffer();
          }

          return all;
       }

       /**
        * Retrieve the context-local error helper used for silent mode operations.
        *
        * @return  The context-local error helper used for silent mode operations.
        */
       protected ErrorHelper getErrorHelper()
       {
          if (errorHelper == null)
          {
             errorHelper = ErrorManager.getErrorHelper();
          }

          return errorHelper;
       }

       /**
        * Get the default lock type for this query.
        *
        * @return   {@link LockType#NONE} if this is a dynamic delegate for a query resource, or
        *           {@link LockType#SHARE} otherwise.
        */
       protected LockType getDefaultLock()
       {
          return dynamicPredicate ? LockType.NONE: LockType.SHARE;
       }

       /**
        * Get the external buffers, if any, associated with this query.
        *
        * @return  Array of external buffers or {@code null}.
        */
       protected RecordBuffer[] getExternalBuffers()
       {
          return externalBuffers;
       }

       /**
        * Get the external buffers definitions, if any, associated with this query.
        *
        * @return  Array of external buffers definitions or {@code null}.
        */
       protected RecordBuffer[] getExternalBuffersDefs()
       {
          return externalBuffersDefs;
       }

       /**
        * Indicate whether there was a fatal error during query substitution parameter processing
        * which should abort this query at the earliest appropriate time.
        *
        * @return  {@code true} if there was a query substitution parameter processing error, else
        *          {@code false}.
        */
       protected boolean isFatalError()
       {
          return fatalError;
       }

       /**
        * Remember there was a fatal error during query substitution parameter processing and that
        * this query should abort at the earliest appropriate time.
        */
       protected void setFatalError()
       {
          fatalError = true;
       }

       /**
        * Indicate whether failed <code>FIRST</code>, <code>LAST</code>, and
        * <code>UNIQUE</code> retrieval attempts should be considered an error
        * condition.
        *
        * @return  <code>true</code> if failed first/last/unique retrievals
        *          should be considered an error;  <code>false</code> is such
        *          failures should be considered a query off-end condition.
        */
       protected boolean isErrorIfNull()
       {
          return errorIfNull;
       }

       /**
        * Determine whether DMO property references in this query's FQL should be unqualified by a
        * DMO alias name.
        *
        * @return  {@code true} if the DMO alias should be omitted from property references, else
        *          {@code false}.
        */
       protected boolean isOmitAlias()
       {
          return omitAlias;
       }

       /**
        * Specify whether DMO property references in this query's FQL should be unqualified by a DMO
        * alias name.
        *
        * @param   omitAlias
        *          {@code true} if the DMO alias should be omitted from property references, else
        *          {@code false}.
        */
       protected void setOmitAlias(boolean omitAlias)
       {
          this.omitAlias = omitAlias;
       }

       /**
        * This method should be called by subclasses after any reposition of the
        * query.  If the query is set to fetch a record on reposition events, it
        * does so now (using the {@link #next} method).  If any listeners are
        * registered to receive notifications of reposition events, they are
        * notified now.
        *
        * @param   permitFetch
        *          <code>true</code> to permit immediate record fetch;
        *          <code>false</code> to override <code>fetchOnReposition</code>
        *          flag and explicitly disallow immediate record fetch.
        */
       protected void afterReposition(boolean permitFetch)
       {
          afterReposition(permitFetch, !permitFetch);
       }

       /**
        * This method should be called by subclasses after any reposition of the
        * query.  If the query is set to fetch a record on reposition events, it
        * does so now (using the {@link #next} method).  If any listeners are
        * registered to receive notifications of reposition events, they are
        * notified now.
        *
        * @param   permitFetch
        *          <code>true</code> to permit immediate record fetch;
        *          <code>false</code> to override <code>fetchOnReposition</code>
        *          flag and explicitly disallow immediate record fetch.
        * @param   error
        *          <code>true</code> if error happened during reposition.
        */
       protected void afterReposition(boolean permitFetch, boolean error)
       {
          if (ignoreAfterRepo)
          {
             return;
          }

          integer row = currentRow();
          int targetRepositionRow = row.isUnknown() ? -1 : row.intValue() - 1;

          if (permitFetch && fetchOnReposition)
          {
             if (isBrowsed())
             {
                next(LockType.NONE);
             }
             else
             {
                loadRowAtCursor();
             }
          }

          // Prevent recursive notifications.
          if (isRepositionNotificationActive())
          {
             return;
          }

          notificationActive = true;
          notifyRepositionListeners(false, error, targetRepositionRow);
          notificationActive = false;
       }

       /**
        * This method should be called by subclasses after any reposition of the
        * query.  If the query is set to fetch a record on reposition events, it
        * does so now (using the {@link #next} method).  If any listeners are
        * registered to receive notifications of reposition events, they are
        * notified now.<p>
        * This method also brings the query to the required state if reposition
        * has failed.
        *
        * @param   permitFetch
        *          <code>true</code> to permit immediate record fetch;
        *          <code>false</code> to override <code>fetchOnReposition</code>
        *          flag and explicitly disallow immediate record fetch.
        * @param   oldCurrentRow
        *          Current row at the start of the reposition. Can be <code>unknown</code>.
        */
       protected void afterReposition(boolean permitFetch, integer oldCurrentRow)
       {
          if (!permitFetch)
          {
             // error during reposition
             integer backupRow = isBrowsed() ? oldCurrentRow : new integer(MathOps.plus(size(), 1));
             if (!backupRow.isUnknown())
             {
                ignoreAfterRepo = true;
                try
                {
                   reposition(backupRow);
                }
                finally
                {
                   ignoreAfterRepo = false;
                }
             }

             if (isBrowsed())
             {
                afterReposition(true, true);
             }
             else
             {
                releaseBuffers();
                afterReposition(false);
             }
          }
          else
          {
             afterReposition(true);
          }
       }

       /**
        * Indicate if we are currently notifying listeners of a reposition event.
        *
        * @return  <code>true</code> if a reposition notification is active, else
        *          <code>false</code>.
        */
       protected boolean isRepositionNotificationActive()
       {
          return notificationActive;
       }

       /**
        * Synchronize the query with the cursor by causing the query to load the
        * current row's results (if any) into its backing buffer(s).
        * <p>
        * This implementation does nothing;  it should be overridden to implement
        * this behavior as needed.
        */
       protected void loadRowAtCursor()
       {
       }

       /**
        * Notify accumulators attached to this query (if any) that a new result
        * row has been accessed, and that they should update their internal state.
        * Subclasses should invoke this method whenever an iteration of a query
        * result occurs, such as on the retrieval of the first, last, next, or
        * previous result row.
        */
       protected final void accumulate()
       {
          if (!hasAccumulators())
          {
             return;
          }

          for (Map.Entry<Accumulator, Boolean> next : accumulators.entrySet())
          {
             Accumulator accum = next.getKey();
             Boolean deferred = next.getValue();

             accumulate(accum, deferred);
          }
       }

       /**
        * Check if query has attached accumulators.
        *
        * @return  <code>true</code> if accumulators attached to the query.
        */
       protected boolean hasAccumulators()
       {
          return accumulators != null && !accumulators.isEmpty();
       }

       /**
        * Subclasses which support the concept of break groups in query results
        * should override this method to report whether the current result row
        * being visited represents a new break group, for the given break group
        * key.
        *
        * @param   key
        *          Resolvable object which is used as the key to identify the
        *          break group category being tested.
        *
        * @return  <code>true</code> if the current result represents the first
        *          entry in a new break group;  else <code>false</code>.  This
        *          default implementation always returns <code>false</code>.
        */
       protected boolean isNewBreakGroup(Resolvable key)
       {
          return false;
       }

       /**
        * Determine whether the query result row currently being visited is the
        * last row within a break group whose category is identified by the
        * specified resolvable object.
        *
        * @param   key
        *          Break group category key.  May be <code>null</code> (which
        *          should always trigger a <code>false</code> return).
        *
        * @return  <code>false</code>;  subclasses should override this method if
        *          a different result is required.
        */
       protected boolean isLastOfBreakGroup(Resolvable key)
       {
          return false;
       }

       /**
        * Prepare to perform a fetch.  This involves checking for a pending error
        * in the error manager and releasing the current buffer record if an
        * error is pending.  Releasing the record may result in an exception
        * being thrown by the record buffer.
        *
        * @return  <code>false</code> if an error is pending, else
        *          <code>true</code>.
        */
       protected boolean prepareFetch()
       {
          if (fatalError)
          {
             try
             {
                releaseBuffers();
             }
             catch (QueryOffEndException exc)
             {
                // intentionally ignored
             }

             if (LOG.isLoggable(Level.FINE))
             {
                String msg = "Fatal error aborted query: " + ErrorManager.getErrorText(1).toStringMessage();
                LOG.log(Level.FINE, msg, new ErrorConditionException(msg));
             }

             return false;
          }

          return true;
       }

       /**
        * Default, no-op implementation which should be overridden by subclasses
        * which need to release the current record of each of their buffers in
        * response to a fetch request while an error is pending in the error
        * manager.
        */
       public void releaseBuffers()
       {
       }

       /**
        * Invoke the appropriate <code>iterate</code> method variant on the
        * specified accumulator.  If the accumulator manages only a primary
        * statistic (i.e., no break groups have been added), the {@link
        * com.goldencode.p2j.util.Accumulator#iterate(boolean) simple form}
        * is invoked.  If break groups have been added to the accumulator, the
        * {@link com.goldencode.p2j.util.Accumulator#iterate(java.util.Map,
        * boolean) more complex form} is invoked.
        *
        * @param   accum
        *          Target accumulator object.
        * @param   deferred
        *          <code>true</code> if the accumulator was registered to this
        *          query as a deferred accumulator, else <code>false</code>.
        *
        * @see     #addAccumulator
        */
       private void accumulate(Accumulator accum, boolean deferred)
       {
          Iterator<Resolvable> iter = accum.breakGroupKeys();

          // Iterate the accumulator.
          if (!iter.hasNext())
          {
             accum.iterate(deferred);
          }
          else
          {
             Map<Resolvable, Boolean> breakMap = new HashMap<>();
             while (iter.hasNext())
             {
                Resolvable key = iter.next();
                Boolean b = (isNewBreakGroup(key) ? Boolean.TRUE : Boolean.FALSE);
                breakMap.put(key, b);
             }
             accum.iterate(breakMap, deferred);
          }

          // Update the accumulator's "last break group" state.
          Resolvable key = accum.getFirstBreakGroupKey();
          accum.setLastOfGroup(isLastOfBreakGroup(key));
       }

       /**
        * Conversion of GET-BUFFER-HANDLE() method (KW_GET_BUFH).
        *
        * Returns a handle of the first buffer of a query object.
        *
        * @return buffer handle
        */
       public handle bufferHandle()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of GET-BUFFER-HANDLE() method (KW_GET_BUFH).
        *
        * Returns a handle of a particular buffer of a query object.
        *
        * @param   bufferName
        *          buffer-name. A CHARACTER expression that evaluates to the name
        *          of a buffer in the query or DataSet object.
        *
        * @return buffer handle
        */
       public handle bufferHandle(String bufferName)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of GET-BUFFER-HANDLE() method (KW_GET_BUFH).
        *
        * Returns a handle of a particular buffer of a query object.
        *
        * @param   bufferName
        *          buffer-name. A CHARACTER expression that evaluates to the name
        *          of a buffer in the query or DataSet object.
        *
        * @return buffer handle
        */
       public handle bufferHandle(character bufferName)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of GET-BUFFER-HANDLE() method (KW_GET_BUFH).
        *
        * Returns a handle of a particular buffer of a query object.
        *
        * @param   bufferSequenceNumber
        *          buffer-sequence-number. An INTEGER that represents the sequence
        *          number of the desired buffer.
        *
        * @return buffer handle
        */
       public handle bufferHandle(int bufferSequenceNumber)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of GET-BUFFER-HANDLE() method (KW_GET_BUFH).
        *
        * Returns a handle of a particular buffer of a query object.
        *
        * @param   bufferSequenceNumber
        *          buffer-sequence-number. An INTEGER that represents the sequence
        *          number of the desired buffer.
        *
        * @return buffer handle
        */
       @Override
       public handle bufferHandle(int64 bufferSequenceNumber)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of QUERY-CLOSE() method (KW_QRY_CLOS).
        *
        * Closes a query object.
        *
        * @return  <code>true</code> if QUERY-CLOSE() succeeds,
        *          else <code>false</code>.
        */
       public logical queryClose()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-BACKWARD() method (KW_REPOS_B).
        *
        * Moves a query object's result list pointer backward a particular number
        * of rows.
        *
        * @return  <code>true</code> if REPOSITION-BACKWARD() succeeds,
        *          else <code>false</code>.
        */
       public logical queryBackward(NumberType rows)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-BACKWARD() method (KW_REPOS_B).
        *
        * Moves a query object's result list pointer backward a particular number
        * of rows.
        *
        * @return  <code>true</code> if REPOSITION-BACKWARD() succeeds,
        *          else <code>false</code>.
        */
       public logical queryBackward(int rows)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-FORWARD() method (KW_REPOS_F).
        *
        * Moves a query object's result list pointer forward a particular number
        * of rows.
        *
        * @return  <code>true</code> if REPOSITION-FORWARD() succeeds,
        *          else <code>false</code>.
        */
       public logical queryForward(NumberType rows)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-FORWARD() method (KW_REPOS_F).
        *
        * Moves a query object's result list pointer forward a particular number
        * of rows.
        *
        * @return  <code>true</code> if REPOSITION-FORWARD() succeeds,
        *          else <code>false</code>.
        */
       public logical queryForward(int rows)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-TO-ROW() method (KW_REPOS_2R).
        *
        * Moves a query object's result list pointer to the row corresponding to the specified
        * sequence number.
        *
        * @param   n
        *          An integer expression representing the sequence number.
        *
        * @return  <code>true</code> if REPOSITION-TO-ROW() succeeds,
        *          else <code>false</code>.
        */
       public logical queryReposition(int n)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-TO-ROW() method (KW_REPOS_2R).
        *
        * Moves a query object's result list pointer to the row corresponding to the specified
        * sequence number.
        *
        * @param   n
        *          An integer expression representing the sequence number.
        *
        * @return  <code>true</code> if REPOSITION-TO-ROW() succeeds,
        *          else <code>false</code>.
        */
       public logical queryReposition(NumberType n)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-TO-ROWID() method (KW_REPOS_2I).
        *
        * Reposition the cursor such that a request to retrieve the next result
        * will retrieve the result which matches the specified array of primary
        * key IDs.  A request to retrieve the previous result will retrieve the
        * result immediately previous to this, if any, in the results list.
        *
        * @param   id1
        *          A primary key ID, representing the target record (in the case
        *          of a join, this ID is associated with the left-most record in
        *          the join).
        * @param   joinIDs
        *          All remaining primary key IDs, if any, arranged from left to
        *          right to coincide with the records being joined by the
        *          underlying query.
        *
        * @return  <code>true</code> if REPOSITION-TO-ROWID() succeeds,
        *          else <code>false</code>.
        */
       public logical queryRepositionByID(rowid id1, rowid...joinIDs)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of REPOSITION-TO-ROWID() method (KW_REPOS_2I).
        *
        * Reposition the cursor such that a request to retrieve the next result
        * will retrieve the result which matches the specified array of primary
        * key IDs.  A request to retrieve the previous result will retrieve the
        * result immediately previous to this, if any, in the results list.
        *
        * @param   id1
        *          A primary key ID, representing the target record (in the case
        *          of a join, this ID is associated with the left-most record in
        *          the join).
        * @param   joinIDs
        *          The list of IDs, starting with the primary key ID representing the target record, while
        *          all remaining primary key IDs, if any, arranged from left to right to coincide with the records
        *          being joined by the underlying query.
        *
        * @return  <code>true</code> if REPOSITION-TO-ROWID() succeeds,
        *          else <code>false</code>.
        */
       public logical queryRepositionByID(rowid[] joinIDs)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of NUM-RESULTS attribute (KW_NUM_RES).
        *
        * Getter of NUM-RESULTS attribute
        *
        */
       public integer getNumResults()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Adds one new buffer to a query object or dynamic DataSet object,
        * without affecting the other buffers, if any. Use the SET-BUFFERS( )
        * method to remove all prior buffers and set all buffers for the object
        * at the same time.
        *
        * @param   bufHandle
        *          A buffer handle to add.
        *
        * @return  true on success
        */
       public logical addBuffer(handle bufHandle)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Adds one new buffer to a query object or dynamic DataSet object,
        * without affecting the other buffers, if any. Use the SET-BUFFERS( )
        * method to remove all prior buffers and set all buffers for the object
        * at the same time.
        *
        * @param   buf
        *          A buffer to add.
        *
        * @return  true on success
        */
       public logical addBuffer(Buffer buf)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Sets buffers for the query. Any buffers previously added
        * or set are removed. Use the <code>addBuffer()</code> method to add one
        * buffer to the object, without affecting the other buffers, if any.
        * This method is the equivalent of <code>SET-BUFFERS()</code> method
        * from 4GL.
        *
        * @param   hBuffers
        *          Variable argument list of buffer handles to be set.
        *          In 4GL, the maximum number of buffers per query is 18.
        *
        * @return  true on success
        */
       public logical setBuffers(handle ... hBuffers)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Sets buffers for the query. Any buffers previously added
        * or set are removed. Use the <code>addBuffer()</code> method to add one
        * buffer to the object, without affecting the other buffers, if any.
        * This method is the equivalent of <code>SET-BUFFERS()</code> method
        * from 4GL.
        *
        * @param   bufs
        *          Variable argument list of Buffer to be set.
        *          In 4GL, the maximum number of buffers per query is 18.
        *
        * @return  true on success
        */
       public logical setBuffers(Buffer ... bufs)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Compiles a predicate (query condition). The query must be open after this call.
        * This method is the equivalent of <code>QUERY-PREPARE()</code> method from 4GL.
        *
        * @param   predicate
        *          The predicate to be prepared by this query. The syntax of the string is the same
        *          of the <code>OPEN QUERY</code> statement of the 4GL.
        *
        * @return  true on success
        */
       public logical prepare(String predicate)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Compiles a predicate (query condition). The query must be open after this call.
        * This method is the equivalent of <code>QUERY-PREPARE()</code> method from 4GL.
        *
        * @param   predicate
        *          The predicate to be prepared by this query. The syntax of the string is the same
        *          of the <code>OPEN QUERY</code> statement of the 4GL.
        *
        * @return  true on success
        */
       public logical prepare(character predicate)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Opens a query object.
        * This method is the equivalent of <code>QUERY-OPEN()</code> method from 4GL.
        * <u>Note</u>: You must perform <code>prepare()</code> on a query
        * object before you perform <code>queryOpen</code> on it.
        *
        * @return  true on success
        */
       public logical queryOpen()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of INDEX-INFORMATION attribute (KW_IDX_INFO).
        *
        * Getter of INDEX-INFORMATION attribute
        *
        * @return A character string consisting of a comma-separated list of the index or
        *         indexes the query uses at the level of join specified.
        */
       public character indexInformation()
       {
          return indexInformation(new integer(1));
       }

       /**
        * Conversion of INDEX-INFORMATION attribute (KW_IDX_INFO).
        *
        * Getter of INDEX-INFORMATION attribute
        *
        * @param n
        * An integer expression that evaluates to the level of join for which you want
        * index information.

        * @return A character string consisting of a comma-separated list of the index or
        *         indexes the query uses at the level of join specified.
        */
       public character indexInformation(int n)
       {
          return indexInformation(new integer(n));
       }

       /**
        * Conversion of INDEX-INFORMATION attribute (KW_IDX_INFO).
        *
        * Getter of INDEX-INFORMATION attribute
        *
        * @param    n
        *           An integer expression that evaluates to the level of join for which you want index
        *           information.
        *
        * @return   A character string consisting of a comma-separated list of the index or
        *           indexes the query uses at the level of join specified.
        */
       public character indexInformation(NumberType n)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of FORWARD-ONLY attribute (KW_FWD_ONLY).
        *
        * Getter of FORWARD-ONLY attribute.
        *
        * @return current value of FORWARD-ONLY attribute.
        */
       public logical forwardOnly()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of FORWARD-ONLY attribute (KW_FWD_ONLY).
        *
        * Setter of FORWARD-ONLY attribute.
        *
        * @param  forwardOnly
        *         New value of FORWARD-ONLY attribute.
        */
       public void changeForwardOnly(logical forwardOnly)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of FORWARD-ONLY attribute (KW_FWD_ONLY).
        *
        * Setter of FORWARD-ONLY attribute.
        *
        * @param  forwardOnly
        *         New value of FORWARD-ONLY attribute.
        */
       public void changeForwardOnly(boolean forwardOnly)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of PREPARE_STRING attribute (KW_PREP_STR).
        *
        * Getter of PREPARE_STRING attribute.
        *
        * @return current value of PREPARE_STRING attribute.
        */
       public character prepareString()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Getter for the CACHE attribute.
        *
        * @return   See above.
        */
       @Override
       public integer getCache()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Setter for the CACHE attribute.
        *
        * @param    n
        *           The cache value.
        */
       @Override
       public void setCache(integer n)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Setter for the CACHE attribute.
        *
        * @param    n
        *           The cache value.
        */
       @Override
       public void setCache(long n)
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of IS-OPEN attribute (KW_IS_OPEN).
        *
        * Getter of IS-OPEN attribute.
        *
        * @return current value of IS-OPEN attribute.
        */
       public logical isOpen()
       {
          throw new RuntimeException("Must use the equivalent QueryWrapper API!");
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT), which returns a Java boolean, for internal usage.
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>.
        */
       @Override
       public boolean _getNext()
       {
          try
          {
             next();
             return !_isOffEnd(); // YES if row is available
          }
          catch (ErrorConditionException ece)
          {
             return false;
          }
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT).
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @param   lockType
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>. If the query
        *          object handle is invalid, the method returns the Unknown value (?).
        */
       @Override
       public logical getNext(LockType lockType)
       {
          return wrapNext(lockType);
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT).
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>. If the query
        *          object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getNext(long lock)
       {
          LockType lockType = fromLockNowait(lock, 0);
          return lockType == null ? logical.of(false) : wrapNext(lockType);
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT).
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>. If the query
        *          object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getNext(NumberType lock)
       {
          LockType lockType = fromLockNowait(lock.longValue(), 0);
          return lockType == null ? logical.of(false) : wrapNext(lockType);
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT).
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>. If the query
        *          object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getNext(long lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait);
          return lockType == null ? logical.of(false) : wrapNext(lockType);
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT).
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>. If the query
        *          object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getNext(long lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait.longValue());
          return lockType == null ? logical.of(false) : wrapNext(lockType);
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT).
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>. If the query
        *          object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getNext(NumberType lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait);
          return lockType == null ? logical.of(false) : wrapNext(lockType);
       }

       /**
        * Conversion of GET-NEXT method (KW_GET_NEXT).
        *
        * Moves a query object's result list pointer ahead one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the next record in the query is found. If the query is not
        *          open or the next record cannot be found (query is empty or the query result list
        *          pointer is on the last row), the method returns <code>false</code>. If the query
        *          object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getNext(NumberType lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait.longValue());
          return lockType == null ? logical.of(false) : wrapNext(lockType);
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        */
       @Override
       public logical getPrevious()
       {
          try
          {
             previous();
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getPrevious(long lock)
       {
          LockType lockType = fromLockNowait(lock, 0);
          return lockType == null ? logical.of(false) : wrapPrevious(lockType);
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @param   lockType
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  IllegalArgumentException
        *          if <code>lock</code> has not valid internal value.
        */
       @Override
       public logical getPrevious(LockType lockType)
       {
          return wrapPrevious(lockType);
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getPrevious(NumberType lock)
       {
          LockType lockType = fromLockNowait(lock.longValue(), 0);
          return lockType == null ? logical.of(false) : wrapPrevious(lockType);
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getPrevious(long lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait);
          return lockType == null ? logical.of(false) : wrapPrevious(lockType);
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getPrevious(NumberType lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait);
          return lockType == null ? logical.of(false) : wrapPrevious(lockType);
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getPrevious(long lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait.longValue());
          return lockType == null ? logical.of(false) : wrapPrevious(lockType);
       }

       /**
        * Conversion of GET-PREV method (KW_GET_PREV).
        *
        * Moves a query object's result list pointer back one row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the previous record in the query is found. If the query is
        *          not open or the previous record cannot be found (query is empty or the query
        *          result list pointer is on the first row), the method returns <code>false</code>.
        *          If the query object handle is invalid, the method returns the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getPrevious(NumberType lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait.longValue());
          return lockType == null ? logical.of(false) : wrapPrevious(lockType);
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        */
       @Override
       public logical getFirst()
       {
          try
          {
             first();
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  IllegalArgumentException
        *          If <code>lock</code> has not valid internal value.
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getFirst(long lock)
       {
          LockType lockType = fromLockNowait(lock, 0);
          return lockType == null ? logical.of(false) : wrapFirst(lockType);
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getFirst(NumberType lock)
       {
          LockType lockType = fromLockNowait(lock.longValue(), 0);
          return lockType == null ? logical.of(false) : wrapFirst(lockType);
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @param   lockType
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  IllegalArgumentException
        *          if <code>lock</code> has not valid internal value.
        */
       @Override
       public logical getFirst(LockType lockType)
       {
          return wrapFirst(lockType);
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  IllegalArgumentException
        *          If <code>lock</code> or <code>nowait</code> have not valid internal values.
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getFirst(long lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait);
          return lockType == null ? logical.of(false) : wrapFirst(lockType);
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getFirst(NumberType lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait);
          return lockType == null ? logical.of(false) : wrapFirst(lockType);
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  IllegalArgumentException
        *          If <code>lock</code> or <code>nowait</code> have not valid internal values.
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getFirst(long lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait.longValue());
          return lockType == null ? logical.of(false) : wrapFirst(lockType);
       }

       /**
        * Conversion of GET-FIRST method (KW_GET_1ST).
        *
        * Moves a query object's result list pointer to the first row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the first record in the query is found. If the query is not
        *          open or the first record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getFirst(NumberType lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait.longValue());
          return lockType == null ? logical.of(false) : wrapFirst(lockType);
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        */
       @Override
       public logical getLast()
       {
          try
          {
             last();
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getLast(long lock)
       {
          LockType lockType = fromLockNowait(lock, 0);
          return lockType == null ? logical.of(false) : wrapLast(lockType);
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getLast(NumberType lock)
       {
          LockType lockType = fromLockNowait(lock.longValue(), 0);
          return lockType == null ? logical.of(false) : wrapLast(lockType);
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @param   lockType
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  IllegalArgumentException
        *          if <code>lock</code> has not valid internal value.
        */
       @Override
       public logical getLast(LockType lockType)
       {
          return wrapLast(lockType);
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getLast(long lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait);
          return lockType == null ? logical.of(false) : wrapLast(lockType);
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getLast(NumberType lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait);
          return lockType == null ? logical.of(false) : wrapLast(lockType);
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getLast(long lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait.longValue());
          return lockType == null ? logical.of(false) : wrapLast(lockType);
       }

       /**
        * Conversion of GET-LAST method (KW_GET_LAST).
        *
        * Moves a query object's result list pointer to the last row.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if the last record in the query is found. If the query is not
        *          open or the last record cannot be found (query is empty), the method returns
        *          <code>false</code>. If the query object handle is invalid, the method returns
        *          the Unknown value (?).
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getLast(NumberType lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait.longValue());
          return lockType == null ? logical.of(false) : wrapLast(lockType);
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        */
       @Override
       public logical getCurrent()
       {
          try
          {
             current();
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getCurrent(long lock)
       {
          LockType lockType = fromLockNowait(lock, 0);
          return lockType == null ? logical.of(false) : wrapCurrent(lockType);
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getCurrent(NumberType lock)
       {
          LockType lockType = fromLockNowait(lock.longValue(), 0);
          return lockType == null ? logical.of(false) : wrapCurrent(lockType);
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @param   lockType of LockType
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        */
       @Override
       public logical getCurrent(LockType lockType)
       {
          return wrapCurrent(lockType);
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getCurrent(long lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait);
          return lockType == null ? logical.of(false) : wrapCurrent(lockType);
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getCurrent(NumberType lock, long nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait);
          return lockType == null ? logical.of(false) : wrapCurrent(lockType);
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getCurrent(long lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock, nowait.longValue());
          return lockType == null ? logical.of(false) : wrapCurrent(lockType);
       }

       /**
        * Conversion of GET-CURRENT() method (KW_GET_CUR).
        *
        * Re-fetches the current record or records associated with the query.
        *
        * @param   lock
        *          SHARE-LOCK|EXCLUSIVE-LOCK|NO-LOCK
        * @param   nowait
        *          NO-WAIT
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        *
        * @throws  ErrorConditionException
        *          If the operation fails.
        */
       @Override
       public logical getCurrent(NumberType lock, NumberType nowait)
       {
          LockType lockType = fromLockNowait(lock.longValue(), nowait.longValue());
          return lockType == null ? logical.of(false) : wrapCurrent(lockType);
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * Default substitution arguments and a lock type of <code>NONE</code> are used for this test.
        *
        * @return  <code>true</code> if the first record exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasAny()
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * The specified substitution arguments and a lock type of <code>NONE</code> are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        *
        * @return  <code>true</code> if the first record exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasAny(Object[] values)
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * Default substitution arguments and the specified lock type are used for this test.
        *
        * @param   lockType
        *          The lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  <code>true</code> if the first record exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasAny(LockType lockType)
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * The specified substitution arguments and lock type are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        * @param   lockType
        *          The lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  <code>true</code> if the first record exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasAny(Object[] values, LockType lockType)
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * Default substitution arguments and a lock type of <code>NONE</code> are used for this test.
        *
        * @return  <code>true</code> if any record matching the query's criteria exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasOne()
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * The specified substitution arguments and a lock type of <code>NONE</code> are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        *
        * @return  <code>true</code> if any record matching the query's criteria exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasOne(Object[] values)
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * Default substitution arguments and the specified lock type are used for this test.
        *
        * @param   lockType
        *          The lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  <code>true</code> if any record matching the query's criteria exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasOne(LockType lockType)
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * The specified substitution arguments and lock type are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        * @param   lockType
        *          The lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  <code>true</code> if any record matching the query's criteria exists, else <code>false</code>.
        *
        * @throws  UnsupportedOperationException
        *          Always, this method must be implemented in derived classes.
        */
       public logical hasOne(Object[] values, LockType lockType)
       {
          throw new UnsupportedOperationException();
       }

       /**
        * Navigate to the first record which meets the query criteria and retrieve
        * it into its record buffer.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentFirst(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentFirst(Object[], LockType)}</li>
        * </ul>
        *
        * @return  <code>true</code> if query could retrieve the first record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       public boolean silentFirst()
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return first();
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to the first record which meets the query criteria and retrieve
        * it into its record buffer.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentFirst(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentFirst(Object[], LockType)}</li>
        * </ul>
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       public boolean silentFirst(LockType lockType)
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return first(lockType);
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to the last record which meets the query criteria and retrieve
        * it into its record buffer.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentLast(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentLast(Object[], LockType)}</li>
        * </ul>
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       public boolean silentLast()
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return last();
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to the last record which meets the query criteria and retrieve
        * it into its record buffer.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentLast(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentLast(Object[], LockType)}</li>
        * </ul>
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       public boolean silentLast(LockType lockType)
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return last(lockType);
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to the next record which meets the query criteria and retrieve
        * it into its record buffer.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the next record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the first record
        * which meets the query criteria.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentNext(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentNext(Object[], LockType)}</li>
        * </ul>
        *
        * @return  <code>true</code> if query could retrieve the next record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       public boolean silentNext()
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return next();
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to the next record which meets the query criteria and retrieve
        * it into its record buffer.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the next record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the first record
        * which meets the query criteria.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentNext(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentNext(Object[], LockType)}</li>
        * </ul>
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the next record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       public boolean silentNext(LockType lockType)
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return next(lockType);
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to the previous record which meets the query criteria and
        * retrieve it into its record buffer.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the previous record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the last record
        * which meets the query criteria.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentPrevious(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentPrevious(Object[], LockType)}</li>
        * </ul>
        *
        * @return  <code>true</code> if query could retrieve the previous record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       public boolean silentPrevious()
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return previous();
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to the previous record which meets the query criteria and
        * retrieve it into its record buffer.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the previous record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the last record
        * which meets the query criteria.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentPrevious(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentPrevious(Object[], LockType)}</li>
        * </ul>
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the previous record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       public boolean silentPrevious(LockType lockType)
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return previous(lockType);
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to a particular record which meets the query criteria and
        * retrieve it into its record buffer.  No more than one record may match
        * the criteria.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentUnique(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentUnique(Object[], LockType)}</li>
        * </ul>
        *
        * @return  <code>true</code> if query could retrieve the unique record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if more than
        *          one record is found which match the criteria.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       public boolean silentUnique()
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return unique();
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Navigate to a particular record which meets the query criteria and
        * retrieve it into its record buffer. No more than one record may match
        * the criteria.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        * Additional methods are defined for specific queries such as:
        * <ul>
        *    <li>{@link RandomAccessQuery#silentUnique(Object[])}</li>
        *    <li>{@link RandomAccessQuery#silentUnique(Object[], LockType)}</li>
        * </ul>
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the unique record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if more than
        *          one record is found which match the criteria.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       public boolean silentUnique(LockType lockType)
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return unique(lockType);
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Reload the record most recently loaded into the backing record buffer,
        * if any.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        *
        * @return  <code>true</code> if query could retrieve the current record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record reload.
        */
       public boolean silentCurrent()
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return current();
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Reload the record most recently loaded into the backing record buffer,
        * if any.
        * <p>
        * This operation is executed in silent mode.
        * <p>
        *
        * @param   lockType
        *          New lock type to apply to the record.
        *
        * @return  <code>true</code> if query could retrieve the current record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record reload.
        */
       public boolean silentCurrent(LockType lockType)
       {
          ErrorHelper eh = getErrorHelper();
          boolean wasSilent = eh.isSilent();
          if (!wasSilent)
          {
             eh.setSilent(true);
          }

          boolean wasLenientOffEnd = isLenientOffEnd();
          if (!wasLenientOffEnd)
          {
             setLenientOffEnd(true);
          }

          try
          {
             return current(lockType);
          }
          catch (LegacyErrorException lex)
          {
             ErrorManager.processLegacyErrorException(lex, eh);
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                eh.setSilent(false);
             }

             if (!wasLenientOffEnd)
             {
                setLenientOffEnd(false);
             }

             eh.handleForwardPending();
          }

          return eh.getErrorStatus();
       }

       /**
        * Specifies the fields included in a record retrieval.
        *
        * @param dmo
        *        DMO proxy which defines the buffer for which the fields are specified.
        * @param fields
        *        The fields included in a record retrieval. If no fields are specified, only record
        *        identifiers are retrieved. No subscripts can be specified for extent fields.
        */
       public void include(DataModelObject dmo, String ... fields)
       {
          RecordBuffer buffer = ((BufferReference) dmo).buffer();
          if (buffer.isTemporary())
          {
             return;
          }

          if (included == null)
          {
             included = new IdentityHashMap<>();
          }

          collectFields(included, dmo, fields, buffer);
       }

       /**
        * Specifies the fields excluded from a record retrieval.
        *
        * @param dmo
        *        DMO proxy which defines the buffer for which the fields are specified.
        * @param fields
        *        The fields excluded from a record retrieval. If no fields are specified, complete
        *        records are retrieved. No subscripts can be specified for extent fields.
        */
       public void exclude(DataModelObject dmo, String ... fields)
       {
          RecordBuffer buffer = ((BufferReference) dmo).buffer();
          if (buffer.isTemporary())
          {
             return;
          }

          if (excluded == null)
          {
             excluded = new IdentityHashMap<>();
          }

          collectFields(excluded, dmo, fields, buffer);
       }

       /**
        * Conversion of NUM-BUFFERS() method (KW_NUM_BUFF).
        *
        * @return  the number of buffers in a query or DataSet object.
        */
       public integer numBuffers()
       {
          return new integer(getTableCount());
       }

       /**
        * Deletes the current row of a query's result list. Implements the 4GL
        * DELETE-RESULT-LIST-ENTRY() method.
        *
        * @return <code>true</code> on success.
        */
       public logical deleteResultListEntry()
       {
          return deleteResultListEntry(false);
       }

       /**
        * Deletes the current row of a query's result list. Implements the 4GL
        * DELETE-RESULT-LIST-ENTRY() method.
        *
        * @param  allowBetweenRows
        *         If <code>true</code> then the cursor can be positioned between rows (the next
        *         row will be deleted). <code>false</code> for conventional DELETE-RESULT-LIST-ENTRY()
        *         mode where the cursor should be positioned on a row (otherwise <code>false</code> is
        *         returned).
        *
        * @return always throws IllegalStateException.
        */
       public logical deleteResultListEntry(boolean allowBetweenRows)
       {
          throw new IllegalStateException(
                "DELETE-RESULT-LIST-ENTRY is not applicable to this type of query");
       }

       /**
        * Creates an entry in the result list for the current row. Implements the 4GL
        * CREATE-RESULT-LIST-ENTRY() method. Created entry contains id(s) of the record(s) currently
        * located in the backing buffer(s).
        *
        * @return <code>true</code> on success.
        */
       public logical createResultListEntry()
       {
          throw new IllegalStateException(
                "CREATE-RESULT-LIST-ENTRY is not applicable to this type of query");
       }

       /**
        * Setter for SKIP-DELETED-RECORD query attribute.
        *
        * @param   on
        *          New value for the attribute.
        */
       @Override
       public void setSkipDeletedRecord(logical on)
       {
          if (on == null || on.isUnknown())
          {
             ErrorManager.recordOrShowError(4083, "SKIP-DELETED-RECORD", "QUERY widget");
             return; // attribute remains unchanged
          }

          boolean javaValue = on.booleanValue();
          if (browsed && javaValue)
          {
             ErrorManager.recordOrShowError(7363, "", "");
             // SKIP-DELETED-RECORD cannot be set on during BROWSE. (7363)

             ErrorManager.recordOrThrowError(3131, "SKIP-DELETED-RECORD", "", "QUERY widget");
             // Unable to set attribute SKIP-DELETED-RECORD in widget  of type QUERY widget (3131)
             return;
          }

          skipDeletedRecord = javaValue;
       }

       /**
        * Get value of SKIP-DELETED-RECORD attribute of the query.
        *
        * @return  the value of SKIP-DELETED-RECORD attribute of the query.
        */
       @Override
       public logical isSkipDeletedRecord()
       {
          return logical.of(skipDeletedRecord);
       }

       /**
        * Set the filter that handles parameters passed to the query. E.g. it can take input field
        * references and substitute them with snapshot values, if necessary.
        *
        * @param parameterFilter
        *        The filter that handles passed parameters.
        */
       @Override
       public void setParameterFilter(ParameterFilter parameterFilter)
       {
          this.parameterFilter = parameterFilter;
       }

       /**
        * Test whether the flag was set.
        *
        * @return  {@code true} during the returning of rows for populating a browse widget.
        *
        * @see     #setFillingBrowseRows
        */
       @Override
       public boolean isFillingBrowseRows()
       {
          return fillingBrowseRows;
       }

       /**
        * Test whether the SKIP-DELETED-RECORD attribute is set.
        *
        * @return  the value of SKIP-DELETED-RECORD attribute of the query as a Java boolean.
        */
       boolean _isSkipDeletedRecord()
       {
          return skipDeletedRecord;
       }

       /**
        * Worker method for wrapping ErrorConditionException and reusing
        * in all P2JQuery.getNext() implementations
        *
        * @param   lockType
        *          instance of LockType
        *
        * @return  <code>true</code> if GET-NEXT() succeeds, else <code>false</code>.
        */
       private logical wrapNext(LockType lockType)
       {
          try
          {
             next(lockType);
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Worker method for wrapping ErrorConditionException and reusing
        * in all P2JQuery.getPrevious() implementations
        *
        * @param   lockType
        *          instance of LockType
        *
        * @return  <code>true</code> if GET-PREV() succeeds, else <code>false</code>.
        */
       private logical wrapPrevious(LockType lockType)
       {
          try
          {
             previous(lockType);
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Worker method for wrapping ErrorConditionException and reusing
        * in all P2JQuery.getFirst() implementations
        *
        * @param   lockType
        *          instance of LockType
        *
        * @return  <code>true</code> if GET-FIRST() succeeds, else <code>false</code>.
        */
       private logical wrapFirst(LockType lockType)
       {
          try
          {
             first(lockType);
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Worker method for wrapping ErrorConditionException and reusing
        * in all P2JQuery.getLast() implementations
        *
        * @param   lockType
        *          instance of LockType
        *
        * @return  <code>true</code> if GET-LAST() succeeds, else <code>false</code>.
        */
       private logical wrapLast(LockType lockType)
       {
          try
          {
             last(lockType);
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Worker method for wrapping ErrorConditionException and reusing
        * in all P2JQuery.getCurrent() implementations
        *
        * @param   lockType
        *          instance of LockType
        *
        * @return  <code>true</code> if GET-CURRENT() succeeds, else <code>false</code>.
        */
       private logical wrapCurrent(LockType lockType)
       {
          try
          {
             current(lockType);
             return logical.of(!_isOffEnd()); // YES if row is available
          }
          catch (ErrorConditionException exc)
          {
             return logical.of(false);
          }
       }

       /**
        * Populate the map of with the ORM properties specified in the given fields.
        *
        * @param    target
        *           The target map.
        * @param    dmo
        *           The DMO instance.
        * @param    fields
        *           The fields to resolve.
        * @param    buffer
        *           The buffer instance.
        */
       private void collectFields(Map<DataModelObject, Property[]> target,
                                  DataModelObject                  dmo,
                                  String[]                         fields,
                                  RecordBuffer                     buffer)
       {
          DmoMeta meta = buffer.getDmoInfo();
          Property[] props = target.computeIfAbsent(dmo, (d) -> new Property[meta.getFieldSize()]);

          for (int i = 0; i < fields.length; i++)
          {
             Property field = meta.getFieldInfo(fields[i]);
             if (field == null)
             {
                // must be a custom extent
                List<Property> extFields = meta.getCustomExtentFieldInfo(fields[i]);
                if (extFields != null)
                {
                   for (int j = 0; j < extFields.size(); j++)
                   {
                      field = extFields.get(j);
                      props[field.propId - 1] = field;
                   }
                }
                else
                {
                   LOG.log(Level.WARNING,
                           "Field " + fields[i] + " can not be resolved in buffer " + buffer.getLegacyName());
                }
             }
             else
             {
                props[field.propId - 1] = field;
             }
             // else: all the not-expanded fields will be fetched in bulk, unconditionally
          }
       }

       /**
        * Get the recursive DATA-RELATION the query is to FILL for.
        * @return the recursive DATA-RELATION the query is to FILL for
        */
       @Override
       public DataRelation getDataRelation()
       {
           return this.relation;
       }

       /**
        * Set the recursive DATA-RELATION the query is to FILL for.
        *
        * @param relation
        *        the relation in question
        */
       @Override
       public void setDataRelation(DataRelation relation)
       {
           this.relation = relation;
       }

       /**
        * Instances of this class are used as keys to cache translateSort output.
        */
       private static class SortCacheKey
       {
          /** Bound buffer alias */
          private final String boundAlias;

          /** Definition buffer alias */
          private final String defAlias;

          /** Bound buffer dmo interface */
          private final Class<?> boundDmoIface;

          /** Definition buffer dmo interface */
          private final Class<?> defDmoIface;

          /** The sort clause to be translated */
          private final String sort;

          /** The cached hash code.*/
          private final int cachedHash;

          /**
           * Constructor.
           *
           * @param   boundAlias
           *          Bound buffer alias
           * @param   defAlias
           *          Definition buffer alias
           * @param   boundDmoIface
           *          Bound buffer dmo interface
           * @param   defDmoIface
           *          Definition buffer dmo interface
           * @param   sort
           *          The sort clause to be translated.
           */
          SortCacheKey(String boundAlias,
                       String defAlias,
                       Class<?> boundDmoIface,
                       Class<?> defDmoIface,
                       String sort)
          {
             this.boundAlias = boundAlias;
             this.defAlias = defAlias;
             this.boundDmoIface = boundDmoIface;
             this.defDmoIface = defDmoIface;
             this.sort = sort;
             // since this object is immutable (all fields are private/final) we can compute now the
             // hash code and cache it for when needed, to improve performance
             this.cachedHash = _hashCode();
          }

          /**
           * Return a hash code consistent with {@link #equals}.
           *
           * @return  Hash code.
           */
          public int hashCode()
          {
             return cachedHash;
          }

          /**
           * Check this object for equivalence with another instance of this class.
           *
           * @param   o
           *          Another instance of this class.
           *
           * @return  <code>true</code> if instances are equivalent, else <code>false</code>.
           */
          public boolean equals(Object o)
          {
             if (!(o instanceof AbstractQuery.SortCacheKey) )
             {
                return false;
             }

             AbstractQuery.SortCacheKey that = (AbstractQuery.SortCacheKey) o;

             if (!boundAlias.equals(that.boundAlias)       ||
                 !defAlias.equals(that.defAlias)           ||
                 !boundDmoIface.equals(that.boundDmoIface) ||
                 !defDmoIface.equals(that.defDmoIface)     ||
                 !sort.equals(that.sort))
             {
                return false;
             }

             return true;
          }

          /**
           * Return a hash code consistent with {@link #equals}.
           *
           * @return  Hash code.
           */
          private int _hashCode()
          {
             int result = 17;
             result = 37 * result + sort.hashCode();
             result = 37 * result + boundAlias.hashCode();
             result = 37 * result + defAlias.hashCode();
             result = 37 * result + boundDmoIface.hashCode();
             result = 37 * result + defDmoIface.hashCode();
             return result;
          }
       }
    }