summaryrefslogtreecommitdiffstats
path: root/wizards/source/sfdatabases/SF_Dataset.xba
diff options
context:
space:
mode:
Diffstat (limited to 'wizards/source/sfdatabases/SF_Dataset.xba')
-rw-r--r--wizards/source/sfdatabases/SF_Dataset.xba1672
1 files changed, 1672 insertions, 0 deletions
diff --git a/wizards/source/sfdatabases/SF_Dataset.xba b/wizards/source/sfdatabases/SF_Dataset.xba
new file mode 100644
index 0000000000..02b3d35aff
--- /dev/null
+++ b/wizards/source/sfdatabases/SF_Dataset.xba
@@ -0,0 +1,1672 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_Dataset" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === The SFDatabases library is one of the associated libraries. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+Option Compatible
+Option ClassModule
+
+Option Explicit
+
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+&apos;&apos;&apos; SF_Dataset
+&apos;&apos;&apos; ==========
+&apos;&apos;&apos; A dataset represents a set of tabular data produced by a database.
+&apos;&apos;&apos; In the user interface of LibreOffice a dataset corresponds with the data
+&apos;&apos;&apos; displayed in a form or a data sheet (table, query).
+&apos;&apos;&apos; To use datasets, the database instance must exist but the Base document may not be open.
+&apos;&apos;&apos;
+&apos;&apos;&apos; In the context of ScriptForge, a dataset may be created automatically by script code :
+&apos;&apos;&apos; - at any moment =&gt; in this case the Base document may or may not be open.
+&apos;&apos;&apos; - any SELECT SQL statement may define the dataset.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The proposed API supports next main purposes:
+&apos;&apos;&apos; - browse for- and backward through the dataset to get its content
+&apos;&apos;&apos; - update any record with new values
+&apos;&apos;&apos; - create new records or delete some.
+&apos;&apos;&apos; So-called &quot;CRUD&quot; operations (create, read, update, delete).
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation:
+&apos;&apos;&apos; A dataset is characterized by
+&apos;&apos;&apos; - the parent database
+&apos;&apos;&apos; - a table/query name or an SQL SELECT statement
+&apos;&apos;&apos; - the DirectSQL option to bypass the processing of SQL by LibreOffice
+&apos;&apos;&apos; - an optional filter
+&apos;&apos;&apos; - an optional sorting order
+&apos;&apos;&apos; 1) From a database class instance
+&apos;&apos;&apos; Dim db As Object, FileName As String, Dataset As Object, Dataset2 As Object
+&apos;&apos;&apos; Set db = CreateScriptService(&quot;SFDatabases.Database&quot;, FileName, , ReadOnly := False)
+&apos;&apos;&apos; Set Dataset = db.CreateDataset(&quot;myTable&quot;, DirectSql := False, Filter := &quot;[City]=&apos;Brussels&apos;&quot;)
+&apos;&apos;&apos; 2) From an existing dataset
+&apos;&apos;&apos; Set Dataset2 = Dataset.CreateDataset(Filter := &quot;[City]=&apos;Paris&apos;&quot;)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Dataset browsing with the MoveNext(), MovePrevious(), ... methods
+&apos;&apos;&apos; After creation of the dataset, the current record is positioned BEFORE the first record.
+&apos;&apos;&apos; Every MoveXXX() method returns False when no record could be retrieved, otherwise True.
+&apos;&apos;&apos; When False, the current record is reset either in BOF or EOF positions.
+&apos;&apos;&apos; Typically:
+&apos;&apos;&apos; Set dataset = db.CreateDataset(&quot;myTable&quot;)
+&apos;&apos;&apos; With Dataset
+&apos;&apos;&apos; Do While .MoveNext()
+&apos;&apos;&apos; ...
+&apos;&apos;&apos; Loop
+&apos;&apos;&apos; .CloseDataset()
+&apos;&apos;&apos; End With
+&apos;&apos;&apos;
+&apos;&apos;&apos; Updates performance:
+&apos;&apos;&apos; This module provides methods to update data stored in database tables.
+&apos;&apos;&apos; Note that the proposed Update() and Insert() methods will always be
+&apos;&apos;&apos; SLOWER or MUCH SLOWER than equivalent SQL statements.
+&apos;&apos;&apos; Always privilege SQL when considering massive updates.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/SF_Dataset.html?DbPAR=BASIC
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+&apos; Error in the dataset&apos;s initial SQL statement
+Private Const SQLSYNTAX2ERROR = &quot;SQLSYNTAX2ERROR&quot;
+&apos; The current record could not be determined
+Private Const NOCURRENTRECORDERROR = &quot;NOCURRENTRECORDERROR&quot;
+&apos; Database is read-only. Method rejected
+Private Const DBREADONLYERROR = &quot;DBREADONLYERROR&quot;
+&apos; Database fields update error
+&apos; Value to store does not fit the type of the field
+&apos; Field is not nullable and value = Null
+&apos; Field is not writable or autovalue
+&apos; Input file does not exist or is empty
+&apos; Field type is not supported
+Private Const RECORDUPDATEERROR = &quot;RECORDUPDATEERROR&quot;
+&apos; The destination file exists and cannot be overwritten
+Private Const FIELDEXPORTERROR = &quot;FIELDEXPORTERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private ObjectType As String &apos; Must be DATASET
+Private ServiceName As String
+
+Private _ParentDatabase As Object &apos; The parent SF_Database instance (must not be void)
+Private _DatasetType As String &apos; TABLE, QUERY or SQL
+Private _Command As String &apos; Table name, query name or SQL statement
+Private _Sql As String &apos; Equivalent SQL command
+Private _DirectSql As Boolean &apos; When True, SQL processed by RDBMS
+Private _Filter As String &apos; WHERE clause without WHERE
+Private _OrderBy As String &apos; ORDER BY clause without ORDER BY
+Private _ReadOnly As Boolean &apos; When True, updates are forbidden
+
+Private _RowSet As Object &apos; com.sun.star.sdb.RowSet
+
+Private _Fields As Variant &apos; Array of field names
+Private _UpdatableFields As Variant &apos; Array of updatable field names
+Private _DefaultValues As Variant &apos; Array of field default values // _Fields
+Private _AutoValue As Long &apos; Index of AutoValue field. None = -1
+
+Private _DatasetIndex As Long &apos; Index of the dataset in the _Datasets array of the parent database
+
+REM ============================================================ MODULE CONSTANTS
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ ObjectType = &quot;DATASET&quot;
+ ServiceName = &quot;SFDatabases.Dataset&quot;
+ Set _ParentDatabase = Nothing
+ _DatasetType = &quot;&quot;
+ _Command = &quot;&quot;
+ _DirectSql = False
+ _Filter = &quot;&quot;
+ _OrderBy = &quot;&quot;
+ _ReadOnly = False
+ Set _RowSet = Nothing
+ _Fields = Array()
+ _UpdatableFields = Array()
+ _DefaultValues = Array()
+ _AutoValue = -1
+ _DatasetIndex = -1
+End Sub &apos; SFDatabases.SF_Dataset Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDatabases.SF_Dataset Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDatabases.SF_Dataset Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get BOF() As Variant
+&apos;&apos;&apos; The BOF property returns True if the current record position is before the first record
+&apos;&apos;&apos; in the Dataset, otherwise it returns False.
+ Bof = _PropertyGet(&quot;BOF&quot;)
+End Property &apos; SFDatabases.SF_Dataset.BOF (get)
+
+REM -----------------------------------------------------------------------------
+Property Let BOF(Optional ByVal pvBOF As Variant)
+&apos;&apos;&apos; Set the updatable property BOF.
+&apos;&apos;&apos; Setting BOF to True positions the current record before the first record.
+&apos;&apos;&apos; Setting it to False is ignored. True is the only relevant value.
+ _PropertySet(&quot;BOF&quot;, pvBOF)
+End Property &apos; SFDatabases.SF_Dataset.BOF (let)
+
+REM -----------------------------------------------------------------------------
+Property Get DefaultValues() As Variant
+&apos;&apos;&apos; Returns a dictionary (field name =&gt; default value).
+&apos;&apos;&apos; The database field type is converted to the corresponding Basic/Python variable types.
+&apos;&apos;&apos; When undefined: returns either Null (field is nullable) or Empty
+&apos;&apos;&apos; The output dictionary should be disposed by the user script
+ DefaultValues = _PropertyGet(&quot;DefaultValues&quot;)
+End Property &apos; SFDatabases.SF_Dataset.DefaultValues (get)
+
+REM -----------------------------------------------------------------------------
+Property Get EOF() As Variant
+&apos;&apos;&apos; The EOF property returns True if the current record position is after the last record
+&apos;&apos;&apos; in the Dataset, otherwise it returns False.
+ EOF = _PropertyGet(&quot;EOF&quot;)
+End Property &apos; SFDatabases.SF_Dataset.EOF (get)
+
+REM -----------------------------------------------------------------------------
+Property Let EOF(Optional ByVal pvEOF As Variant)
+&apos;&apos;&apos; Set the updatable property EOF.
+&apos;&apos;&apos; Setting EOF to True positions the current record after the last record.
+&apos;&apos;&apos; Setting it to False is ignored. True is the only relevant value.
+ _PropertySet(&quot;EOF&quot;, pvEOF)
+End Property &apos; SFDatabases.SF_Dataset.EOF (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Fields() As Variant
+&apos;&apos;&apos; Returns the list of the field names contained in the dataset
+ Fields = _PropertyGet(&quot;Fields&quot;)
+End Property &apos; SFDatabases.SF_Dataset.Fields (get)
+
+REM -----------------------------------------------------------------------------
+Property Get Filter() As Variant
+&apos;&apos;&apos; The Filter is a SQL WHERE clause without the WHERE keyword
+ Filter = _PropertyGet(&quot;Filter&quot;)
+End Property &apos; SFDatabases.SF_Dataset.Filter (get)
+
+REM -----------------------------------------------------------------------------
+Property Get OrderBy() As Variant
+&apos;&apos;&apos; The OrderBy is an SQL ORDER BY clause without the ORDER BY keyword
+ OrderBy = _PropertyGet(&quot;OrderBy&quot;)
+End Property &apos; SFDatabases.SF_Dataset.OrderBy (get)
+
+REM -----------------------------------------------------------------------------
+Property Get ParentDatabase() As Object
+&apos;&apos;&apos; Returns the database instance to which the dataset belongs
+ Set ParentDatabase = _PropertyGet(&quot;ParentDatabase&quot;)
+End Property &apos; SFDatabases.SF_Dataset.ParentDatabase
+
+REM -----------------------------------------------------------------------------
+Property Get RowCount() As Long
+&apos;&apos;&apos; Returns the number of records present in the dataset
+&apos;&apos;&apos; When that number exceeds a certain limit, its determination requires
+&apos;&apos;&apos; that the whole dataset has been read first, up to its last row.
+&apos;&apos;&apos; For huge datasets, this can represent a significant performance cost.
+ RowCount = _PropertyGet(&quot;RowCount&quot;)
+End Property &apos; SFDatabases.SF_Dataset.RowCount
+
+REM -----------------------------------------------------------------------------
+Property Get RowNumber() As Long
+&apos;&apos;&apos; Returns the sequence number &gt;= 1 of the current record. Returns 0 if unknown.
+ RowNumber = _PropertyGet(&quot;RowNumber&quot;)
+End Property &apos; SFDatabases.SF_Dataset.RowNumber
+
+REM -----------------------------------------------------------------------------
+Property Get Source() As String
+&apos;&apos;&apos; Returns the source of the data: table name, query name or sql statement
+ Source = _PropertyGet(&quot;Source&quot;)
+End Property &apos; SFDatabases.SF_Dataset.Source
+
+REM -----------------------------------------------------------------------------
+Property Get SourceType() As String
+&apos;&apos;&apos; Returns the type of source of the data: TABLE, QUERY or SQL
+ SourceType = _PropertyGet(&quot;SourceType&quot;)
+End Property &apos; SFDatabases.SF_Dataset.SourceType
+
+REM -----------------------------------------------------------------------------
+Property Get UpdatableFields() As Variant
+&apos;&apos;&apos; Returns the list of the names of the updatable fields contained in the dataset
+ UpdatableFields = _PropertyGet(&quot;UpdatableFields&quot;)
+End Property &apos; SFDatabases.SF_Dataset.UpdatableFields (get)
+
+REM -----------------------------------------------------------------------------
+Property Get Values() As Variant
+&apos;&apos;&apos; Returns a dictionary (field name =&gt; field value) applied on the current record
+&apos;&apos;&apos; Binary fields ? =&gt; their length is returned
+&apos;&apos;&apos; The output dictionary should be disposed by the user script
+&apos;&apos;&apos; Returns Nothing when there is no current record
+ Values = _PropertyGet(&quot;Values&quot;)
+End Property &apos; SFDatabases.SF_Dataset.Values (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XRowSet() As Object
+&apos;&apos;&apos; Returns the com.sun.star.sdb.RowSet UNO object representing the dataset
+ XRowSet = _PropertyGet(&quot;XRowSet&quot;)
+End Property &apos; SFDocuments.SF_Document.XRowSet
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDataset() As Boolean
+&apos;&apos;&apos; Close the actual dataset
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; dataset.CloseDataset()
+
+Dim bClose As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Sataset.CloseDataset&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bClose = False
+
+Check:
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+Try:
+ If Not IsNull(_RowSet) Then
+ With _RowSet
+ .close()
+ .dispose()
+ End With
+ If _DatasetIndex &gt;= 0 Then Set _ParentDatabase._Datasets(_DatasetIndex) = Nothing
+ Dispose()
+ bClose = True
+ End If
+
+Finally:
+ CloseDataset = bClose
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.CloseDataset
+
+REM -----------------------------------------------------------------------------
+Public Function CreateDataset(Optional ByVal Filter As Variant _
+ , Optional ByVal OrderBy As Variant _
+ ) As Object
+&apos;&apos;&apos; Create and return a Dataset class instance based on the actual Dataset
+&apos;&apos;&apos; Filter and OrderBy properties may be redefined.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Filter: an additional condition that records must match, expressed
+&apos;&apos;&apos; as a valid SQL WHERE clause without the WHERE keyword
+&apos;&apos;&apos; Default: the filter applied on the actual dataset.
+&apos;&apos;&apos; OrderBy: the ordering of the dataset expressed as a valid SQL ORDER BY clause
+&apos;&apos;&apos; without the ORDER BY keywords.
+&apos;&apos;&apos; Default: the same sorting order as the actual dataset.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A SF_Dataset instance or Nothing when not successful
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; SQLSYNTAX2ERROR The given SQL statement is incorrect
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim ds1 As Object, ds2 As Object, ds3 As Object, ds4 As Object
+&apos;&apos;&apos; Set ds1 = dataset.CreateDataset() &apos; dataset and ds1 contain the same set of data
+&apos;&apos;&apos; Set ds2 = dataset.CreateDataset(Filter := &quot;&quot;) &apos; Suppress the current filter
+&apos;&apos;&apos; Set ds3 = dataset.CreateDataset(Filter := &quot;[Name] LIKE &apos;A%&apos;&quot;)
+&apos;&apos;&apos; &apos; Define a new filter
+&apos;&apos;&apos; Set ds4 = dataset.CreateDataset(Filter := &quot;(&quot; &amp; dataset.Filter &amp; &quot;) AND [Name] LIKE &apos;A%&apos;&quot;)
+&apos;&apos;&apos; &apos; Combine actual filter with an additional condition
+
+Dim oDataset As Object &apos; Return value
+
+Const cstThisSub = &quot;SFDatabases.Dataset.CreateDataset&quot;
+Const cstSubArgs = &quot;[Filter=&quot;&quot;...filter...&quot;&quot;], [OrderBy=&quot;&quot;...orderby...&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oDataset = Nothing
+
+Check:
+ If IsMissing(Filter) Or IsEmpty(Filter) Then Filter = _Filter
+ If IsMissing(OrderBy) Or IsEmpty(OrderBy) Then OrderBy = _OrderBy
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(Filter, &quot;Filter&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(OrderBy, &quot;OrderBy&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oDataset = New SF_Dataset
+ With oDataset
+ Set .[Me] = oDataset
+ Set ._ParentDatabase = _ParentDatabase
+ ._DatasetType = _DatasetType
+ ._Command = _Command
+ ._Sql = _Sql
+ ._DirectSql = _DirectSql
+ ._Filter = _ParentDatabase._ReplaceSquareBrackets(Filter)
+ ._OrderBy = _ParentDatabase._ReplaceSquareBrackets(OrderBy)
+ ._ReadOnly = _ReadOnly
+ &apos; If creation not successful, then cancel everything
+ If Not ._Initialize() Then Set oDataset = .Dispose()
+ End With
+
+Finally:
+ Set CreateDataset = oDataset
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.CreateDataset
+
+REM -----------------------------------------------------------------------------
+Public Function Delete() As Boolean
+&apos;&apos;&apos; Deletes the current record, from the dataset and from the database.
+&apos;&apos;&apos; The cursor is set on the record following immediately the deleted record,
+&apos;&apos;&apos; or after the last record if the deleted one was the last one.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DBREADONLYERROR The actual method cannot be executed
+&apos;&apos;&apos; NOCURRENTRECORDERROR The current record could not be determined
+&apos;&apos;&apos; Examples
+&apos;&apos;&apos; dataset.Delete()
+
+Dim bDelete As Boolean &apos; Return value
+Dim bLast As Boolean &apos; True when the current record is the last one
+Const cstThisSub = &quot;SFDatabases.Dataset.Delete&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bDelete = False
+
+ With _RowSet
+
+Check:
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If _ReadOnly Then GoTo CatchreadOnly
+ If .isBeforeFirst() Or .isAfterLast() Or .rowDeleted() Then GoTo CatchCurrent
+
+Try:
+ bLast = .isLast()
+ .deleteRow()
+ bDelete = .rowDeleted
+ If bLast Then .afterLast() Else .next()
+
+ End With
+
+Finally:
+ Delete = bDelete
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchCurrent:
+ ScriptForge.SF_Exception.RaiseFatal(NOCURRENTRECORDERROR)
+ GoTo Finally
+CatchReadOnly:
+ ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.Delete
+
+REM -----------------------------------------------------------------------------
+Public Function ExportValueToFile(Optional ByVal FieldName As Variant _
+ , Optional ByVal FileName As Variant _
+ , Optional ByVal Overwrite As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Export the content of a binary field to a given file
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FieldName: the name of a binary field as a case-sensitive string
+&apos;&apos;&apos; FileName: the destination file name in ScriptForge.FileSystem service notation
+&apos;&apos;&apos; Overwrite: when True, the destination file may be overwritten
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; NOCURRENTRECORDERROR The current record could not be determined
+&apos;&apos;&apos; FIELDEXPORTERROR The destination has its readonly attribute set or overwriting rejected
+
+Dim bExport As Variant &apos; Return value
+Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
+Dim sFile As String &apos; Alias of FileName
+Dim lColIndex As Long &apos; Column index
+Dim oColumn As Object &apos; com.sun.star.sdb.ODataColumn
+Dim oStream As Object &apos; com.sun.star.io.XInputStream
+Const cstThisSub = &quot;SFDatabases.Dataset.ExportValueToFile&quot;
+Const cstSubArgs = &quot;FieldName, FileName, [Overwrite=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bExport = False
+
+Check:
+ If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(FieldName, &quot;FieldName&quot;, V_STRING, _Fields) Then GoTo Catch
+ If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+ &apos; Check destination file overwriting
+ sFile = ConvertToUrl(FileName)
+ Set oSfa = ScriptForge.SF_Utils._GetUNOService(&quot;FileAccess&quot;)
+ If oSfa.exists(sFile) Then
+ If Not Overwrite Then GoTo CatchFile
+ If oSfa.isReadonly(sFile) Then GoTo CatchFile
+ End If
+
+ &apos; Check the current record
+ With _RowSet
+ If .isBeforeFirst() Or .isAfterLast() Or .rowDeleted() Then GoTo CatchCurrent
+ End With
+
+Try:
+ lColIndex = ScriptForge.SF_Array.IndexOf(_Fields, FieldName, CaseSensitive := True)
+ If lColIndex &gt;= 0 Then
+
+ &apos; Establish the input stream
+ Set oColumn = _RowSet.Columns.getByIndex(lColIndex)
+ With com.sun.star.sdbc.DataType
+ Select Case oColumn.Type
+ Case .BINARY, .VARBINARY, .LONGVARBINARY, .BLOB
+ Set oStream = oColumn.getBinaryStream()
+ &apos;Case .VARCHAR, .LONGVARCHAR, .CLOB
+ Case Else
+ Set oStream = Nothing
+ End Select
+ End With
+
+ &apos; Process NULL value
+ If Not IsNull(oStream) And oColumn.IsNullable = com.sun.star.sdbc.ColumnValue.NULLABLE Then
+ If oColumn.wasNull() Then
+ oStream.closeInput()
+ Set oStream = Nothing
+ End If
+ End If
+
+ &apos; Dump field into file
+ If Not IsNull(oStream) Then
+ If oStream.getLength() &gt; 0 Then
+ oSfa.writeFile(sFile, oStream)
+ End If
+ oStream.closeInput()
+ End If
+ End If
+
+ bExport = True
+
+Finally:
+ ExportValueToFile = bExport
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchCurrent:
+ ScriptForge.SF_Exception.RaiseFatal(NOCURRENTRECORDERROR)
+ GoTo Finally
+CatchFile:
+ ScriptForge.SF_Exception.RaiseFatal(FIELDEXPORTERROR, &quot;FileName&quot;, FileName, &quot;Overwrite&quot;, Overwrite)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.ExportValueToFile
+
+REM -----------------------------------------------------------------------------
+Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
+&apos;&apos;&apos; Return the actual value of the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The actual value of the propRATTCerty
+&apos;&apos;&apos; If the property does not exist, returns Null
+
+Const cstThisSub = &quot;SFDatabases.Dataset.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ GetProperty = Null
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ GetProperty = _PropertyGet(PropertyName)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function GetRows(Optional ByVal Header As Variant _
+ , Optional ByVal MaxRows As Variant _
+ ) As Variant
+&apos;&apos;&apos; Return the content of the dataset as an array
+&apos;&apos;&apos; This operation can be done in chunks:
+&apos;&apos;&apos; - The collected data starts at the current row + 1
+&apos;&apos;&apos; - When MaxRows &gt; 0 then the collection stops after this limit has been reached.
+&apos;&apos;&apos; Otherwise all the data up to the end is collected.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Header: When True, a header row is inserted at the top of the array with the column names. Default = False
+&apos;&apos;&apos; MaxRows: The maximum number of returned rows. If absent, all records up to the end are returned
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; a 2D array(row, column), even if only 1 column and/or 1 record
+&apos;&apos;&apos; an empty array if no records returned
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim a As Variant, lMaxRows As Long
+&apos;&apos;&apos; lMaxRows = 100
+&apos;&apos;&apos; Do
+&apos;&apos;&apos; a = dataset.GetRows(Header := True, MaxRows := lMaxRows)
+&apos;&apos;&apos; If UBound(a, 1) &gt;= 0 Then
+&apos;&apos;&apos; &apos; ...
+&apos;&apos;&apos; End If
+&apos;&apos;&apos; Loop Until UBound(a, 1) &lt; lMaxRows &apos; Includes empty array - Use ... &lt; lMaxRows - 1 when Header := False
+
+Dim vResult As Variant &apos; Return value
+Dim lCols As Long &apos; Number of columns
+Dim lRows As Long &apos; Number of rows
+Dim oColumns As Object &apos; Collection of com.sun.star.sdb.ODataColumn
+Dim bRead As Boolean &apos; When True, next record has been read successfully
+Dim i As Long
+Const cstThisSub = &quot;SFDatabases.Dataset.GetRows&quot;
+Const cstSubArgs = &quot;[Header=False], [MaxRows=0]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vResult = Array()
+
+Check:
+ If IsMissing(Header) Or IsEmpty(Header) Then Header = False
+ If IsMissing(MaxRows) Or IsEmpty(MaxRows) Then MaxRows = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(Header, &quot;Header&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(MaxRows, &quot;MaxRows&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+ If MaxRows &lt; 0 Then MaxRows = 1
+
+Try:
+ With _RowSet
+
+ &apos; Check if there is any data to collect
+ bRead = .next()
+
+ If bRead Then
+ &apos;Initialize output array with header row
+ Set oColumns = .getColumns()
+ lCols = oColumns.Count - 1
+ If Header Then
+ lRows = 0
+ ReDim vResult(0 To lRows, 0 To lCols)
+ For i = 0 To lCols
+ vResult(lRows, i) = oColumns.getByIndex(i).Name
+ Next i
+ If MaxRows &gt; 0 Then MaxRows = MaxRows + 1
+ Else
+ lRows = -1
+ End If
+
+ &apos; Load data
+ Do While bRead
+ lRows = lRows + 1
+ If lRows = 0 Then
+ ReDim vResult(0 To lRows, 0 To lCols)
+ Else
+ ReDim Preserve vResult(0 To lRows, 0 To lCols)
+ End If
+ For i = 0 To lCols
+ vResult(lRows, i) = _ParentDatabase._GetColumnValue(_RowSet, i + 1)
+ Next i
+ If MaxRows = 0 Or lRows &lt; MaxRows - 1 Then bRead = .next() Else bRead = False
+ Loop
+
+ Else
+ vResult = Array()
+ End If
+
+ End With
+
+Finally:
+ GetRows = vResult
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.GetRows
+
+REM -----------------------------------------------------------------------------
+Public Function GetValue(Optional ByVal FieldName As Variant) As Variant
+&apos;&apos;&apos; Returns the value of a given field in the current record
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FieldName: the name of a field as a case-sensitive string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The found value as a Basic variable
+&apos;&apos;&apos; The length of binary fields is returned,not their content.
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; NOCURRENTRECORDERROR The current record could not be determined
+
+Dim vValue As Variant &apos; Return value
+Dim lColIndex As Long &apos; Column index
+Const cstThisSub = &quot;SFDatabases.Dataset.GetValue&quot;
+Const cstSubArgs = &quot;FieldName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vValue = Null
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(FieldName, &quot;FieldName&quot;, V_STRING, _Fields) Then GoTo Catch
+ End If
+
+ With _RowSet
+ If .isBeforeFirst() Or .isAfterLast() Or .rowDeleted() Then GoTo CatchCurrent
+ End With
+
+Try:
+ lColIndex = ScriptForge.SF_Array.IndexOf(_Fields, FieldName, CaseSensitive := True)
+ If lColIndex &gt;= 0 Then vValue = _ParentDatabase._GetColumnValue(_RowSet, lColIndex + 1)
+
+Finally:
+ GetValue = vValue
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchCurrent:
+ ScriptForge.SF_Exception.RaiseFatal(NOCURRENTRECORDERROR)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.GetValue
+
+REM -----------------------------------------------------------------------------
+Public Function Insert(ParamArray pvArgs As Variant) As Long
+&apos;&apos;&apos; Create a new record in the database and initialize its fields.
+&apos;&apos;&apos; The current record is unchanged. The new record is inserted at the end of the dataset.
+&apos;&apos;&apos; Updatable fields not mentioned in the arguments are initialized with their default value.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Either a single argument
+&apos;&apos;&apos; UpdatesList: a ScriptForge.SF_Dictionary object listing pairs of key/item where
+&apos;&apos;&apos; the key = an updatable field
+&apos;&apos;&apos; the item = its value
+&apos;&apos;&apos; or an even number of arguments alternating
+&apos;&apos;&apos; FieldName: an updatable field
+&apos;&apos;&apos; FieldValue: its value
+&apos;&apos;&apos; The first form is particularly convenient in Python scripts
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; When the primary key is an AutoValue field: the autovalue of the new record
+&apos;&apos;&apos; (to facilitate the use of the new primary key in foreign keys)
+&apos;&apos;&apos; Otherwise: 0 (= successful), -1 (= not successful)
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DBREADONLYERROR The actual method cannot be executed
+&apos;&apos;&apos; RECORDUPDATEERROR When value to store does not fit the type of the field
+&apos;&apos;&apos; or when field is not nullable and value = Null
+&apos;&apos;&apos; or when field is not writable or is an autovalue
+&apos;&apos;&apos; or when input file does not exist or is empty
+&apos;&apos;&apos; or when field type is not supported
+&apos;&apos;&apos; TABLEPRIMARYKEYERROR Primary key duplication
+&apos;&apos;&apos; Examples
+&apos;&apos;&apos; (Basic)
+&apos;&apos;&apos; Dim newID As Long
+&apos;&apos;&apos; newID = dataset.Insert(&quot;LastName&quot;, &quot;Doe&quot;, &quot;FirstName&quot;, &quot;John&quot;)
+&apos;&apos;&apos; &apos; ... is equivalent to:
+&apos;&apos;&apos; Dim dict As Object, newID As Long
+&apos;&apos;&apos; Set dict = CreateScriptService(&quot;ScriptForge.Dictionary&quot;)
+&apos;&apos;&apos; dict.Add(&quot;LastName&quot;, &quot;Doe&quot;)
+&apos;&apos;&apos; dict.Add(&quot;FirstName&quot;, &quot;John&quot;)
+&apos;&apos;&apos; newID = dataset.Insert(dict)
+&apos;&apos;&apos; (Python) - next statements are equivalent
+&apos;&apos;&apos; newid = dataset.Insert(&apos;LastName&apos;, &apos;Doe&apos;, &apos;FirstName&apos;, &apos;John&apos;)
+&apos;&apos;&apos; newid = dataset.Insert({&apos;LastName&apos;: &apos;Doe&apos;, &apos;FirstName&apos;: &apos;John&apos;})
+&apos;&apos;&apos; newid = dataset.Insert(dict(LastName = &apos;Doe&apos;, FirstName = &apos;John&apos;))
+&apos;&apos;&apos; newid = dataset.Insert(LastName = &apos;Doe&apos;, FirstName = &apos;John&apos;)
+
+Dim lInsert As Long &apos; Return value
+Dim sSubArgs As String &apos; Alias of cstSubArgs
+Dim sField As String &apos; A single field name
+Dim oUpdates As Object &apos; A SF_Dictionary object
+Dim lColIndex As Long &apos; Column index
+Dim vKeys As Variant &apos; List of keys in the dictionary
+Dim sKey As String &apos; A single key in vKeys
+Dim i As Long
+Const cstThisSub = &quot;SFDatabases.Dataset.Insert&quot;
+Const cstSubArgs1 = &quot;UpdatesList&quot;
+Const cstSubArgs2 = &quot;FieldName1, FieldValue1, [FieldName2, FieldValue2], ...&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ lInsert = -1
+
+Check:
+ If UBound(pvArgs) = 0 Then &apos; Dictionary case
+ sSubArgs = cstSubArgs1
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, sSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(pvArgs(0), &quot;UpdatesList&quot;, ScriptForge.V_OBJECT) Then GoTo Catch
+ End If
+ Set oUpdates = pvArgs(0)
+ Else
+ sSubArgs = cstSubArgs2 &apos; Arguments list case
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, sSubArgs)
+ For i = 0 To UBound(pvArgs) Step 2
+ If Not ScriptForge.SF_Utils._Validate(pvArgs(i), &quot;FieldName&quot; &amp; i, V_STRING, _UpdatableFields) Then GoTo Catch
+ Next i
+ End If
+
+ If _ReadOnly Then GoTo CatchReadOnly
+
+Try:
+ With _RowSet
+
+ &apos; Initialize the insertion row
+ .moveToInsertRow()
+ &apos; Initial storage of default values
+ For Each sField In _UpdatableFields
+ lColIndex = ScriptForge.SF_Array.IndexOf(_Fields, sField, CaseSensitive := True)
+ _SetColumnValue(lColIndex, _DefaultValues(lColIndex))
+ Next sField
+
+ If UBound(pvArgs) = 0 Then
+ With oUpdates
+ vKeys = .Keys
+ For Each sKey in vKeys
+ lColIndex = ScriptForge.SF_Array.IndexOf(_Fields, sKey, CaseSensitive := True)
+ If lColIndex &gt;= 0 Then
+ _SetColumnValue(lColIndex, .Item(sKey))
+ Else &apos; To force an error
+ If Not ScriptForge.SF_Utils._Validate(sKey, &quot;FieldName&quot;, V_STRING, _UpdatableFields) Then GoTo Catch
+ End If
+ Next sKey
+ End With
+ Else
+ For i = 0 To UBound(pvArgs) Step 2
+ lColIndex = ScriptForge.SF_Array.IndexOf(_Fields, pvArgs(i), CaseSensitive := True)
+ If lColIndex &gt;= 0 Then
+ If i &lt; UBound(pvArgs) Then _SetColumnValue(lColIndex, pvArgs(i + 1))
+ Else &apos; To force an error
+ If Not ScriptForge.SF_Utils._Validate(sKey, &quot;FieldName&quot;, V_STRING, _UpdatableFields) Then GoTo Catch
+ End If
+ Next i
+ End If
+
+ .insertRow()
+
+ &apos; Compute the return value: either 0 or the new content of the pre-identified AUtoValue field
+ If _AutoValue &lt; 0 Then lInsert = 0 Else lInsert = _ParentDatabase._GetColumnValue(_RowSet, _AutoValue + 1)
+
+ .moveToCurrentRow()
+
+ End With
+
+Finally:
+ Insert = lInsert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchReadOnly:
+ ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.Insert
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Model service as an array
+
+ Methods = Array( _
+ &quot;CloseDataset&quot; _
+ , &quot;CreateDataset&quot; _
+ , &quot;Delete&quot; _
+ , &quot;ExportValueToFile&quot; _
+ , &quot;GetRows&quot; _
+ , &quot;GetValue&quot; _
+ , &quot;Insert&quot; _
+ , &quot;MoveFirst&quot; _
+ , &quot;MoveLast&quot; _
+ , &quot;MoveNext&quot; _
+ , &quot;MovePrevious&quot; _
+ , &quot;Reload&quot; _
+ , &quot;Update&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Dataset.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function MoveFirst() As Boolean
+&apos;&apos;&apos; Move the cursor to the 1st record
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False when the Move was unsuccessful
+&apos;&apos;&apos; When False the cursor is reset before the first record
+
+Dim bMove As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Dataset.MoveFirst&quot;
+Const cstSubArgs = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+Try:
+ with _RowSet
+ bMove = .first()
+ If Not bMove Then .beforeFirst()
+ End With
+
+Finally:
+ MoveFirst = bMove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.MoveFirst
+
+REM -----------------------------------------------------------------------------
+Public Function MoveLast() As Boolean
+&apos;&apos;&apos; Move the cursor to the last record
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False when the Move was unsuccessful
+&apos;&apos;&apos; When False the cursor is reset before the first record
+
+Dim bMove As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Dataset.MoveLast&quot;
+Const cstSubArgs = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+Try:
+ with _RowSet
+ bMove = .last()
+ If Not bMove Then .beforeFirst()
+ End With
+
+Finally:
+ MoveLast = bMove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.MoveLast
+
+REM -----------------------------------------------------------------------------
+Public Function MoveNext(Optional ByVal Offset As Variant) As Boolean
+&apos;&apos;&apos; Move the cursor N records forward. Deleted records are skipped.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Offset: number of records to go forward (may be negative). Default = 1
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False when the Move was unsuccessful
+&apos;&apos;&apos; When False the cursor is reset before the first record when Offset &gt; 0, after the last record otherwise
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; dataset.MoveNext(3) &apos; 3 records forward
+&apos;&apos;&apos; dataset.MoveNext(-1) &apos; equivalent to MovePrevious()
+
+Dim bMove As Boolean &apos; Return value
+Dim lRow As Long &apos; Row number
+Const cstThisSub = &quot;SFDatabases.Dataset.MoveNext&quot;
+Const cstSubArgs = &quot;[Offset=1]&quot;
+
+Check:
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ If IsMissing(Offset) Or IsEmpty(Offset) Then Offset = 1
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(Offset, &quot;Offset&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ End If
+
+Try:
+ with _RowSet
+ Select Case Offset
+ Case 0 : bMove = True
+ Case 1 : bMove = .next()
+ Case -1 : bMove = .previous()
+ Case &gt; 1 : bMove = .relative(Offset) &apos; RowSet.relative() stops at boundary when moving forward only !?
+ Case Else &apos; &lt; -1
+ lRow = .Row()
+ If lRow &gt; Abs(Offset) Then bMove = .relative(Offset) Else bMove = False
+ End Select
+ If bMove Then
+ If .rowDeleted() Then
+ If Offset &gt;= 0 Then bMove = MoveNext() Else bMove = MovePrevious()
+ End If
+ End If
+ End With
+
+Finally:
+ MoveNext = bMove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.MoveNext
+
+REM -----------------------------------------------------------------------------
+Public Function MovePrevious(Optional ByVal Offset As Variant) As Boolean
+&apos;&apos;&apos; Move the cursor N records backward. Deleted records are skipped.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Offset: number of records to go backward (may be negative). Default = 1
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False when the Move was unsuccessful
+&apos;&apos;&apos; When False the cursor is reset before the first record
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; dataset.MovePrevious(3) &apos; 3 records backward
+&apos;&apos;&apos; dataset.MovePrevious(-1) &apos; equivalent to MoveNext()
+
+Dim bMove As Boolean &apos; Return value
+Dim lRow As Long &apos; Row number
+Const cstThisSub = &quot;SFDatabases.Dataset.MovePrevious&quot;
+Const cstSubArgs = &quot;[Offset=1]&quot;
+
+Check:
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ If IsMissing(Offset) Or IsEmpty(Offset) Then Offset = 1
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(Offset, &quot;Offset&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ End If
+
+Try:
+ with _RowSet
+ Select Case Offset
+ Case 0 : bMove = True
+ Case 1 : bMove = .previous()
+ Case -1 : bMove = .next()
+ Case &lt; -1 : bMove = .relative(- Offset) &apos; RowSet.relative() stops at boundary when moving forward only !?
+ Case Else &apos; &gt; 1
+ lRow = .Row()
+ If lRow &gt; Offset Then bMove = .relative(- Offset) Else bMove = False
+ End Select
+ If bMove Then
+ If .rowDeleted() Then
+ If Offset &lt; 0 Then bMove = MoveNext() Else bMove = MovePrevious()
+ End If
+ End If
+ End With
+
+Finally:
+ MovePrevious = bMove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.MovePrevious
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Model class as an array
+
+ Properties = Array( _
+ &quot;BOF&quot; _
+ , &quot;DefaultValues&quot; _
+ , &quot;EOF&quot; _
+ , &quot;Fields&quot; _
+ , &quot;Filter&quot; _
+ , &quot;OrderBy&quot; _
+ , &quot;ParentDatabase&quot; _
+ , &quot;RowCount&quot; _
+ , &quot;RowNumber&quot; _
+ , &quot;Source&quot; _
+ , &quot;SourceType&quot; _
+ , &quot;UpdatableFields&quot; _
+ , &quot;Values&quot; _
+ , &quot;XRowSet&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Dataset.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function Reload(Optional ByVal Filter As Variant _
+ , Optional ByVal OrderBy As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Reload the dataset from the database.
+&apos;&apos;&apos; Useful in particular after record deletions and insertions.
+&apos;&apos;&apos; Filter and OrderBy properties may be redefined.
+&apos;&apos;&apos; The cursor is reset before the first record.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Filter: a condition that records must match, expressed
+&apos;&apos;&apos; as a valid SQL WHERE clause without the WHERE keyword
+&apos;&apos;&apos; Default: the actual filter is left unchanged.
+&apos;&apos;&apos; OrderBy: the ordering of the dataset expressed as a valid SQL ORDER BY clause
+&apos;&apos;&apos; without the ORDER BY keywords.
+&apos;&apos;&apos; Default: the actual sorting order is left unchanged.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; SQLSYNTAX2ERROR The given SQL statement is incorrect
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; dataset.Reload() &apos; dataset is refreshed
+&apos;&apos;&apos; dataset.Reload(Filter := &quot;&quot;) &apos; Suppress the current filter
+&apos;&apos;&apos; dataset.Reload(Filter := &quot;[Name] LIKE &apos;A%&apos;&quot;)
+&apos;&apos;&apos; &apos; Define a new filter
+&apos;&apos;&apos; dataset.Reload(Filter := &quot;(&quot; &amp; dataset.Filter &amp; &quot;) AND [Name] LIKE &apos;A%&apos;&quot;)
+&apos;&apos;&apos; &apos; Combine actual filter with an additional condition
+
+Dim bReload As Boolean &apos; Return value
+Dim bErrorHandler As Boolean &apos; Can be set off to ease debugging of complex SQL statements
+Const cstThisSub = &quot;SFDatabases.Dataset.Reload&quot;
+Const cstSubArgs = &quot;[Filter=&quot;&quot;...filter...&quot;&quot;], [OrderBy=&quot;&quot;...orderby...&quot;&quot;]&quot;
+
+ bErrorHandler = ScriptForge.SF_Utils._ErrorHandling()
+ If bErrorHandler Then On Local Error GoTo Catch
+ bReload = False
+
+Check:
+ If IsMissing(Filter) Or IsEmpty(Filter) Then Filter = _Filter
+ If IsMissing(OrderBy) Or IsEmpty(OrderBy) Then OrderBy = _OrderBy
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(Filter, &quot;Filter&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(OrderBy, &quot;OrderBy&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ If Len(Filter) &gt; 0 Then _Filter = _ParentDatabase._ReplaceSquareBrackets(Filter) Else _Filter = &quot;&quot;
+ If Len(OrderBy) &gt; 0 Then _OrderBy = _ParentDatabase._ReplaceSquareBrackets(OrderBy) Else _OrderBy = &quot;&quot;
+ With _RowSet
+ .Filter = _Filter
+ .ApplyFilter = ( Len(_Filter) &gt; 0 )
+ .Order = _OrderBy
+ If bErrorhandler Then On Local Error GoTo CatchSql
+ .execute()
+ End With
+
+ bReload = True
+
+Finally:
+ Reload = bReload
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchSql:
+ On Local Error GoTo 0
+ ScriptForge.SF_Exception.RaiseFatal(SQLSYNTAX2ERROR, _Sql, &quot;WHERE &quot; &amp; _Filter, &quot;ORDER BY &quot; &amp; _OrderBy)
+ GoTo Catch
+End Function &apos; SFDatabases.SF_Dataset.Reload
+
+REM -----------------------------------------------------------------------------
+Public Function SetProperty(Optional ByVal PropertyName As Variant _
+ , Optional ByRef Value As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set a new value to the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Value: its new value
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+
+Const cstThisSub = &quot;SFDatabases.Dataset.SetProperty&quot;
+Const cstSubArgs = &quot;PropertyName, Value&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ SetProperty = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ SetProperty = _PropertySet(PropertyName, Value)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.SetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Update(ParamArray pvArgs As Variant) As Boolean
+&apos;&apos;&apos; Updates a set of fields in the current record
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Either a single argument
+&apos;&apos;&apos; UpdatesList: a ScriptForge.SF_Dictionary object listing pairs of key/item where
+&apos;&apos;&apos; the key = an updatable field
+&apos;&apos;&apos; the item = its new value
+&apos;&apos;&apos; or an even number of arguments alternating
+&apos;&apos;&apos; FieldName: an updatable field
+&apos;&apos;&apos; FieldValue: its new value
+&apos;&apos;&apos; The first form is particularly convenient in Python scripts
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DBREADONLYERROR The actual method cannot be executed
+&apos;&apos;&apos; RECORDUPDATEERROR When value to store does not fit the type of the field
+&apos;&apos;&apos; or when field is not nullable and value = Null
+&apos;&apos;&apos; or when field is not writable or is an autovalue
+&apos;&apos;&apos; or when input file does not exist or is empty
+&apos;&apos;&apos; or when field type is not supported
+&apos;&apos;&apos; NOCURRENTRECORDERROR The current record could not be determined
+&apos;&apos;&apos; Examples
+&apos;&apos;&apos; (Basic)
+&apos;&apos;&apos; dataset.Update(&quot;LastName&quot;, &quot;Doe&quot;, &quot;FirstName&quot;, &quot;John&quot;)
+&apos;&apos;&apos; &apos; ... is equivalent to:
+&apos;&apos;&apos; Dim dict As Object
+&apos;&apos;&apos; Set dict = CreateScriptService(&quot;ScriptForge.Dictionary&quot;)
+&apos;&apos;&apos; dict.Add(&quot;LastName&quot;, &quot;Doe&quot;)
+&apos;&apos;&apos; dict.Add(&quot;FirstName&quot;, &quot;John&quot;)
+&apos;&apos;&apos; dataset.Update(dict)
+&apos;&apos;&apos; (Python) - next statements are equivalent
+&apos;&apos;&apos; dataset.Update({&apos;LastName&apos;: &apos;Doe&apos;, &apos;FirstName&apos;: &apos;John&apos;})
+&apos;&apos;&apos; dataset.Update(dict(LastName = &apos;Doe&apos;, FirstName = &apos;John&apos;))
+&apos;&apos;&apos; dataset.Update(LastName = &apos;Doe&apos;, FirstName = &apos;John&apos;)
+
+Dim bUpdate As Boolean &apos; Return value
+Dim sSubArgs As String &apos; Alias of cstSubArgs
+Dim oUpdates As Object &apos; A SF_Dictionary object
+Dim lColIndex As Long &apos; Column index
+Dim vKeys As Variant &apos; List of keys in the dictionary
+Dim sKey As String &apos; A single key in vKeys
+Dim i As Long
+Const cstThisSub = &quot;SFDatabases.Dataset.Update&quot;
+Const cstSubArgs1 = &quot;UpdatesList&quot;
+Const cstSubArgs2 = &quot;FieldName1, FieldValue1, [FieldName2, FieldValue2], ...&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bUpdate = False
+
+Check:
+ If UBound(pvArgs) = 0 Then &apos; Dictionary case
+ sSubArgs = cstSubArgs1
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, sSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(pvArgs(0), &quot;UpdatesList&quot;, ScriptForge.V_OBJECT) Then GoTo Catch
+ End If
+ Set oUpdates = pvArgs(0)
+ Else
+ sSubArgs = cstSubArgs2 &apos; Arguments list case
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, sSubArgs)
+ For i = 0 To UBound(pvArgs) Step 2
+ If Not ScriptForge.SF_Utils._Validate(pvArgs(i), &quot;FieldName&quot; &amp; i, V_STRING, _UpdatableFields) Then GoTo Catch
+ Next i
+ End If
+
+ If _ReadOnly Then GoTo CatchReadOnly
+ With _RowSet
+ If .isBeforeFirst() Or .isAfterLast() Or .rowDeleted() Then GoTo CatchCurrent
+ End With
+
+Try:
+ If UBound(pvArgs) = 0 Then
+ With oUpdates
+ vKeys = .Keys
+ For Each sKey in vKeys
+ lColIndex = ScriptForge.SF_Array.IndexOf(_Fields, sKey, CaseSensitive := True)
+ If lColIndex &gt;= 0 Then
+ _SetColumnValue(lColIndex, .Item(sKey))
+ Else &apos; To force an error
+ If Not ScriptForge.SF_Utils._Validate(sKey, &quot;FieldName&quot;, V_STRING, _UpdatableFields) Then GoTo Catch
+ End If
+ Next sKey
+ End With
+ Else
+ For i = 0 To UBound(pvArgs) Step 2
+ lColIndex = ScriptForge.SF_Array.IndexOf(_Fields, pvArgs(i), CaseSensitive := True)
+ If lColIndex &gt;= 0 Then
+ If i &lt; UBound(pvArgs) Then _SetColumnValue(lColIndex, pvArgs(i + 1))
+ Else &apos; To force an error
+ If Not ScriptForge.SF_Utils._Validate(sKey, &quot;FieldName&quot;, V_STRING, _UpdatableFields) Then GoTo Catch
+ End If
+ Next i
+ End If
+
+ If _RowSet.IsModified Then _RowSet.updateRow()
+ bUpdate = True
+
+Finally:
+ Update = bUpdate
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchCurrent:
+ ScriptForge.SF_Exception.RaiseFatal(NOCURRENTRECORDERROR)
+ GoTo Finally
+CatchReadOnly:
+ ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset.Update
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Private Function _ConvertDefaultValue(ByRef poColumn As Object) As Variant
+&apos;&apos;&apos; Default values of table fields are stored in the Base file or in the database as strings.
+&apos;&apos;&apos; The actual method converts those strings into a Basic native type.
+&apos;&apos;&apos; Usage: facilitate the initialization of new records
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; poColumn: a com.sun.star.sdb.ODataColumn UNO object
+&apos;&apos;&apos; Returns
+&apos;&apos;&apos; The default value for the column expressed as a string, a number, a date, ...
+&apos;&apos;&apos; Nullable columns have probably a Null default value.
+
+Dim sValue As String &apos; The default value as a string
+Dim vValue As Variant &apos; The default value as a native Basic type
+Dim SESSION As Object : Set SESSION = ScriptForge.SF_Session
+
+Try:
+ With poColumn
+
+ &apos; Determine the default value as a string
+ If SESSION.HasUnoProperty(poColumn, &quot;DefaultValue&quot;) Then &apos; Default value in database set via SQL statement
+ sValue = .DefaultValue
+ ElseIf SESSION.HasUnoProperty(poColumn, &quot;ControlDefault&quot;) Then &apos; Default value set in Base via table edition
+ If IsEmpty(.ControlDefault) Then sValue = &quot;&quot; Else sValue = .ControlDefault
+ Else
+ sValue = &quot;&quot;
+ End If
+
+ &apos; Convert the string to a native type
+ If sValue = &quot;&quot; Then &apos; No default value =&gt; Null or Empty
+ If .IsNullable = com.sun.star.sdbc.ColumnValue.NULLABLE Then vValue = Null Else vValue = Empty
+ Else
+ vValue = sValue
+ With com.sun.star.sdbc.DataType
+ Select Case poColumn.Type
+ Case .CHAR, .VARCHAR, .LONGVARCHAR
+ Case .BIT, .BOOLEAN : vValue = CBool( sValue = &quot;1&quot; )
+ Case .TINYINT : vValue = CInt(sValue)
+ Case .SMALLINT, .INTEGER, .BIGINT : vValue = CLng(sValue)
+ Case .FLOAT : vValue = CSng(sValue)
+ Case .REAL, .DOUBLE : vValue = CDbl(sValue)
+ Case .NUMERIC, .DECIMAL
+ If SESSION.HasUnoProperty(poColumn, &quot;Scale&quot;) Then
+ If poColumn.Scale &gt; 0 Then vValue = CDbl(sValue)
+ End If
+ Case .DATE : vValue = DateValue(sValue)
+ Case .TIME : vValue = TimeValue(sValue)
+ Case .TIMESTAMP : vValue = DateValue(sValue) + TimeValue(sValue)
+ Case .BINARY, .VARBINARY, .LONGVARBINARY, .BLOB
+ Case .CLOB
+ Case Else
+ End Select
+ End With
+ End If
+
+ End With
+
+Finally:
+ _ConvertDefaultValue = vValue
+ Exit Function
+End Function &apos; SFDatabases.SF_Dataset._ConvertDefaultValue
+
+REM -----------------------------------------------------------------------------
+Public Function _Initialize() As Boolean
+&apos;&apos;&apos; Called immediately after instance creation to complete the initial values
+&apos;&apos;&apos; An eventual error must be trapped in the calling routine to cancel the instance creation
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False when Dataset creation is unsuccessful. Typically because of SQL error
+
+Dim bDataset As Boolean &apos; Return value
+Dim bErrorHandler As Boolean &apos; Can be set off to ease debugging of complex SQL statements
+Dim sFields As String &apos; Comma-separated list of field names
+Dim sUpdatableFields As String &apos; Comma-separated list of updatable field names
+Dim oColumn As Object &apos; com.sun.star.sdb.ODataColumn
+Dim SESSION As Object : Set SESSION = ScriptForge.SF_Services.CreateScriptService(&quot;ScriptForge.Session&quot;)
+Dim i As Long
+
+ bErrorHandler = ScriptForge.SF_Utils._ErrorHandling()
+ If bErrorHandler Then On Local Error GoTo Catch
+
+Try:
+ Set _RowSet = CreateUnoService(&quot;com.sun.star.sdb.RowSet&quot;)
+ With _RowSet
+ Set .ActiveConnection = _ParentDatabase._Connection
+ .Command = _Sql
+ Select Case _DatasetType
+ Case &quot;TABLE&quot; : .CommandType = com.sun.star.sdb.CommandType.TABLE
+ Case &quot;QUERY&quot; : .CommandType = com.sun.star.sdb.CommandType.QUERY
+ Case &quot;SQL&quot; : .CommandType = com.sun.star.sdb.CommandType.COMMAND
+ End Select
+
+ .EscapeProcessing = Not _DirectSql
+ .Filter = _Filter
+ .ApplyFilter = ( Len(_Filter) &gt; 0 )
+ .order = _OrderBy
+ If _ReadOnly Then
+ .TransactionIsolation = com.sun.star.sdbc.TransactionIsolation.READ_UNCOMMITTED
+ Else
+ .TransactionIsolation = com.sun.star.sdbc.TransactionIsolation.READ_COMMITTED
+ End If
+
+ If bErrorHandler Then On Local Error GoTo CatchSql
+ .execute()
+
+ If bErrorHandler Then On Local Error GoTo Catch
+ &apos; Collect info about columns: field name, updatable, default value, AutoValue
+ With .Columns
+ sFields = &quot;&quot;
+ sUpdatableFields = &quot;&quot;
+ ReDim _DefaultValues (0 To .Count - 1)
+ &apos; Columns are scanned by index to guarantee that names and indexes are aligned
+ For i = 0 To .Count - 1
+ Set oColumn = .getByIndex(i)
+ With oColumn
+ &apos; Field names
+ sFields = sFields &amp; &quot;,&quot; &amp; .Name
+ &apos; Updatable field names
+ If Not _ReadOnly And .IsWritable And Not .IsAutoIncrement Then sUpdatableFields = sUpdatableFields &amp; &quot;,&quot; &amp; .Name
+ &apos; Default values
+ _DefaultValues(i) = _ConvertDefaultValue(oColumn)
+ &apos; AutoValue
+ If _AutoValue &lt; 0 And .IsAutoIncrement Then _AutoValue = i
+ End With
+ Next i
+ If Len(sFields) &lt;= 1 Then _Fields = Array() Else _Fields = Split(Mid(sFields, 2), &quot;,&quot;)
+ If Len(sUpdatableFields) &lt;= 1 Then _UpdatableFields = Array() Else _UpdatableFields = Split(Mid(sUpdatableFields, 2), &quot;,&quot;)
+ End With
+ End With
+
+ &apos; Insert the instance in the _Datasets array of the parent database
+ _DatasetIndex = _ParentDatabase._AddToDatasets([Me])
+
+ bDataset = ( _DatasetIndex &gt;= 0 )
+
+Finally:
+ _Initialize = bDataset
+ Exit Function
+Catch:
+ bDataset = False
+ GoTo Finally
+CatchSql:
+ On Local Error GoTo 0
+ ScriptForge.SF_Exception.RaiseFatal(SQLSYNTAX2ERROR, _Sql, &quot;WHERE &quot; &amp; _Filter, &quot;ORDER BY &quot; &amp; _OrderBy)
+ GoTo Catch
+End Function &apos; SFDatabases.SF_Dataset._Initialize
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertyGet(Optional ByVal psProperty As String) As Variant
+&apos;&apos;&apos; Return the value of the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+
+Dim vBookmark As Variant &apos; Bookmark on the current record
+Dim vValue As Variant &apos; A single record field value
+Dim vValuesDict As Object &apos; A dictionary (field name, field value)
+Dim i As Long
+
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ cstThisSub = &quot;SFDatabases.Dataset.get&quot; &amp; psProperty
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+ With _RowSet
+ Select Case psProperty
+ Case &quot;BOF&quot;
+ _PropertyGet = .isBeforeFirst()
+ Case &quot;DefaultValues&quot;
+ &apos; Load the pairs field name / field default value in the dictionary
+ vValuesDict = ScriptForge.SF_Services.CreateScriptService(&quot;ScriptForge.Dictionary&quot;)
+ For i = 0 To UBound(_DefaultValues)
+ vValuesDict.Add(_Fields(i), _DefaultValues(i))
+ Next i
+ Set _PropertyGet = vValuesDict
+ Case &quot;EOF&quot;
+ _PropertyGet = .isAfterLast()
+ Case &quot;Fields&quot;
+ _PropertyGet = _Fields
+ Case &quot;Filter&quot;
+ _PropertyGet = _Filter
+ Case &quot;OrderBy&quot;
+ _PropertyGet = _OrderBy
+ Case &quot;ParentDatabase&quot;
+ Set _PropertyGet = _ParentDatabase
+ Case &quot;RowCount&quot;
+ If .IsRowCountFinal Then
+ _PropertyGet = .RowCount
+ Else
+ If .isBeforeFirst() Or .isAfterLast() Or .rowDeleted() Then vBookmark = Null Else vBookmark = .getBookmark
+ .last()
+ _PropertyGet = .RowCount
+ If IsNull(vBookmark) Then .beforeFirst() Else .moveToBookmark(vBookmark)
+ End If
+ Case &quot;RowNumber&quot;
+ If .isBeforeFirst() Or .isAfterLast() Or .rowDeleted() Then _PropertyGet = 0 Else _PropertyGet = .Row
+ Case &quot;Source&quot;
+ _PropertyGet = _Command
+ Case &quot;SourceType&quot;
+ _PropertyGet = _DatasetType
+ Case &quot;UpdatableFields&quot;
+ _PropertyGet = _UpdatableFields
+ Case &quot;Values&quot;
+ If .isBeforeFirst() Or .isAfterLast() Or .rowDeleted() Then
+ Set _PropertyGet = Nothing
+ Else
+ &apos; Load the pairs field name / field value in the dictionary
+ vValuesDict = ScriptForge.SF_Services.CreateScriptService(&quot;ScriptForge.Dictionary&quot;)
+ For i = 0 To UBound(_Fields)
+ vValue = _ParentDatabase._GetColumnValue(_RowSet, i + 1, False)
+ vValuesDict.Add(_Fields(i), vValue)
+ Next i
+ Set _PropertyGet = vValuesDict
+ End If
+ Case &quot;XRowSet&quot;
+ Set _PropertyGet = _RowSet
+ Case Else
+ _PropertyGet = Null
+ End Select
+ End With
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertySet(Optional ByVal psProperty As String _
+ , Optional ByVal pvValue As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set the new value of the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+&apos;&apos;&apos; pvValue: the new value of the given property
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if successful
+
+Dim bSet As Boolean &apos; Return value
+Dim cstThisSub As String
+Const cstSubArgs = &quot;Value&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bSet = False
+
+ cstThisSub = &quot;SFDatabases.Dataset.set&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+ bSet = True
+ Select Case UCase(psProperty)
+ Case UCase(&quot;BOF&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;BOF&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If pvValue Then _RowSet.beforeFirst() &apos; Only True is valid
+ Case UCase(&quot;EOF&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;EOF&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If pvValue Then _RowSet.afterLast() &apos; Only True is valid
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ _PropertySet = bSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset._PropertySet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the Dataset instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[DATASET]: tablename,base file url&quot;
+
+ _Repr = &quot;[DATASET]: &quot; &amp; _Command &amp; &quot;,&quot; &amp; _ParentDatabase._Location
+
+End Function &apos; SFDatabases.SF_Dataset._Repr
+
+REM -----------------------------------------------------------------------------
+Private Function _SetColumnValue(ByVal plColIndex As Long _
+ , ByRef pvValue As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Store a value in a given column of the current record
+&apos;&apos;&apos; The resultset.insertRow() or resultset.updateRow() methods are supposed to be executed in the calling routine
+&apos;&apos;&apos; The type of the column is found in the resultset&apos;s metadata
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; plColIndex: the index of the column to extract the value from. Starts at 0
+&apos;&apos;&apos; Read-only columns are ignored.
+&apos;&apos;&apos; pvValue:the Variant value to store in the column
+&apos;&apos;&apos; Strings and numbers are supplied respectively as strings or numeric values
+&apos;&apos;&apos; Dates and times are supplied as Basic dates
+&apos;&apos;&apos; Null values are supplied as Null
+&apos;&apos;&apos; Errors or other strange data types are ignored
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; RECORDUPDATEERROR when value to store does not fit the type of the field
+&apos;&apos;&apos; or when field is not nullable and value = Null
+&apos;&apos;&apos; or when field is not writable or is an autovalue
+&apos;&apos;&apos; or when input file does not exist or is empty
+&apos;&apos;&apos; or when field type is not supported
+
+Dim bSet As Boolean &apos; Return value
+Dim sColumn As String &apos; Column name
+Dim oColumn As Object &apos; com.sun.star.sdb.DataColumn
+Dim lType As Long &apos; SQL column type: com.sun.star.sdbc.DataType
+Dim vDateTime As Variant &apos; com.sun.star.util.DateTime
+Dim bNullable As Boolean &apos; The field is defined as accepting Null values
+Dim vTemp As Variant &apos; Work variable for date and time related conversions
+Dim sFile As String &apos; File name in FileSystem notation
+Dim oSimpleFileAccess As Object &apos; com.sun.star.ucb.SimpleFileAccess
+Dim oStream As Object &apos; com.sun.star.io.XInputStream
+Dim lFileLength As Long &apos; Binary file length in bytes
+
+Dim UTILS As Object : Set UTILS = ScriptForge.SF_Utils
+Dim SESS As Object : Set SESS = ScriptForge.SF_Session
+
+ bSet = False
+ On Local Error GoTo CatchError
+
+Check:
+ Set oColumn = _RowSet.Columns.getByIndex(plColIndex)
+ sColumn = oColumn.Name
+ If _ReadOnly Then GoTo CatchError
+ If Not ScriptForge.SF_Array.Contains(_UpdatableFields, sColumn, CaseSensitive := True) Then GoTo CatchError
+
+Try:
+ With com.sun.star.sdbc.DataType
+ If IsEmpty(pvValue) Then &apos; An empty default value means not nullable and no default =&gt; ignore
+ ElseIf IsNull(pvValue) Then
+ If oColumn.IsNullable = com.sun.star.sdbc.ColumnValue.NULLABLE Then oColumn.updateNull() Else Goto CatchError
+ Else
+ Select Case oColumn.Type
+ Case .BIT, .BOOLEAN
+ If VarType(pvValue) &lt;&gt; UTILS.V_BOOLEAN Then GoTo CatchError
+ oColumn.updateBoolean(pvValue)
+ Case .TINYINT
+ If UTILS._VarTypeExt(pvValue) &lt;&gt; UTILS.V_NUMERIC Then Goto CatchError
+ If pvValue &lt; -128 Or pvValue &gt; +127 Then Goto CatchError
+ oColumn.updateShort(CInt(pvValue))
+ Case .SMALLINT
+ If UTILS._VarTypeExt(pvValue) &lt;&gt; UTILS.V_NUMERIC Then Goto CatchError
+ If pvValue &lt; -32768 Or pvValue &gt; 32767 Then Goto CatchError
+ oColumn.updateInt(CInt(pvValue))
+ Case .INTEGER
+ If UTILS._VarTypeExt(pvValue) &lt;&gt; UTILS.V_NUMERIC Then Goto CatchError
+ If pvValue &lt; -2147483648 Or pvValue &gt; 2147483647 Then Goto CatchError
+ oColumn.updateInt(CLng(pvValue))
+ Case .BIGINT
+ If UTILS._VarTypeExt(pvValue) &lt;&gt; UTILS.V_NUMERIC Then Goto CatchError
+ oColumn.updateLong(pvValue) &apos; No proper type conversion for HYPER data type
+ Case .FLOAT
+ If UTILS._VarTypeExt(pvValue) &lt;&gt; UTILS.V_NUMERIC Then Goto CatchError
+ If Abs(pvValue) &lt; 3.402823E38 And Abs(pvValue) &gt; 1.401298E-45 Then oColumn.updateFloat(CSng(pvValue)) Else Goto CatchError
+ Case .REAL, .DOUBLE
+ If UTILS._VarTypeExt(pvValue) &lt;&gt; UTILS.V_NUMERIC Then Goto CatchError
+ &apos;If Abs(pvValue) &lt; 1.79769313486232E308 And Abs(pvValue) &gt; 4.94065645841247E-307 Then oColumn.updateDouble(CDbl(pvValue)) Else Goto CatchError
+ oColumn.updateDouble(CDbl(pvValue))
+ Case .NUMERIC, .DECIMAL
+ If UTILS._VarTypeExt(pvValue) &lt;&gt; UTILS.V_NUMERIC Then Goto CatchError
+ If SESS.HasUnoProperty(oColumn, &quot;Scale&quot;) Then
+ If oColumn.Scale &gt; 0 Then
+ &apos;If Abs(pvValue) &lt; 1.79769313486232E308 And Abs(pvValue) &gt; 4.94065645841247E-307 Then oColumn.updateDouble(CDbl(pvValue)) Else Goto CatchError
+ oColumn.updateDouble(CDbl(pvValue))
+ Else
+ oColumn.updateString(CStr(pvValue))
+ End If
+ Else
+ Column.updateString(CStr(pvValue))
+ End If
+ Case .CHAR, .VARCHAR, .LONGVARCHAR, .CLOB
+ If VarType(pvValue) &lt;&gt; V_STRING Then GoTo CatchError
+ If SESS.HasUnoProperty(oColumn, &quot;Precision&quot;) Then
+ If oColumn.Precision &gt; 0 And Len(pvValue) &gt; oColumn.Precision Then Goto CatchError
+ End If
+ oColumn.updateString(pvValue)
+ Case .DATE
+ If VarType(pvValue) &lt;&gt; V_DATE Then GoTo CatchError
+ vTemp = New com.sun.star.util.Date
+ With vTemp
+ .Day = Day(pvValue)
+ .Month = Month(pvValue)
+ .Year = Year(pvValue)
+ End With
+ oColumn.updateDate(vTemp)
+ Case .TIME
+ If VarType(pvValue) &lt;&gt; V_DATE Then GoTo CatchError
+ vTemp = New com.sun.star.util.Time
+ With vTemp
+ .Hours = Hour(pvValue)
+ .Minutes = Minute(pvValue)
+ .Seconds = Second(pvValue)
+ .NanoSeconds = 0
+ End With
+ oColumn.updateTime(vTemp)
+ Case .TIMESTAMP
+ If VarType(pvValue) &lt;&gt; V_DATE Then GoTo CatchError
+ vTemp = New com.sun.star.util.DaWHEREteTime
+ With vTemp
+ .Day = Day(pvValue)
+ .Month = Month(pvValue)
+ .Year = Year(pvValue)
+ .Hours = Hour(pvValue)
+ .Minutes = Minute(pvValue)
+ .Seconds = Second(pvValue)
+ .NanoSeconds = 0
+ End With
+ oColumn.updateTimestamp(vTemp)
+ Case .BINARY, .VARBINARY, .LONGVARBINARY, .BLOB
+ If VarType(pvValue) &lt;&gt; V_STRING Then GoTo CatchError
+ If Not UTILS._ValidateFile(pvValue, &quot;FieldValue&quot;) Then GoTo CatchError
+ &apos; Verify file
+ sFile = ConvertToUrl(pvValue)
+ oSimpleFileAccess = UTILS._GetUnoService(&quot;FileAccess&quot;)
+ If Not oSimpleFileAccess.exists(sFile) Then Goto CatchError
+ &apos; Load the binary data
+ Set oStream = oSimpleFileAccess.openFileRead(sFile)
+ lFileLength = oStream.getLength()
+ If lFileLength = 0 Then Goto CatchError &apos; &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; PUT NULL
+ oColumn.updateBinaryStream(oStream, lFileLength)
+ oStream.closeInput()
+ Case Else
+ Goto CatchError
+ End Select
+ End If
+ End With
+
+ bSet = True
+
+Finally:
+ _SetColumnValue = bSet
+ Exit Function
+CatchError:
+ On Local Error GoTo 0
+ ScriptForge.SF_Exception.RaiseFatal(RECORDUPDATEERROR, sColumn, ScriptForge.SF_String.Represent(pvValue), oColumn.TypeName)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Dataset._SetColumnValue
+
+REM ============================================ END OF SFDATABASES.SF_DATASET
+</script:module> \ No newline at end of file