TableParameter.java
/*
** Module : TableParameter.java
** Abstract : Parameter support for TABLE and TABLE-HANDLE.
**
** Copyright (c) 2013-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 SVL 20130226 Created initial version.
** 002 SVL 20140106 Added table handle variable.
** 003 CA 20140110 Added support for usage from java side (when there is no DMO passed).
** 004 SVL 20140414 Added support for remote parameters.
** 005 SVL 20140611 Do not allow tableHandle to be null.
** 006 SVL 20140709 Added parameterIndex.
** 007 CA 20171030 Added support for BY-REFERENCE table parameter mode.
** 008 OM 20190510 Added error handling when incorrect handle is passed to constructor.
** OM 20190523 Unified parameter options handling for TempTables and DataSet params.
** 009 ECF 20190823 Temporary workaround for regression with output parameters.
** TODO: proper fix needed.
** 010 HC 20190719 Fixed unexpected error when passing TABLE-HANDLE as output parameter.
** CA 20190731 Create a dynamic table from a REMOTE table, when INPUT is used.
** CA 20190826 If a table-handle parameter is for a temp-table, then set the 'table'
** Java field for the default-buffer, and not the temp-table.
** 011 CA 20200427 Allow a TABLE to be read from JSON argument, in case of a remote REST call.
** 012 OM 20210106 Dropped 2nd parameter of TEMP-TABLE-PREPARE.
** SVL 20210517 Added isValid().
** SVL 20210517 Fixed isValid() for the case of a result set.
** CA 20211125 Fixed an issue when the TABLE-HANDLE handle is not unknown, but invalid - getTable() must
** return null.
** CA 20220901 Refactored scope notification support: ScopeableFactory was removed, and the registration
** is now specific to each type of scopeable. For each case, the block will be registered
** for scope support (for that particular scopeable) only when the scopeable is 'active'
** (i.e. unnamed streams or accumulators are used). This allows a lazy registration of
** scopeables, to avoid the unnecessary overhead of processing all the scopeables for each
** and every block.
** GES 20210430 Reworked to enable subclasses which reduce parameter processing from common use cases.
** CA 20220601 Any TableParameter or DataSetParameter instances at a legacy OO method or constructor call
** must be transformed to their mode'ed version, depending on the INPUT, OUTPUT or
** INPUT-OUTPUT mode, and the DATASET-HANDLE, DATASET, TABLE-HANDLE or TABLE versions.
** CA 20220727 Fixed OO dataset/table parameters (at the method definition and method call) when there is
** an APPEND option.
** IAS 20221029 Added SCHEMA-MARSHAL attribute support.
** IAS 20221108 Replaced static 'TempTableBuilder.createRemoteTable' with a regular one.
** IAS 20221111 Fixed 'schemaMarshalLevel' support.
** CA 20230116 Avoid using handle.unwrap, handle.getReference or other BDT usage from within FWD runtime.
** 013 CA 20240409 Allow the parameter mode to be set (useful for CALL parameter).
*/
/*
** 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.util;
import java.util.*;
import com.goldencode.p2j.persist.*;
/**
* Represents a table or table-handle parameter, the mode (INPUT, INPUT-OUTPUT or OUTPUT) and any table
* parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE). Allows subclasses to specialize this
* behavior. A core feature is to implement interchangeability between TABLE and TABLE-HANDLE parameters
* (in the 4GL you can pass a TABLE parameter to a procedure that takes TABLE-HANDLE and vice versa).
* <p>
* The parameter mode can be implemented in this class or it can be overridden in a subclass. If it is
* implemented here, then the input and output flags must be passed to the constructor. If the subclass
* overrides the {@link #isInputMode} and {@link #isOutputMode} methods then the input and output boolean
* members will be ignored. This is sub-optimal but it was done deliberately to enable the base class to
* be used as a full replacement for any of the subclasses.
*/
public class TableParameter
{
/** Buffer of the wrapped table. */
private Temporary table;
/** Table handle. */
private final handle tableHandle;
/** Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE). */
private EnumSet<ParameterOption> options;
/** Calling- or called-side result set (for remote parameters). */
private TableWrapper resultSet = null;
/** Determines if this is a remote (passed to appserver) parameter. */
private boolean remoteParameter = false;
/** {@code true} if the parameter mode is INPUT or INPUT-OUTPUT. */
private boolean input = false;
/** {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT. */
private boolean output = false;
/** Table SCHEMA-MARSHAL attribute value encodes as int */
private int schemaMarshalLevel;
/** Flag indicating this is a TABLE-HANDLE argument. */
private boolean isTableHandle = false;
/**
* 1-based index of the table parameter (in declaration of the function). <code>0</code>
* if it is not defined.
*/
private int parameterIndex = 0;
/**
* Create an instance using a buffer.
*
* @param table
* Buffer of the wrapped table.
*/
public TableParameter(Temporary table)
{
this(table, ParameterOption.NONE);
}
/**
* Create an instance using a buffer.
*
* @param table
* Buffer of the wrapped table.
* @param option
* Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
*/
public TableParameter(Temporary table, ParameterOption option)
{
this(table, option, false, false);
}
/**
* Create an instance using a buffer.
*
* @param table
* Buffer of the wrapped table.
* @param options
* Table parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE).
*/
public TableParameter(Temporary table, EnumSet<ParameterOption> options)
{
this(table, options, false, false);
}
/**
* Create an instance using a buffer.
*
* @param table
* Buffer of the wrapped table.
* @param option
* Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
* @param input
* {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
* @param output
* {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
*/
public TableParameter(Temporary table, ParameterOption option, boolean input, boolean output)
{
this(table, EnumSet.of(option), input, output);
}
/**
* Create an instance using a buffer.
*
* @param table
* Buffer of the wrapped table.
* @param options
* Table parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE).
* @param input
* {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
* @param output
* {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
*/
public TableParameter(Temporary table, EnumSet<ParameterOption> options, boolean input, boolean output)
{
BufferManager.registerScopeable(null);
this.table = (Temporary) ((BufferImpl) table).ref();
this.tableHandle = ((BufferReference) table).buffer().tableHandle();
this.options = options;
this.input = input;
this.output = output;
this.schemaMarshalLevel = ((AbstractTempTable)this.tableHandle.getResource()).getSchemaMarshalLevel();
}
/**
* Create an instance using a table handle.
*
* @param tableHandle
* Handle to the wrapped table.
*/
public TableParameter(handle tableHandle)
{
this(tableHandle, ParameterOption.NONE);
}
/**
* Create an instance using a table handle.
*
* @param tableHandle
* Handle to the wrapped table.
* @param option
* Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
*/
public TableParameter(handle tableHandle, ParameterOption option)
{
this(tableHandle, option, false, false);
}
/**
* Create an instance using a table handle.
*
* @param tableHandle
* Handle to the wrapped table.
* @param options
* Table parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE).
*/
public TableParameter(handle tableHandle, EnumSet<ParameterOption> options)
{
this(tableHandle, options, false, false);
}
/**
* Create an instance using a table handle.
*
* @param tableHandle
* Handle to the wrapped table.
* @param option
* Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
* @param input
* {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
* @param output
* {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
*/
public TableParameter(handle tableHandle, ParameterOption option, boolean input, boolean output)
{
this(tableHandle, EnumSet.of(option), input, output);
}
/**
* Create an instance using a table handle.
*
* @param tableHandle
* Handle to the wrapped table.
* @param options
* Table parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE).
* @param input
* {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
* @param output
* {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
*/
public TableParameter(handle tableHandle, EnumSet<ParameterOption> options, boolean input, boolean output)
{
BufferManager.registerScopeable(null);
WrappedResource res = tableHandle.getResource();
isTableHandle = true;
if (tableHandle._isValid())
{
if (res instanceof TempTable)
{
this.table = TemporaryBuffer.getDefaultBuffer(tableHandle);
}
else if (res instanceof Temporary)
{
this.table = (Temporary) res;
}
else
{
String msg1 = "TEMP-TABLE handle parameter requires valid and prepared handle";
String msg2 = "Error attempting to push run time parameters onto the stack";
ErrorManager.recordOrThrowError(new int[] { 9068, 984 },
new String[] { msg1, msg2 }, false);
}
}
else
{
this.table = null;
}
this.tableHandle = tableHandle;
this.options = options;
this.input = false;
this.output = false;
}
/**
* Create an instance using a result set (for remote parameters).
*
* @param resultSet
* Result set containing input data.
*/
public TableParameter(TableWrapper resultSet)
{
this(resultSet, ParameterOption.NONE);
}
/**
* Create an instance using a result set (for remote parameters).
*
* @param resultSet
* Result set containing input data.
* @param option
* Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
*/
public TableParameter(TableWrapper resultSet, ParameterOption option)
{
this(resultSet, option, false, false);
}
/**
* Create an instance using a result set (for remote parameters).
*
* @param resultSet
* Result set containing input data.
* @param option
* Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
* @param input
* {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
* @param output
* {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
*/
public TableParameter(TableWrapper resultSet, ParameterOption option, boolean input, boolean output)
{
this(resultSet, EnumSet.of(option), input, output);
}
/**
* Create an instance using a result set (for remote parameters).
*
* @param resultSet
* Result set containing input data.
* @param options
* Table parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE).
* @param input
* {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
* @param output
* {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
*/
public TableParameter(TableWrapper resultSet, EnumSet<ParameterOption> options, boolean input, boolean output)
{
BufferManager.registerScopeable(null);
this.table = null;
this.tableHandle = new handle();
this.resultSet = resultSet;
this.options = options;
this.remoteParameter = true;
this.input = input;
this.output = output;
// if INPUT, create a dynamic table
if (resultSet.isInput() && !resultSet.isFromJson() && resultSet.isAfterTable())
{
BufferImpl buff = new TempTableBuilder().createRemoteTable(resultSet.getTableName(),
resultSet.propertyIterator(),
resultSet.getIndexes(),
resultSet.getXmlns(),
resultSet.getXmlPrefix(),
resultSet.getSchemaMarshalLevel());
TemporaryBuffer.insertAllRows(resultSet,
(Temporary) buff,
resultSet.isAppend(),
TemporaryBuffer.CopyTableMode.INPUT_PARAM_MODE,
getParameterIndex());
this.table = (Temporary) buff;
}
}
/**
* Get the state of the {@link #isTableHandle} flag.
*
* @return See above.
*/
public boolean isTableHandle()
{
return isTableHandle;
}
/**
* Determines if this parameter is INPUT or INPUT-OUTPUT.
*
* @return {@code true} if this is INPUT or INPUT-OUTPUT.
*/
public boolean isInputMode()
{
return input;
}
/**
* Set the flag for whether this parameter is INPUT or INPUT-OUTPUT.
*
* @param input
* {@code true} if this is INPUT or INPUT-OUTPUT.
*/
public void setInputMode(boolean input)
{
this.input = input;
}
/**
* Determines if this parameter is OUTPUT or INPUT-OUTPUT.
*
* @return {@code true} if this is OUTPUT or INPUT-OUTPUT.
*/
public boolean isOutputMode()
{
return output;
}
/**
* Set the flag for whether this parameter is OUTPUT or INPUT-OUTPUT.
*
* @param output
* {@code true} if this is OUTPUT or INPUT-OUTPUT.
*/
public void setOutputMode(boolean output)
{
this.output = output;
}
/**
* Determines if this is a remote parameter.
*
* @return {@code true} if this is a remote parameter, {@code false} if this is a
* local parameter.
*/
public boolean isRemoteParameter()
{
return remoteParameter;
}
/**
* Get the result set (for remote parameters).
*
* @return Result set.
*/
public TableWrapper getResultSet()
{
return resultSet;
}
/**
* Set the result set (for remote parameters).
*
* @param resultSet
* Result set, normally containing output data.
*/
public void setResultSet(TableWrapper resultSet)
{
this.resultSet = resultSet;
}
/**
* Get the table-handle which references the wrapped table.
*
* @return The table-handle (contains the wrapped table).
*/
public handle getTableHandle()
{
return tableHandle;
}
/**
* Set the parameter's table.
*
* @param table
* The table
*/
public void setTable(Temporary table)
{
this.table = table;
}
/**
* Get the buffer of the wrapped table.
*
* @return buffer of the wrapped table.
*/
public Temporary getTable()
{
if (table == null)
{
if (tableHandle._isValid())
{
AbstractTempTable tt = (AbstractTempTable) tableHandle.getResource();
return (tt == null) ? null : (Temporary) tt.defaultBufferHandleNative();
}
else
{
return null;
}
}
else
{
return table;
}
}
/**
* Determines if the APPEND option was specified on the calling side.
*
* @return {@code true} if the APPEND option was specified on the calling side.
*/
public boolean isAppend()
{
return options.contains(ParameterOption.APPEND);
}
/**
* Determines if the {@code BY-VALUE} option was specified on the calling side.
*
* @return {@code true} if the {@code BY-VALUE} option was specified on the calling side.
*/
public boolean isByValue()
{
return options.contains(ParameterOption.BY_VALUE);
}
/**
* Determines if the {@code BIND} option was specified on the calling side.
*
* @return {@code true} if the {@code BIND} option was specified on the calling side.
*/
public boolean isBind()
{
return options.contains(ParameterOption.BIND);
}
/**
* Determines if the {@code BY-REFERENCE} option was specified on the calling side.
*
* @return {@code true} if the {@code BY-REFERENCE} option was specified on the calling side.
*/
public boolean isByReference()
{
return options.contains(ParameterOption.BY_REFERENCE);
}
/**
* Get the table parameter options for this argument.
*
* @return The table parameter options.
*/
public EnumSet<ParameterOption> getParameterOptions()
{
return options;
}
/**
* Set the table parameter options for this argument.
*
* @param options
* The table parameter options.
*/
public void setParameterOptions(EnumSet<ParameterOption> options)
{
this.options = options;
}
/**
* Get 1-based index of the table parameter (in declaration of the function).
*
* @return 1-based index of the table parameter or <code>0</code> if it is not defined.
*/
public int getParameterIndex()
{
return parameterIndex;
}
/** Get encoded table SCHEMA-MARSHAL attribute value.
*
* @return encoded table SCHEMA-MARSHAL attribute value.
*/
public int getSchemaMarshalLevel()
{
return schemaMarshalLevel;
}
/**
* Set 1-based index of the table parameter (in declaration of the function).
*
* @param parameterIndex
* 1-based index of the table parameter.
*/
public void setParameterIndex(int parameterIndex)
{
this.parameterIndex = parameterIndex;
}
/**
* Return <code>true</code> if the table parameter is valid. It can be invalid, say, if an unknown table
* handle has been assigned to it.
*
* @return <code>true</code> if the table parameter is valid.
*/
public boolean isValid()
{
return table != null || (tableHandle != null && tableHandle._isValid()) || resultSet != null;
}
}