Joinable.java

/*
** Module   : Joinable.java
** Abstract : Interface for query types which can be added as components to CompoundQuery
**
** Copyright (c) 2004-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
** 001 ECF 20051113   @23385 Created initial version. Query types which
**                           can be added as components to CompoundQuery
**                           must implement this interface.
** 002 ECF 20051214   @23749 Added support for scrolling queries. Added
**                           several methods and extend the new Scrollable
**                           base interface.
** 003 ECF 20060106   @23797 Removed record retrieval methods. Methods
**                           first, last, next, previous, unique, and
**                           current are now defined higher up in this
**                           interface hierarchy.
** 004 ECF 20060216   @24719 Added client-side where clause support. Added
**                           setResults method to allow CompoundQuery to
**                           set a final results list into a component.
**                           Added resolveArgs parameter to reset method
**                           to allow conditional substitution parameter
**                           resolution.
** 005 ECF 20070504   @33408 Changed load() signature to return boolean.
** 006 ECF 20070518   @33681 Added getRecordBuffers(). Must return an
**                           array of RecordBuffer instances used by this
**                           query.
** 007 ECF 20080112   @37000 Removed restriction of dealing only with IDs.
**                           This interface now handles both DMO instances
**                           and primary key IDs.
** 008 ECF 20081105   @40310 Added registerRecordChangeListeners(). Used
**                           by CompoundQuery to ensure change listeners
**                           are registered with the change broker at the
**                           appropriate scope.
** 009 ECF 20090312   @42983 Removed getTableCount(). This method has moved to
**                           the P2JQuery interface.
** 010 ECF 20090703   @43052 API change. Modified load() signature.
** 011 ECF 20131028          Import change.
** 012 ECF 20150802          Added methods required for compound query optimization.
** 013 SVL 20160608          Added silentIfNullId parameter to load() function.
** 014 ECF 20160620          Fixed javadoc and copyright.
** 015 ECF 20160727          Added getReferencedBuffers.
** 016 ECF 20180206          Cleaned up inheritance hierarchy.
** 017 OM  20180220          Added outer-join attribute.
** 018 OM  20181023          Added external buffers when making the adaptive server join
**                           components.
** 019 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.
** 020 CA  20240422          Allow the query to be reset to its initialization state, so the same instance can
**                           be used to execute child buffer FILL operations.
*/

/*
** 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 com.goldencode.p2j.persist.lock.*;

/**
 * Query types which can be added as components to <code>CompoundQuery</code>
 * must implement this interface.  It defines methods which will be called by
 * the enclosing, compound query object, which retrieve records and update
 * the state of the implementing query and its buffer(s).
 */
interface Joinable
extends Scrollable
{
   /**
    * Clear the current results from the query, so the same query instance can be executed again, without 
    * having to {@link #open() re-open} it.
    */
   @Override
   default void clearResults()
   {
      reset(true, 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);
   
   /**
    * Set a mode on the underlying buffer(s) such that setting a null record
    * into a buffer may or may not result in an error condition.  This applies
    * only to <code>FIRST</code>, <code>LAST</code>, and <code>UNIQUE</code>
    * iteration types.
    *
    * @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);
   
   /**
    * Force the query to retrieve full records, rather than primary keys only.
    */
   public void setFullRecords();
   
   /**
    * Set each buffer backing the query to unknown mode.
    *
    * @see  RecordBuffer#setUnknownMode()
    */
   public void setUnknownRecord();
   
   /**
    * Reset the query by clearing its state to a known default.  This is
    * used to ensure that subsequent requests for FIRST and LAST record
    * retrievals operate on a known query/buffer state.
    *
    * @param   resolveArgs
    *          <code>true</code> if query substitution arguments should be
    *          resolved as part of the reset;  <code>false</code> if existing
    *          argument values should be used for the next query execution.
    * @param   forceOrigArgs
    *          Flag indicating that the original arguments used at the construction of the query must be
    *          re-calculated.
    */
   public void reset(boolean resolveArgs, boolean forceOrigArgs);
   
   /**
    * 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.
    * 
    * @return  All record buffers referenced by this query.
    */
   public RecordBuffer[] getReferencedBuffers();
   
   /**
    * Load one or more records into their associated buffers, given an array
    * of primary key ID values, or the actual DMOs themselves.  The IDs/DMOs
    * are arranged to coincide with the tables underlying the query, from
    * left-most to right-most (in the sense of how they are joined by the
    * query).
    * <p>
    * The contract of this method is that if an implementor cannot load the
    * expected value (e.g., the record has been deleted or is otherwise no
    * longer available), it must throw {@link MissingRecordException}, after
    * setting the associated buffer into {@link RecordBuffer#setUnknownMode()
    * unknown mode}. Note that a record being locked by another session does
    * not constitute a "missing" record. Note also that this exception must
    * not be thrown until all the elements of <code>data</code> have been
    * processed to the extent possible.
    *
    * @param   data
    *          Array of primary key IDs or DMOs, one per table involved in the
    *          query.
    * @param   lockType
    *          Lock type which should override that of the query.  Set to
    *          <code>null</code> to allow query to use its current lock type.
    * @param   silentIfNullId
    *          If <code>true</code>, do not raise {@link MissingRecordException}
    *          if some of the provided IDs are <code>null</code>. <code>null</code>
    *          IDs are valid for queries with OUTER join.
    *
    * @throws  MissingRecordException
    *          if any record cannot be loaded, because it is no longer
    *          available (e.g., deleted, etc.).
    * @throws  PersistenceException
    *          if there is an error loading data.
    * 
    * @see     #getRow()
    */
   public void load(Object[] data, LockType lockType, boolean silentIfNullId)
   throws MissingRecordException,
          PersistenceException;
   
   /**
    * Set the given results object into this query.
    *
    * @param   results
    *          New result set for this query.
    */
   public void setResults(Results results);
   
   /**
    * Assemble an array of primary key IDs or DMOs for the current record(s)
    * in the buffers underlying this query, from left-most to right-most (in
    * the sense of how the query joins the associated tables).  This method
    * essentially takes a lightweight snapshot of the currently loaded
    * record(s), so that this may be restored later.
    *
    * @return  Array of primary key IDs, one per table involved in the query.
    *
    * @see     #load(Object[], LockType, boolean)
    */
   public Object[] getRow();
   
   /**
    * Fetch the array of primary key IDs associated with the first result in
    * this query's result list.
    *
    * @return  Array of record IDs reflecting the first query result, or
    *          <code>null</code> if there is no such result.
    */
   public Object[] peekFirst();
   
   /**
    * Fetch the array of primary key IDs associated with the last result in
    * this query's result list.
    *
    * @return  Array of record IDs reflecting the last query result, or
    *          <code>null</code> if there is no such result.
    */
   public Object[] peekLast();
   
   /**
    * Check if this type of query should register record change listeners. Use this 
    * to avoid computing any scope for registration and end up with a no registration
    * behavior. 
    * 
    * @return   {@code true} if we should invest time into computing the scope in order
    *           to call {@link #registerRecordChangeListeners} or {@code false} if we
    *           can skip doing any work to honor {@link #registerRecordChangeListeners}.
    */
   public default boolean shouldRegisterRecordChangeListeners()
   {
      return true;
   }
   
   /**
    * Register all <code>RecordChangeListener</code>s associated with this
    * query (if any), with the context-local <code>ChangeBroker</code> at the
    * indicated scope.
    * 
    * @param   scope
    *          Scope at which listeners should be registered with the change
    *          broker.
    */
   public void registerRecordChangeListeners(Integer scope);
   
   /**
    * Create an adaptive query component based on the information in this query (which is
    * presumed to be a single-table query), and given a list of adaptive query components which
    * represent outer, nested query loops, which will perform a server-side join to the
    * innermost, nested query component.
    * 
    * @param   joinList
    *          List of adaptive components representing nested query loops which will contain
    *          the adaptive query component returned by this method. The last component in the
    *          list represents the nested query loop immediately containing the returned query
    *          component, with which the join will be made.
    * @param   query
    *          Adaptive query which will manage the server-side join of <code>joinList</code> and
    *          the returned query component.
    * @param   iteration
    *          Iteration type: FIRST, LAST, or NEXT.
    * @param   outer
    *          <code>true</code> if the join should be a left outer join. This type of join is
    *          not supported at the time of this writing, so this parameter should always be
    *          <code>false</code>. It is here to support a planned, future enhancement.
    * @param   fallback
    *          A compound query component to use in the event the multi-table adaptive query
    *          switches from preselect to dynamic retrieval mode.
    * 
    * @return  An adaptive query component suitable for a server-side join, based on the
    *          information in this query.
    */
   public AdaptiveComponent makeAdaptiveServerJoinComponent(List<AdaptiveComponent> joinList,
                                                            AdaptiveQuery query,
                                                            int iteration,
                                                            boolean outer,
                                                            CompoundComponent fallback);
   
   /**
    * Create a preselect query component based on the information in this query (which is
    * presumed to be a single-table query), and given a list of preselect query components which
    * represent outer, nested query loops, which will perform a server-side join to the
    * innermost, nested query component.
    * 
    * @param   joinList
    *          List of preselect components representing nested query loops which will contain
    *          the preselect query component returned by this method. The last component in the
    *          list represents the nested query loop immediately containing the returned query
    *          component, with which the join will be made.
    * @param   iteration
    *          Iteration type: FIRST, LAST, or NEXT.
    * @param   outer
    *          <code>true</code> if the join should be a left outer join. This type of join is
    *          not supported at the time of this writing, so this parameter should always be
    *          <code>false</code>. It is here to support a planned, future enhancement.
    * 
    * @return  A preselect query component suitable for a server-side join, based on the
    *          information in this query.
    */
   public QueryComponent makePreselectServerJoinComponent(List<QueryComponent> joinList,
                                                          int iteration,
                                                          boolean outer);
   
   /**
    * Indicate whether this query has a client-side where clause expression associated with it.
    * 
    * @return  <code>true</code> if there is a where expression; else <code>false</code>.
    */
   public boolean hasWhereExpression();
   
   /**
    * Get the query substitution arguments associated with this query, if any.
    * 
    * @return  Substitution arguments, in order, or <code>null</code> if there are none.
    */
   public Object[] getArgs();
   
   /**
    * Get the original, HQL where clause associated with this query.
    * 
    * @return  Original where clause, or <code>null</code> if there is none.
    */
   public String getOriginalWhere();
   
   /**
    * Force this query to operate in dynamic retrieval mode, if it supports this mode.
    */
   public void forceDynamicOperation();
   
   /**
    * Get the sort phrase associated with this query.
    * 
    * @return  Sort phrase.
    */
   public String getSortPhrase();
   
   /**
    * Get the external buffers, if any, associated with this query.
    *
    * @return  Array of external buffers or {@code null}.
    */
   public RecordBuffer[] getExternalBuffers();
   
   /**
    * Translate the WHERE clause.
    */
   public void translateWhere();
}