summaryrefslogtreecommitdiffstats
path: root/wizards/source/sfdatabases
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /wizards/source/sfdatabases
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'wizards/source/sfdatabases')
-rw-r--r--wizards/source/sfdatabases/SF_Database.xba1475
-rw-r--r--wizards/source/sfdatabases/SF_Dataset.xba1672
-rw-r--r--wizards/source/sfdatabases/SF_Datasheet.xba952
-rw-r--r--wizards/source/sfdatabases/SF_Register.xba280
-rw-r--r--wizards/source/sfdatabases/__License.xba26
-rw-r--r--wizards/source/sfdatabases/dialog.xlb3
-rw-r--r--wizards/source/sfdatabases/script.xlb9
7 files changed, 4417 insertions, 0 deletions
diff --git a/wizards/source/sfdatabases/SF_Database.xba b/wizards/source/sfdatabases/SF_Database.xba
new file mode 100644
index 0000000000..ff0eafc5b7
--- /dev/null
+++ b/wizards/source/sfdatabases/SF_Database.xba
@@ -0,0 +1,1475 @@
+<?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_Database" 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_Database
+&apos;&apos;&apos; ===========
+&apos;&apos;&apos; Management of databases embedded in or related to Base documents
+&apos;&apos;&apos; Each instance of the current class represents a single database, with essentially its tables, queries and data
+&apos;&apos;&apos;
+&apos;&apos;&apos; The exchanges with the database are done in SQL only.
+&apos;&apos;&apos; To make them more readable, use optionally square brackets to surround table/query/field names
+&apos;&apos;&apos; instead of the (RDBMS-dependent) normal surrounding character (usually, double-quote, single-quote or other).
+&apos;&apos;&apos; SQL statements may be run in direct or indirect mode. In direct mode the statement is transferred literally
+&apos;&apos;&apos; without syntax checking nor review to the database system.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The provided interfaces include simple tables, queries and fields lists, and access to database metadata.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Transaction handling
+&apos;&apos;&apos; Changes to data remain reversible until the moment the running script instructs the database to commit them.
+&apos;&apos;&apos; The implicit (default) behaviour is that commit takes place after the execution of every single SQL statement.
+&apos;&apos;&apos; The choice can be made (SetTransactionMode()) to take commitments manually.
+&apos;&apos;&apos; The Commit() and Rollback() statements delimit transactions.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation and usage:
+&apos;&apos;&apos; 1) To access any database at anytime
+&apos;&apos;&apos; Dim myDatabase As Object
+&apos;&apos;&apos; Set myDatabase = CreateScriptService(&quot;SFDatabases.Database&quot;, FileName, , [ReadOnly], [User, [Password]])
+&apos;&apos;&apos; &apos; Args:
+&apos;&apos;&apos; &apos; FileName: the name of the Base file compliant with the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; &apos; RegistrationName: the name of a registered database (mutually exclusive with FileName)
+&apos;&apos;&apos; &apos; ReadOnly: Default = True
+&apos;&apos;&apos; &apos; User, Password: additional connection arguments to the database server
+&apos;&apos;&apos; &apos; ... Run queries, SQL statements, ...
+&apos;&apos;&apos; myDatabase.CloseDatabase()
+&apos;&apos;&apos;
+&apos;&apos;&apos; 2) To access the database related to the current Base document
+&apos;&apos;&apos; Dim myDoc As Object, myDatabase As Object, ui As Object
+&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
+&apos;&apos;&apos; Set myDoc = ui.OpenBaseDocument(&quot;myDb.odb&quot;)
+&apos;&apos;&apos; Set myDatabase = myDoc.GetDatabase() &apos; user and password are supplied here, if needed
+&apos;&apos;&apos; &apos; ... Run queries, SQL statements, ...
+&apos;&apos;&apos; myDoc.CloseDocument()
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_database.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
+
+Private Const DBREADONLYERROR = &quot;DBREADONLYERROR&quot;
+Private Const SQLSYNTAXERROR = &quot;SQLSYNTAXERROR&quot;
+Private Const SQLSYNTAX2ERROR = &quot;SQLSYNTAX2ERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object
+Private ObjectType As String &apos; Must be DATABASE
+Private ServiceName As String
+Private _DataSource As Object &apos; com.sun.star.comp.dba.ODatabaseSource
+Private _Connection As Object &apos; com.sun.star.sdbc.XConnection
+Private _URL As String &apos; Text on status bar
+Private _Location As String &apos; File name
+Private _ReadOnly As Boolean
+Private _MetaData As Object &apos; com.sun.star.sdbc.XDatabaseMetaData
+Private _User As String &apos; Connection parameters to enable a reconnection
+Private _Password As String
+Private _Datasets As Variant &apos; Array of open datasets
+
+REM ============================================================ MODULE CONSTANTS
+
+Const cstToken = &quot;//&quot; &apos; Form names accept special characters but not slashes
+
+REM ===================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;DATABASE&quot;
+ ServiceName = &quot;SFDatabases.Database&quot;
+ Set _DataSource = Nothing
+ Set _Connection = Nothing
+ _URL = &quot;&quot;
+ _Location = &quot;&quot;
+ _ReadOnly = True
+ Set _MetaData = Nothing
+ _User = &quot;&quot;
+ _Password = &quot;&quot;
+ _Datasets = Array()
+End Sub &apos; SFDatabases.SF_Database Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDatabases.SF_Database Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDatabases.SF_Database Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get Queries() As Variant
+&apos;&apos;&apos; Return the list of available queries in the database
+ Queries = _PropertyGet(&quot;Queries&quot;)
+End Property &apos; SFDatabases.SF_Database.Queries (get)
+
+REM -----------------------------------------------------------------------------
+Property Get Tables() As Variant
+&apos;&apos;&apos; Return the list of available Tables in the database
+ Tables = _PropertyGet(&quot;Tables&quot;)
+End Property &apos; SFDatabases.SF_Database.Tables (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XConnection() As Variant
+&apos;&apos;&apos; Return a com.sun.star.sdbc.XConnection UNO object
+ XConnection = _PropertyGet(&quot;XConnection&quot;)
+End Property &apos; SFDatabases.SF_Database.XConnection (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XMetaData() As Variant
+&apos;&apos;&apos; Return a com.sun.star.sdbc.XDatabaseMetaData UNO object
+ XMetaData = _PropertyGet(&quot;XMetaData&quot;)
+End Property &apos; SFDatabases.SF_Database.XMetaData (get)
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Sub CloseDatabase()
+&apos;&apos;&apos; Close the current database connection
+
+Const cstThisSub = &quot;SFDatabases.Database.CloseDatabase&quot;
+Const cstSubArgs = &quot;&quot;
+
+ On Local Error GoTo 0 &apos; Disable useless error checking
+
+Check:
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+Try:
+ _CloseConnection()
+ Dispose()
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+End Sub &apos; SFDatabases.SF_Database.CloseDatabase
+
+REM -----------------------------------------------------------------------------
+Public Sub Commit()
+&apos;&apos;&apos; Commit all updates done since the previous Commit or Rollback
+&apos;&apos;&apos; The statement is ignored if the commits are done automatically after each SQL statement.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DBREADONLYERROR The method is not applicable on a read-only database
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; db.SetTransactionMode(4) &apos; Highest transaction level
+&apos;&apos;&apos; db.RunSql(&quot;UPDATE ...&quot;)
+&apos;&apos;&apos; db.Commit()
+&apos;&apos;&apos; db.RunSql(DELETE ...&quot;)
+&apos;&apos;&apos; If ...something happened... Then db.Rollback() Else db.Commit()
+&apos;&apos;&apos; db.SetTransactionMode() &apos; Back to the automatic mode
+
+Const cstThisSub = &quot;SFDatabases.Database.Commit&quot;
+Const cstSubArgs = &quot;&quot;
+
+ On Local Error GoTo Finally
+
+Check:
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If _ReadOnly Then GoTo Catch_ReadOnly
+
+Try:
+ With _Connection
+ If Not .AutoCommit Then
+ .commit()
+ &apos; To make updates potentially visible in the user interface ...
+ _FlushConnection()
+ End If
+ End With
+
+Finally:
+ On Local Error GoTo 0
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch_ReadOnly:
+ ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
+ GoTo Finally
+End Sub &apos; SFDatabases.SF_Database.Commit
+
+REM -----------------------------------------------------------------------------
+Public Function CreateDataset(Optional ByVal SQLCommand As Variant _
+ , Optional ByVal DirectSql As Variant _
+ , Optional ByVal Filter As Variant _
+ , Optional ByVal OrderBy As Variant _
+ ) As Object
+&apos;&apos;&apos; Create and return a Dataset class instance based on a table, a query
+&apos;&apos;&apos; or an SQL SELECT statement.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SQLCommand: as a case-sensitive string, a table name, a query name
+&apos;&apos;&apos; or a valid SQL SELECT statement. Identifiers may be surrounded
+&apos;&apos;&apos; with square brackets
+&apos;&apos;&apos; DirectSql: when True, the statement is processed by the targeted RDBMS
+&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; 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; 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
+
+Dim oDataset As Object &apos; Return value
+Dim bDirect As Boolean &apos; Alias of DirectSql
+Dim sSql As String &apos; SQL statement
+Dim sType As String &apos; TABLE, QUERY or SQL
+Dim oQuery As Object &apos; com.sun.star.ucb.XContent
+Dim ARR As Object : Set ARR = ScriptForge.SF_Array
+
+Const cstThisSub = &quot;SFDatabases.Database.CreateDataset&quot;
+Const cstSubArgs = &quot;SQLCommand, [DirectSQL=False], [Filter=&quot;&quot;&quot;&quot;], [OrderBy=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oDataset = Nothing
+
+Check:
+ If IsMissing(DirectSQL) Or IsEmpty(DirectSQL) Then DirectSQL = False
+ If IsMissing(Filter) Or IsEmpty(Filter) Then Filter = &quot;&quot;
+ If IsMissing(OrderBy) Or IsEmpty(OrderBy) Then OrderBy = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(SQLCommand, &quot;SQLCommand&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(DirectSQL, &quot;DirectSQL&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ 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:
+ &apos; Table, query of SQL ? Prepare dataset
+ If ARR.Contains(Tables, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
+ If Len(Filter) + Len(OrderBy) = 0 Then &apos; Filter seems not applicable on pure TABLE resultset
+ sType = &quot;TABLE&quot;
+ sSql = SQLCommand
+ Else
+ sType = &quot;SQL&quot;
+ sSql = &quot;SELECT * FROM [&quot; &amp; SQLCommand &amp; &quot;]&quot;
+ End If
+ bDirect = DirectSQL
+ ElseIf ARR.Contains(Queries, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
+ Set oQuery = _Connection.Queries.getByName(SQLCommand)
+ If Len(Filter) + Len(OrderBy) = 0 Then &apos; Filter seems not applicable on pure QUERY resultset
+ sType = &quot;QUERY&quot;
+ sSql = SQLCommand
+ Else
+ sType = &quot;SQL&quot;
+ sSql = oQuery.Command
+ End If
+ bDirect = Not oQuery.EscapeProcessing
+ ElseIf ScriptForge.SF_String.StartsWith(SQLCommand, &quot;SELECT&quot;, CaseSensitive := False) Then
+ sType = &quot;SQL&quot;
+ sSql = SQLCommand
+ bDirect = DirectSQL
+ Else
+ If Not ScriptForge.SF_Utils._Validate(SQLCommand, &quot;SQLCommand&quot;, V_STRING _
+ , ARR.Flatten(ARR.Append(Tables, Queries))) Then GoTo Finally
+ End If
+
+ Set oDataset = New SF_Dataset
+ With oDataset
+ Set .[Me] = oDataset
+ Set ._ParentDatabase = [Me]
+ ._DatasetType = sType
+ ._Command = SQLCommand
+ ._Sql = _ReplaceSquareBrackets(sSql)
+ ._DirectSql = bDirect
+ ._Filter = _ReplaceSquareBrackets(Filter)
+ ._OrderBy = _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_Database.CreateDataset
+
+REM -----------------------------------------------------------------------------
+Public Function DAvg(Optional ByVal Expression As Variant _
+ , Optional ByVal TableName As Variant _
+ , Optional ByVal Criteria As Variant _
+ ) As Variant
+&apos;&apos;&apos; Compute the aggregate function AVG() on a field or expression belonging to a table
+&apos;&apos;&apos; filtered by a WHERE-clause.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Expression: an SQL expression
+&apos;&apos;&apos; TableName: the name of a table
+&apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
+
+ DAvg = _DFunction(&quot;Avg&quot;, Expression, TableName, Criteria)
+
+End Function &apos; SFDatabases.SF_Database.DAvg
+
+REM -----------------------------------------------------------------------------
+Public Function DCount(Optional ByVal Expression As Variant _
+ , Optional ByVal TableName As Variant _
+ , Optional ByVal Criteria As Variant _
+ ) As Variant
+&apos;&apos;&apos; Compute the aggregate function COUNT() on a field or expression belonging to a table
+&apos;&apos;&apos; filtered by a WHERE-clause.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Expression: an SQL expression
+&apos;&apos;&apos; TableName: the name of a table
+&apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
+
+ DCount = _DFunction(&quot;Count&quot;, Expression, TableName, Criteria)
+
+End Function &apos; SFDatabases.SF_Database.DCount
+
+REM -----------------------------------------------------------------------------
+Public Function DLookup(Optional ByVal Expression As Variant _
+ , Optional ByVal TableName As Variant _
+ , Optional ByVal Criteria As Variant _
+ , Optional ByVal OrderClause As Variant _
+ ) As Variant
+&apos;&apos;&apos; Compute the aggregate function Lookup() on a field or expression belonging to a table
+&apos;&apos;&apos; filtered by a WHERE-clause.
+&apos;&apos;&apos; To order the results, a pvOrderClause may be precised. The 1st record will be retained.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Expression: an SQL expression
+&apos;&apos;&apos; TableName: the name of a table
+&apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
+&apos;&apos;&apos; pvOrderClause: an optional order clause incl. &quot;DESC&quot; if relevant
+
+ DLookup = _DFunction(&quot;Lookup&quot;, Expression, TableName, Criteria, OrderClause)
+
+End Function &apos; SFDatabases.SF_Database.DLookup
+
+REM -----------------------------------------------------------------------------
+Public Function DMax(Optional ByVal Expression As Variant _
+ , Optional ByVal TableName As Variant _
+ , Optional ByVal Criteria As Variant _
+ ) As Variant
+&apos;&apos;&apos; Compute the aggregate function MAX() on a field or expression belonging to a table
+&apos;&apos;&apos; filtered by a WHERE-clause.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Expression: an SQL expression
+&apos;&apos;&apos; TableName: the name of a table
+&apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
+
+ DMax = _DFunction(&quot;Max&quot;, Expression, TableName, Criteria)
+
+End Function &apos; SFDatabases.SF_Database.DMax
+
+REM -----------------------------------------------------------------------------
+Public Function DMin(Optional ByVal Expression As Variant _
+ , Optional ByVal TableName As Variant _
+ , Optional ByVal Criteria As Variant _
+ ) As Variant
+&apos;&apos;&apos; Compute the aggregate function MIN() on a field or expression belonging to a table
+&apos;&apos;&apos; filtered by a WHERE-clause.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Expression: an SQL expression
+&apos;&apos;&apos; TableName: the name of a table
+&apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
+
+ DMin = _DFunction(&quot;Min&quot;, Expression, TableName, Criteria)
+
+End Function &apos; SFDatabases.SF_Database.DMin
+
+REM -----------------------------------------------------------------------------
+Public Function DSum(Optional ByVal Expression As Variant _
+ , Optional ByVal TableName As Variant _
+ , Optional ByVal Criteria As Variant _
+ ) As Variant
+&apos;&apos;&apos; Compute the aggregate function Sum() on a field or expression belonging to a table
+&apos;&apos;&apos; filtered by a WHERE-clause.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Expression: an SQL expression
+&apos;&apos;&apos; TableName: the name of a table
+&apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
+
+ DSum = _DFunction(&quot;Sum&quot;, Expression, TableName, Criteria)
+
+End Function &apos; SFDatabases.SF_Database.DSum
+
+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 property
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myDatabase.GetProperty(&quot;Queries&quot;)
+
+Const cstThisSub = &quot;SFDatabases.Database.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_Database.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function GetRows(Optional ByVal SQLCommand As Variant _
+ , Optional ByVal DirectSQL As Variant _
+ , Optional ByVal Header As Variant _
+ , Optional ByVal MaxRows As Variant _
+ ) As Variant
+&apos;&apos;&apos; Return the content of a table, a query or a SELECT SQL statement as an array
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SQLCommand: a table name, a query name or a SELECT SQL statement
+&apos;&apos;&apos; DirectSQL: when True, no syntax conversion is done by LO. Default = False
+&apos;&apos;&apos; Ignored when SQLCommand is a table or a query name
+&apos;&apos;&apos; Header: When True, a header row is inserted on 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 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
+&apos;&apos;&apos; a = myDatabase.GetRows(&quot;SELECT [First Name], [Last Name] FROM [Employees] ORDER BY [Last Name]&quot;, Header := True)
+
+Dim vResult As Variant &apos; Return value
+Dim oResult As Object &apos; com.sun.star.sdbc.XResultSet
+Dim oQuery As Object &apos; com.sun.star.ucb.XContent
+Dim sSql As String &apos; SQL statement
+Dim bDirect &apos; Alias of DirectSQL
+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.Database.GetRows&quot;
+Const cstSubArgs = &quot;SQLCommand, [DirectSQL=False], [Header=False], [MaxRows=0]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vResult = Array()
+
+Check:
+ If IsMissing(DirectSQL) Or IsEmpty(DirectSQL) Then DirectSQL = False
+ 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(SQLCommand, &quot;SQLCommand&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(DirectSQL, &quot;DirectSQL&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ 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
+
+Try:
+ &apos; Table, query of SQL ? Prepare resultset
+ If ScriptForge.SF_Array.Contains(Tables, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
+ sSql = &quot;SELECT * FROM [&quot; &amp; SQLCommand &amp; &quot;]&quot;
+ bDirect = True
+ ElseIf ScriptForge.SF_Array.Contains(Queries, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
+ Set oQuery = _Connection.Queries.getByName(SQLCommand)
+ sSql = oQuery.Command
+ bDirect = Not oQuery.EscapeProcessing
+ ElseIf ScriptForge.SF_String.StartsWith(SQLCommand, &quot;SELECT&quot;, CaseSensitive := False) Then
+ sSql = SQLCommand
+ bDirect = DirectSQL
+ Else
+ GoTo Finally
+ End If
+
+ &apos; Execute command
+ Set oResult = _ExecuteSql(sSql, bDirect)
+ If IsNull(oResult) Then GoTo Finally
+
+ With oResult
+ &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
+ bRead = .first()
+ Do While bRead And (MaxRows = 0 Or lRows &lt; MaxRows - 1)
+ 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) = _GetColumnValue(oResult, i + 1)
+ Next i
+ bRead = .next()
+ Loop
+ End With
+
+Finally:
+ GetRows = vResult
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database.GetRows
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Database service as an array
+
+ Methods = Array( _
+ &quot;CloseDatabase&quot; _
+ , &quot;Commit&quot; _
+ , &quot;CreateDataset&quot; _
+ , &quot;DAvg&quot; _
+ , &quot;DCount&quot; _
+ , &quot;DLookup&quot; _
+ , &quot;DMax&quot; _
+ , &quot;DMin&quot; _
+ , &quot;DSum&quot; _
+ , &quot;GetRows&quot; _
+ , &quot;OpenFormDocument&quot; _
+ , &quot;OpenQuery&quot; _
+ , &quot;OpenSql&quot; _
+ , &quot;OpenTable&quot; _
+ , &quot;Rollback&quot; _
+ , &quot;RunSql&quot; _
+ , &quot;SetTransactionMode&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Database.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function OpenFormDocument(Optional ByVal FormDocument As Variant) As Object
+&apos;&apos;&apos; Open the FormDocument given by its hierarchical name in normal mode
+&apos;&apos;&apos; If the form document is already open, the form document is made active
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FormDocument: a valid form document name as a case-sensitive string
+&apos;&apos;&apos; When hierarchical, the hierarchy must be rendered with forward slashes (&quot;/&quot;)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A FormDocument instance or Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Form name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Set oForm = oDb.OpenFormDocument(&quot;Folder1/myFormDocument&quot;)
+
+Dim oOpen As Object &apos; Return value
+Dim oFormDocuments As Variant &apos; com.sun.star.comp.dba.ODocumentContainer
+Dim vFormNames As Variant &apos; Array of all document form names present in the document
+Dim vOpenArgs As Variant &apos; Array of property values
+Dim oNewForm As Object &apos; Output of loadComponent()
+Const cstThisSub = &quot;SFDatabases.Database.OpenFormDocument&quot;
+Const cstSubArgs = &quot;FormDocument&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ &apos; Build list of available FormDocuments recursively with _CollectFormDocuments
+ Set oFormDocuments = _Connection.Parent.DataBaseDocument.FormDocuments
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ vFormNames = Split(_CollectFormDocuments(oFormDocuments), cstToken)
+ If Not ScriptForge.SF_Utils._Validate(FormDocument, &quot;FormDocument&quot;, V_STRING, vFormNames) Then GoTo Finally
+ End If
+
+Try:
+ vOpenArgs = Array(SF_Utils._MakePropertyValue(&quot;ActiveConnection&quot;, _Connection) _
+ , SF_Utils._MakePropertyValue(&quot;OpenMode&quot;, &quot;open&quot;) _
+ )
+ Set oNewForm = oFormDocuments.loadComponentFromURL(FormDocument, &quot;&quot;, 0, vOpenArgs)
+
+ Set oOpen = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.FormDocument&quot;, oNewForm)
+
+ &apos; Prevent desynchronization when using .last(), .next() etc immediately after component loading. Bug #156836
+ Wait 1
+
+Finally:
+ Set OpenFormDocument = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Databases.SF_Database.OpenFormDocument
+
+REM -----------------------------------------------------------------------------
+Public Function OpenQuery(Optional ByVal QueryName As Variant) As Object
+&apos;&apos;&apos; Open the query given by its name
+&apos;&apos;&apos; The datasheet will live independently from any other (typically Base) component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; QueryName: a valid query name as a case-sensitive string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the query could be opened, otherwise Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Query name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDb.OpenQuery(&quot;myQuery&quot;)
+
+Dim oOpen As Object &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Database.OpenQuery&quot;
+Const cstSubArgs = &quot;QueryName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(QueryName, &quot;QueryName&quot;, V_STRING, Queries) Then GoTo Finally
+ End If
+
+Try:
+ Set oOpen = _OpenDatasheet(QueryName, com.sun.star.sdb.CommandType.QUERY _
+ , _Connection.Queries.getByName(QueryName).EscapeProcessing)
+
+Finally:
+ Set OpenQuery = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos;SFDatabases.SF_Database.OpenQuery
+
+REM -----------------------------------------------------------------------------
+Public Function OpenSql(Optional ByRef Sql As Variant _
+ , Optional ByVal DirectSql As Variant _
+ ) As Object
+&apos;&apos;&apos; Open the datasheet based on a SQL SELECT statement.
+&apos;&apos;&apos; The datasheet will live independently from any other (typically Base) component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Sql: a valid Sql statement as a case-sensitive string.
+&apos;&apos;&apos; Identifiers may be surrounded by square brackets
+&apos;&apos;&apos; DirectSql: when True, the statement is processed by the targeted RDBMS
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if it could be opened, otherwise Nothing
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDb.OpenSql(&quot;SELECT * FROM [Customers] ORDER BY [CITY]&quot;)
+
+Dim oOpen As Object &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Database.OpenSql&quot;
+Const cstSubArgs = &quot;Sql, [DirectSql=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If IsMissing(DirectSql) Or IsEmpty(DirectSql) Then DirectSql = False
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(Sql, &quot;Sql&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(DirectSql, &quot;DirectSql&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ Set oOpen = _OpenDatasheet(_ReplaceSquareBrackets(Sql), com.sun.star.sdb.CommandType.COMMAND, Not DirectSql)
+
+Finally:
+ Set OpenSql = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database.OpenSql
+
+REM -----------------------------------------------------------------------------
+Public Function OpenTable(Optional ByVal TableName As Variant) As Object
+&apos;&apos;&apos; Open the table given by its name
+&apos;&apos;&apos; The datasheet will live independently from any other (typically Base) component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TableName: a valid table name as a case-sensitive string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the table could be opened, otherwise Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Table name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDb.OpenTable(&quot;myTable&quot;)
+
+Dim oOpen As Object &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Database.OpenTable&quot;
+Const cstSubArgs = &quot;TableName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(TableName, &quot;TableName&quot;, V_STRING, Tables) Then GoTo Finally
+ End If
+
+Try:
+ Set oOpen = _OpenDatasheet(TableName, com.sun.star.sdb.CommandType.TABLE, True)
+
+Finally:
+ Set OpenTable = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database.OpenTable
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Database class as an array
+
+ Properties = Array( _
+ &quot;Queries&quot; _
+ , &quot;Tables&quot; _
+ , &quot;XConnection&quot; _
+ , &quot;XMetaData&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Database.Properties
+
+REM -----------------------------------------------------------------------------
+Public Sub Rollback()
+&apos;&apos;&apos; Cancel all updates done since the previous Commit or Rollback
+&apos;&apos;&apos; The statement is ignored if the commits are done automatically after each SQL statement.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DBREADONLYERROR The method is not applicable on a read-only database
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; db.SetTransactionMode(4) &apos; Highest transaction level
+&apos;&apos;&apos; db.RunSql(&quot;UPDATE ...&quot;)
+&apos;&apos;&apos; db.Commit()
+&apos;&apos;&apos; db.RunSql(DELETE ...&quot;)
+&apos;&apos;&apos; If ...something happened... Then db.Rollback() Else db.Commit()
+&apos;&apos;&apos; db.SetTransactionMode() &apos; Back to the automatic mode
+
+Const cstThisSub = &quot;SFDatabases.Database.Rollback&quot;
+Const cstSubArgs = &quot;&quot;
+
+ On Local Error GoTo Finally
+
+Check:
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If _ReadOnly Then GoTo Catch_ReadOnly
+
+Try:
+ With _Connection
+ If Not .AutoCommit Then .rollback()
+ End With
+
+Finally:
+ On Local Error GoTo 0
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch_ReadOnly:
+ ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
+ GoTo Finally
+End Sub &apos; SFDatabases.SF_Database.Rollback
+
+REM -----------------------------------------------------------------------------
+Public Function RunSql(Optional ByVal SQLCommand As Variant _
+ , Optional ByVal DirectSQL As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Execute an action query (table creation, record insertion, ...) or SQL statement on the current database
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SQLCommand: a query name or an SQL statement
+&apos;&apos;&apos; DirectSQL: when True, no syntax conversion is done by LO. Default = False
+&apos;&apos;&apos; Ignored when SQLCommand is a query name
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DBREADONLYERROR The method is not applicable on a read-only database
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myDatabase.RunSql(&quot;INSERT INTO [EMPLOYEES] VALUES(25, &apos;SMITH&apos;, &apos;John&apos;)&quot;, DirectSQL := True)
+
+Dim bResult As Boolean &apos; Return value
+Dim oStatement As Object &apos; com.sun.star.sdbc.XStatement
+Dim oQuery As Object &apos; com.sun.star.ucb.XContent
+Dim sSql As String &apos; SQL statement
+Dim bDirect &apos; Alias of DirectSQL
+Const cstQuery = 2, cstSql = 3
+Const cstThisSub = &quot;SFDatabases.Database.RunSql&quot;
+Const cstSubArgs = &quot;SQLCommand, [DirectSQL=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bResult = False
+
+Check:
+ If IsMissing(DirectSQL) Or IsEmpty(DirectSQL) Then DirectSQL = False
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(SQLCommand, &quot;SQLCommand&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(DirectSQL, &quot;DirectSQL&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+ If _ReadOnly Then GoTo Catch_ReadOnly
+
+Try:
+ &apos; Query of SQL ?
+ If ScriptForge.SF_Array.Contains(Queries, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
+ Set oQuery = _Connection.Queries.getByName(SQLCommand)
+ sSql = oQuery.Command
+ bDirect = Not oQuery.EscapeProcessing
+ ElseIf Not ScriptForge.SF_String.StartsWith(SQLCommand, &quot;SELECT&quot;, CaseSensitive := False) Then
+ sSql = SQLCommand
+ bDirect = DirectSQL
+ Else
+ GoTo Finally
+ End If
+
+ &apos; Execute command
+ bResult = _ExecuteSql(sSql, bDirect)
+
+Finally:
+ RunSql = bResult
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+Catch_ReadOnly:
+ ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database.RunSql
+
+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.Database.SetProperty&quot;
+Const cstSubArgs = &quot;PropertyName, Value&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ SetProperty = False
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ Select Case UCase(PropertyName)
+ Case Else
+ End Select
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database.SetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function SetTransactionMode(Optional ByVal TransactionMode As Variant) As Boolean
+&apos;&apos;&apos; Configure the handling of transactions.
+&apos;&apos;&apos; Usually all transactions are in auto-commit mode, that means, a commit takes place
+&apos;&apos;&apos; after each single SQL command. Therefore to control a transaction manually, implies to switch auto-commit off.
+&apos;&apos;&apos; The first SQL command starts a transaction that is active until the corresponding
+&apos;&apos;&apos; methods have been committed or rolled back.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The transaction mode remains valid until the next call of the method with a different value,
+&apos;&apos;&apos; or until the closure of the actual Database instance,
+&apos;&apos;&apos; or until a call to SetTransactionMode() without argument, which cancels the manual transaction mode.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The method may close and replace the actual connection. This means that all open datasets
+&apos;&apos;&apos; are first closed. Open datasheets might see their content changed or vanish.
+&apos;&apos;&apos; The easiest is to set the transaction mode immediately after the creation of the Database instance.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TransactionMode: one of the com.sun.star.sdbc.TransactionIsolation constants:
+&apos;&apos;&apos; (0) NONE Indicates that transactions are not supported. Default: cancel the transaction mode.
+&apos;&apos;&apos; (1) READ_UNCOMMITTED Dirty reads, non-repeatable reads and phantom reads can occur.
+&apos;&apos;&apos; This level allows a row changed by one transaction to be read by another transaction
+&apos;&apos;&apos; before any changes in that row have been committed (a &quot;dirty read&quot;).
+&apos;&apos;&apos; If any of the changes are rolled back, the second transaction will have retrieved an invalid row.
+&apos;&apos;&apos; (2) READ_COMMITTED Dirty reads are prevented; non-repeatable reads and phantom reads can occur.
+&apos;&apos;&apos; This level only prohibits a transaction from reading a row with uncommitted changes in it.
+&apos;&apos;&apos; (4) REPEATABLE_READ Dirty reads and non-repeatable reads are prevented; phantom reads can occur.
+&apos;&apos;&apos; This level prohibits a transaction from reading a row with uncommitted changes in it,
+&apos;&apos;&apos; and it also prohibits the situation where one transaction reads a row,
+&apos;&apos;&apos; a second transaction alters the row, and the first transaction rereads the row,
+&apos;&apos;&apos; getting different values the second time (a &quot;non-repeatable read&quot;).
+&apos;&apos;&apos; (8) SERIALIZABLE Dirty reads, non-repeatable reads and phantom reads are prevented.
+&apos;&apos;&apos; This level includes the prohibitions in REPEATABLE_READ and further prohibits
+&apos;&apos;&apos; the situation where one transaction reads all rows that satisfy a WHERE condition,
+&apos;&apos;&apos; a second transaction inserts a row that satisfies that WHERE condition,
+&apos;&apos;&apos; and the first transaction rereads for the same condition, retrieving
+&apos;&apos;&apos; the additional &quot;phantom&quot; row in the second read.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful.
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DBREADONLYERROR The method is not applicable on a read-only database
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDb.SetTransactionMode(com.sun.star.sdbc.TransactionIsolation.SERIALIZABLE) &apos; 8
+
+Dim bSet As Boolean &apos; Return value
+Dim bCommit As Boolean &apos; To compare with AutoCommit
+Const cstThisSub = &quot;SFDatabases.Database.SetTransactionMode&quot;
+Const cstSubArgs = &quot;TransactionMode=0|1|2|4|8&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bSet = False
+
+Check:
+ If IsMissing(TransactionMode) Or IsEmpty(TransactionMode) Then TransactionMode = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(TransactionMode, &quot;TransactionMode&quot;, ScriptForge.V_NUMERIC, Array(0, 1, 2, 4, 8)) Then GoTo Finally
+ End If
+ If _ReadOnly Then GoTo Catch_ReadOnly
+
+Try:
+ With _Connection
+ bCommit = ( TransactionMode &gt; com.sun.star.sdbc.TransactionIsolation.NONE )
+ &apos; Replace the existing connection
+ If Not IsNull(_Connection) Then
+ If .AutoCommit And bCommit Then
+ _CloseConnection()
+ Set _Connection = _DataSource.getIsolatedConnection(_User, _Password)
+ ElseIf Not .AutoCommit And Not bCommit Then
+ _CloseConnection()
+ Set _Connection = _DataSource.getConnection(_User, _Password)
+ End If
+ End If
+
+ &apos; Set the transaction mode
+ If bCommit Then
+ .SetTransactionIsolation(CLng(TransactionMode))
+ .setAutoCommit(Not bCommit)
+ End If
+ End With
+
+ bSet = True
+
+Finally:
+ SetTransactionMode = bSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+Catch_ReadOnly:
+ ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database.SetTransactionMode
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Function _AddToDatasets(ByRef poDataset As Object) As Long
+&apos;&apos;&apos; Insert a newly created Dataset instance in the open datasets array
+&apos;&apos;&apos; and return the index of the used entry.
+&apos;&apos;&apos; Empty space is reused.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; poDataset: the dataset instance to insert
+
+Dim lIndex As Long &apos; Return value
+Dim lSize As Long &apos; UBound of the _datasets array
+Dim i As Long
+
+Check:
+ lIndex = -1
+ If IsNull(poDataset) Then Exit Function
+ On Local Error GoTo Finally
+ lSize = UBound(_Datasets)
+
+Try:
+ &apos; Can an empty entry be reused ?
+ For i = 0 To lSize
+ If IsNull(_Datasets(i)) Then
+ lIndex = i
+ Exit For
+ End If
+ Next i
+
+ &apos; Resize _Datasets if no empty space
+ If lIndex &lt; 0 Then
+ lSize = lSize + 1
+ If lSize &gt; 0 Then
+ ReDim Preserve _Datasets(0 To lSize)
+ Else
+ ReDim _Datasets (0 To 0)
+ End If
+ lIndex = lSize
+ End If
+
+ &apos; Insert new object
+ Set _Datasets(lIndex) = poDataset
+
+Finally:
+ _AddToDatasets = lIndex
+ Exit Function
+End Function &apos; SFDatabases.SF_Database._AddToDatasets
+
+REM -----------------------------------------------------------------------------
+Private Sub _CloseConnection()
+&apos;&apos;&apos; Close the actual connection
+&apos;&apos;&apos; To enable transaction modes, it is necessary to reinitialize the connection to the datasource.
+&apos;&apos;&apos; The reinit includes
+&apos;&apos;&apos; - the closure of all open datasets
+&apos;&apos;&apos; - flushing the buffered and committed updates
+&apos;&apos;&apos; - the effective closure
+&apos;&apos;&apos; Otherwise the experience showed undesired side-effects.
+
+Dim oParent As Object &apos; Parent of actual connection
+Dim oDataset As Object &apos; Single dataset in the _Datasets array
+Dim oSession As Object : Set oSession = ScriptForge.SF_Session
+Dim i As Long
+
+ On Local Error GoTo Finally &apos; Never abort
+
+Check:
+ If IsNull(_Connection) Then Exit Sub
+
+Try:
+ &apos; Close datasets
+ For i = 0 To UBound(_Datasets)
+ Set oDataset = _Datasets(i)
+ If Not IsNull(oDataset) Then oDataset.CloseDataset()
+ Set _Datasets(i) = Nothing
+ Next i
+ _Datasets = Array()
+
+ &apos; Flush buffers
+ _FlushConnection()
+
+ &apos; Close the connection
+ _Connection.close()
+ _Connection.dispose()
+
+Finally:
+ On Local Error GoTo 0
+ Exit Sub
+End Sub &apos; SFDatabases.SF_Database._CloseConnection
+
+REM -----------------------------------------------------------------------------
+Private Function _CollectFormDocuments(ByRef poContainer As Object) As String
+&apos;&apos;&apos; Returns a token-separated string of all hierarchical formdocument names
+&apos;&apos;&apos; depending on the formdocuments container in argument
+&apos;&apos;&apos; The function traverses recursively the whole tree below the container
+&apos;&apos;&apos; The initial call starts from the container _Component.getFormDocuments
+&apos;&apos;&apos; The list contains closed and open forms
+
+Dim sCollectNames As String &apos; Return value
+Dim oSubItem As Object &apos; com.sun.star.container.XNameAccess (folder) or com.sun.star.ucb.XContent (form)
+Dim i As Long
+Const cstFormType = &quot;application/vnd.oasis.opendocument.text&quot;
+ &apos; Identifies forms. Folders have a zero-length content type
+
+ On Local Error GoTo Finally
+
+Try:
+ sCollectNames = &quot;&quot;
+ With poContainer
+ For i = 0 To .Count - 1
+ Set oSubItem = .getByIndex(i)
+ If oSubItem.ContentType = cstFormType Then &apos; Add the form to the list
+ sCollectNames = sCollectNames &amp; cstToken &amp; oSubItem.HierarchicalName
+ Else
+ sCollectNames = sCollectNames &amp; cstToken &amp; _CollectFormDocuments(oSubItem)
+ End If
+ Next i
+ End With
+
+Finally:
+ If Len(sCollectNames) &gt; 0 Then
+ _CollectFormDocuments = Mid(sCollectNames, Len(cstToken) + 1) &apos; Skip the initial token
+ Else
+ _CollectFormDocuments = &quot;&quot;
+ End If
+ Exit Function
+End Function &apos; SFDatabases.SF_Database._CollectFormDocuments
+
+REM -----------------------------------------------------------------------------------------------------------------------
+Private Function _DFunction(ByVal psFunction As String _
+ , Optional ByVal pvExpression As Variant _
+ , Optional ByVal pvTableName As Variant _
+ , Optional ByVal pvCriteria As Variant _
+ , Optional ByVal pvOrderClause As Variant _
+ ) As Variant
+&apos;&apos;&apos; Build and execute a SQL statement computing the aggregate function psFunction
+&apos;&apos;&apos; on a field or expression pvExpression belonging to a table pvTableName
+&apos;&apos;&apos; filtered by a WHERE-clause pvCriteria.
+&apos;&apos;&apos; To order the results, a pvOrderClause may be precised.
+&apos;&apos;&apos; Only the 1st record will be retained anyway.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psFunction an optional aggregate function: SUM, COUNT, AVG, LOOKUP
+&apos;&apos;&apos; pvExpression: an SQL expression
+&apos;&apos;&apos; pvTableName: the name of a table, NOT surrounded with quoting char
+&apos;&apos;&apos; pvCriteria: an optional WHERE clause without the word WHERE
+&apos;&apos;&apos; pvOrderClause: an optional order clause incl. &quot;DESC&quot; if relevant
+&apos;&apos;&apos; (meaningful only for LOOKUP)
+
+Dim vResult As Variant &apos; Return value
+Dim oResult As Object &apos; com.sun.star.sdbc.XResultSet
+Dim sSql As String &apos; SQL statement.
+Dim sExpr As String &apos; For inclusion of aggregate function
+Dim sTarget as String &apos; Alias of pvExpression
+Dim sWhere As String &apos; Alias of pvCriteria
+Dim sOrderBy As String &apos; Alias of pvOrderClause
+Dim sLimit As String &apos; TOP 1 clause
+Dim sProductName As String &apos; RDBMS as a string
+Const cstAliasField = &quot;[&quot; &amp; &quot;TMP_ALIAS_ANY_FIELD&quot; &amp; &quot;]&quot; &apos; Alias field in SQL expression
+Dim cstThisSub As String : cstThisSub = &quot;SFDatabases.SF_Database.D&quot; &amp; psFunction
+Const cstSubArgs = &quot;Expression, TableName, [Criteria=&quot;&quot;&quot;&quot;], [OrderClause=&quot;&quot;&quot;&quot;]&quot;
+Const cstLookup = &quot;Lookup&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vResult = Null
+
+Check:
+ If IsMissing(pvCriteria) Or IsEmpty(pvCriteria) Then pvCriteria = &quot;&quot;
+ If IsMissing(pvOrderClause) Or IsEmpty(pvOrderClause) Then pvOrderClause = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(pvExpression, &quot;Expression&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvTableName, &quot;TableName&quot;, V_STRING, Tables) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvCriteria, &quot;Criteria&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvOrderClause, &quot;OrderClause&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ If pvCriteria &lt;&gt; &quot;&quot; Then sWhere = &quot; WHERE &quot; &amp; pvCriteria Else sWhere = &quot;&quot;
+ If pvOrderClause &lt;&gt; &quot;&quot; Then sOrderBy = &quot; ORDER BY &quot; &amp; pvOrderClause Else sOrderBy = &quot;&quot;
+ sLimit = &quot;&quot;
+
+ pvTableName = &quot;[&quot; &amp; pvTableName &amp; &quot;]&quot;
+
+ sProductName = UCase(_MetaData.getDatabaseProductName())
+
+ Select Case sProductName
+ Case &quot;MYSQL&quot;, &quot;SQLITE&quot;
+ If psFunction = cstLookup Then
+ sTarget = pvExpression
+ sLimit = &quot; LIMIT 1&quot;
+ Else
+ sTarget = UCase(psFunction) &amp; &quot;(&quot; &amp; pvExpression &amp; &quot;)&quot;
+ End If
+ sSql = &quot;SELECT &quot; &amp; sTarget &amp; &quot; AS &quot; &amp; cstAliasField &amp; &quot; FROM &quot; &amp; psTableName &amp; sWhere &amp; sOrderBy &amp; sLimit
+ Case &quot;FIREBIRD (ENGINE12)&quot;
+ If psFunction = cstLookup Then sTarget = &quot;FIRST 1 &quot; &amp; pvExpression Else sTarget = UCase(psFunction) &amp; &quot;(&quot; &amp; pvExpression &amp; &quot;)&quot;
+ sSql = &quot;SELECT &quot; &amp; sTarget &amp; &quot; AS &quot; &amp; cstAliasField &amp; &quot; FROM &quot; &amp; pvTableName &amp; sWhere &amp; sOrderBy
+ Case Else &apos; Standard syntax - Includes HSQLDB
+ If psFunction = cstLookup Then sTarget = &quot;TOP 1 &quot; &amp; pvExpression Else sTarget = UCase(psFunction) &amp; &quot;(&quot; &amp; pvExpression &amp; &quot;)&quot;
+ sSql = &quot;SELECT &quot; &amp; sTarget &amp; &quot; AS &quot; &amp; cstAliasField &amp; &quot; FROM &quot; &amp; pvTableName &amp; sWhere &amp; sOrderBy
+ End Select
+
+ &apos; Execute the SQL statement and retain the first column of the first record
+ Set oResult = _ExecuteSql(sSql, True)
+ If Not IsNull(oResult) And Not IsEmpty(oResult) Then
+ If oResult.first() Then vResult = _GetColumnValue(oResult, 1) Else GoTo Finally
+ End If
+ Set oResult = Nothing
+
+Finally:
+ _DFunction = vResult
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database._DFunction
+
+REM -----------------------------------------------------------------------------
+Private Function _ExecuteSql(ByVal psSql As String _
+ , ByVal pbDirect As Boolean _
+ ) As Variant
+&apos;&apos;&apos; Return a read-only Resultset based on a SELECT SQL statement or execute the given action SQL (INSERT, CREATE TABLE, ...)
+&apos;&apos;&apos; The method raises a fatal error when the SQL statement cannot be interpreted
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psSql : the SQL statement. Square brackets are replaced by the correct field surrounding character
+&apos;&apos;&apos; pbDirect: when True, no syntax conversion is done by LO. Default = False
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; SQLSYNTAXERROR The given SQL statement is incorrect
+
+Dim vResult As Variant &apos; Return value - com.sun.star.sdbc.XResultSet or Boolean
+Dim oStatement As Object &apos; com.sun.star.sdbc.XStatement
+Dim sSql As String &apos; Alias of psSql
+Dim bSelect As Boolean &apos; True when SELECT statement
+Dim bErrorHandler As Boolean &apos; Can be set off to ease debugging of complex SQL statements
+
+ Set vResult = Nothing
+ bErrorHandler = ScriptForge.SF_Utils._ErrorHandling()
+ If bErrorHandler Then On Local Error GoTo Catch
+
+Try:
+ sSql = _ReplaceSquareBrackets(psSql)
+ bSelect = ScriptForge.SF_String.StartsWith(sSql, &quot;SELECT&quot;, CaseSensitive := False)
+
+ Set oStatement = _Connection.createStatement()
+ With oStatement
+ If bSelect Then
+ .ResultSetType = com.sun.star.sdbc.ResultSetType.SCROLL_INSENSITIVE
+ .ResultSetConcurrency = com.sun.star.sdbc.ResultSetConcurrency.READ_ONLY
+ End If
+ .EscapeProcessing = Not pbDirect
+
+ &apos; Setup the result set
+ If bErrorHandler Then On Local Error GoTo Catch_Sql
+ If bSelect Then Set vResult = .executeQuery(sSql) Else vResult = .execute(sSql)
+ End With
+
+Finally:
+ _ExecuteSql = vResult
+ Set oStatement = Nothing
+ Exit Function
+Catch_Sql:
+ On Local Error GoTo 0
+ ScriptForge.SF_Exception.RaiseFatal(SQLSYNTAXERROR, sSql)
+ GoTo Finally
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database._ExecuteSql
+
+REM -----------------------------------------------------------------------------
+Private Sub _FlushConnection()
+&apos;&apos;&apos; Empties the buffers of the actual connection
+&apos;&apos;&apos; Sub called after each commit and at connection closure..
+
+Dim oParent As Object &apos; Parent of actual connection
+Dim oSession As Object : Set oSession = ScriptForge.SF_Session
+
+ On Local Error GoTo Finally &apos; Never abort
+
+Check:
+ If IsNull(_Connection) Then Exit Sub
+
+Try:
+ &apos; Flush buffers
+ With oSession
+ If .HasUnoMethod(_Connection, &quot;getParent&quot;) Then
+ Set oParent = _Connection.getParent()
+ If .HasUnoMethod(oParent, &quot;flush&quot;) Then oParent.flush()
+ ElseIf .HasUnoMethod(_Connection, &quot;flush&quot;) Then
+ _Connection.flush()
+ End If
+ End With
+
+Finally:
+ Exit Sub
+End Sub &apos; SFDatabases.SF_Database._FlushConnection
+
+REM -----------------------------------------------------------------------------
+Private Function _GetColumnValue(ByRef poResultSet As Object _
+ , ByVal plColIndex As Long _
+ ) As Variant
+&apos;&apos;&apos; Get the data stored in the current record of a result set in a given column
+&apos;&apos;&apos; The type of the column is found in the resultset&apos;s metadata
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; poResultSet: com.sun.star.sdbc.XResultSet or com.sun.star.awt.XTabControllerModel
+&apos;&apos;&apos; plColIndex: the index of the column to extract the value from. Starts at 1
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The Variant value found in the column
+&apos;&apos;&apos; Dates and times are returned as Basic dates
+&apos;&apos;&apos; Null values are returned as Null
+&apos;&apos;&apos; Binary fields are returned as a Long giving their length
+&apos;&apos;&apos; Errors or strange data types are returned as Null as well
+
+Dim vValue As Variant &apos; Return value
+Dim lType As Long &apos; SQL column type: com.sun.star.sdbc.DataType
+Dim vDateTime As Variant &apos; com.sun.star.util.DateTime
+Dim oStream As Object &apos; Long character or binary streams
+Dim bNullable As Boolean &apos; The field is defined as accepting Null values
+Dim lSize As Long &apos; Binary field length
+
+Const cstMaxBinlength = 2 * 65535
+
+ On Local Error Goto 0 &apos; Disable error handler
+ vValue = Empty &apos; Default value if error
+
+ With com.sun.star.sdbc.DataType
+ lType = poResultSet.MetaData.getColumnType(plColIndex)
+ bNullable = ( poResultSet.MetaData.IsNullable(plColIndex) = com.sun.star.sdbc.ColumnValue.NULLABLE )
+
+ Select Case lType
+ Case .ARRAY : vValue = poResultSet.getArray(plColIndex)
+ Case .BINARY, .VARBINARY, .LONGVARBINARY, .BLOB
+ Set oStream = poResultSet.getBinaryStream(plColIndex)
+ If IsNull(oStream) Then
+ lSize = 0
+ Else
+ If bNullable Then
+ If Not poResultSet.wasNull() Then lSize = CLng(oStream.getLength()) Else lSize = 0
+ Else
+ lSize = CLng(oStream.getLength())
+ End If
+ oStream.closeInput()
+ End If
+ vValue = lSize &apos; Return length of field, not content
+ Case .BIT, .BOOLEAN : vValue = poResultSet.getBoolean(plColIndex)
+ Case .DATE
+ vDateTime = poResultSet.getDate(plColIndex)
+ If Not poResultSet.wasNull() Then vValue = DateSerial(CInt(vDateTime.Year), CInt(vDateTime.Month), CInt(vDateTime.Day))
+ Case .DISTINCT, .OBJECT, .OTHER, .STRUCT
+ vValue = Null
+ Case .DOUBLE, .REAL : vValue = poResultSet.getDouble(plColIndex)
+ Case .FLOAT : vValue = poResultSet.getFloat(plColIndex)
+ Case .INTEGER, .SMALLINT : vValue = poResultSet.getInt(plColIndex)
+ Case .BIGINT : vValue = CLng(poResultSet.getLong(plColIndex))
+ Case .DECIMAL, .NUMERIC : vValue = poResultSet.getDouble(plColIndex)
+ Case .SQLNULL : vValue = poResultSet.getNull(plColIndex)
+ Case .REF : vValue = poResultSet.getRef(plColIndex)
+ Case .TINYINT : vValue = poResultSet.getShort(plColIndex)
+ Case .CHAR, .VARCHAR, .LONGVARCHAR, .CLOB
+ vValue = poResultSet.getString(plColIndex)
+ Case .TIME
+ vDateTime = poResultSet.getTime(plColIndex)
+ If Not poResultSet.wasNull() Then vValue = TimeSerial(vDateTime.Hours, vDateTime.Minutes, vDateTime.Seconds)&apos;, vDateTime.HundredthSeconds)
+ Case .TIMESTAMP
+ vDateTime = poResultSet.getTimeStamp(plColIndex)
+ If Not poResultSet.wasNull() Then vValue = DateSerial(CInt(vDateTime.Year), CInt(vDateTime.Month), CInt(vDateTime.Day)) _
+ + TimeSerial(vDateTime.Hours, vDateTime.Minutes, vDateTime.Seconds)&apos;, vDateTime.HundredthSeconds)
+ Case Else
+ vValue = poResultSet.getString(plColIndex) &apos;GIVE STRING A TRY
+ If IsNumeric(vValue) Then vValue = Val(vValue) &apos;Required when type = &quot;&quot;, sometimes numeric fields are returned as strings (query/MSAccess)
+ End Select
+ &apos; .wasNull() must be preceded by getXXX(). Done. Test for Null here.
+ If bNullable Then
+ If poResultSet.wasNull() Then vValue = Null
+ End If
+ End With
+
+ _GetColumnValue = vValue
+
+End Function &apos; SFDatabases.SF_Database._GetColumnValue
+
+REM -----------------------------------------------------------------------------
+Public Function _OpenDatasheet(Optional ByVal psCommand As Variant _
+ , piDatasheetType As Integer _
+ , pbEscapeProcessing As Boolean _
+ ) As Object
+&apos;&apos;&apos; Open the datasheet given by its name and its type
+&apos;&apos;&apos; The datasheet will live independently from any other component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psCommand: a valid table or query name or an SQL statement as a case-sensitive string
+&apos;&apos;&apos; piDatasheetType: one of the com.sun.star.sdb.CommandType constants
+&apos;&apos;&apos; pbEscapeProcessing: == Not DirectSql
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the datasheet could be opened, otherwise Nothing
+
+Dim oOpen As Object &apos; Return value
+Dim oNewDatasheet As Object &apos; com.sun.star.lang.XComponent
+Dim oURL As Object &apos; com.sun.star.util.URL
+Dim oDispatch As Object &apos; com.sun.star.frame.XDispatch
+Dim vArgs As Variant &apos; Array of property values
+
+ On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Try:
+ &apos; Setup the dispatcher
+ Set oURL = New com.sun.star.util.URL
+ oURL.Complete = &quot;.component:DB/DataSourceBrowser&quot;
+ Set oDispatch = StarDesktop.queryDispatch(oURL, &quot;_blank&quot;, com.sun.star.frame.FrameSearchFlag.CREATE)
+
+ &apos; Setup the arguments of the component to create
+ With ScriptForge.SF_Utils
+ vArgs = Array( _
+ ._MakePropertyValue(&quot;ActiveConnection&quot;, _Connection) _
+ , ._MakePropertyValue(&quot;CommandType&quot;, piDatasheetType) _
+ , ._MakePropertyValue(&quot;Command&quot;, psCommand) _
+ , ._MakePropertyValue(&quot;ShowMenu&quot;, True) _
+ , ._MakePropertyValue(&quot;ShowTreeView&quot;, False) _
+ , ._MakePropertyValue(&quot;ShowTreeViewButton&quot;, False) _
+ , ._MakePropertyValue(&quot;Filter&quot;, &quot;&quot;) _
+ , ._MakePropertyValue(&quot;ApplyFilter&quot;, False) _
+ , ._MakePropertyValue(&quot;EscapeProcessing&quot;, pbEscapeProcessing) _
+ )
+ End With
+
+ &apos; Open the targeted datasheet
+ Set oNewDatasheet = oDispatch.dispatchWithReturnValue(oURL, vArgs)
+ If Not IsNull(oNewDatasheet) Then Set oOpen = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Datasheet&quot;, oNewDatasheet, [Me])
+
+Finally:
+ Set _OpenDatasheet = oOpen
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base._OpenDatasheet
+
+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 cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ cstThisSub = &quot;SFDatabases.Database.get&quot; &amp; psProperty
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+ Select Case psProperty
+ Case &quot;Queries&quot;
+ If Not IsNull(_Connection) Then _PropertyGet = _Connection.Queries.getElementNames() Else _PropertyGet = Array()
+ Case &quot;Tables&quot;
+ If Not IsNull(_Connection) Then _PropertyGet = _Connection.Tables.getElementNames() Else _PropertyGet = Array()
+ Case &quot;XConnection&quot;
+ Set _PropertyGet = _Connection
+ Case &quot;XMetaData&quot;
+ Set _PropertyGet = _MetaData
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Database._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _ReplaceSquareBrackets(ByVal psSql As String) As String
+&apos;&apos;&apos; Returns the input SQL command after replacement of square brackets by the table/field names quoting character
+
+Dim sSql As String &apos; Return value
+Dim sQuote As String &apos; RDBMS specific table/field surrounding character
+Dim sConstQuote As String &apos; Delimiter for string constants in SQL - usually the single quote
+Const cstDouble = &quot;&quot;&quot;&quot; : Const cstSingle = &quot;&apos;&quot;
+
+Try:
+ sQuote = _MetaData.IdentifierQuoteString
+ sConstQuote = Iif(sQuote = cstSingle, cstDouble, cstSingle)
+
+ &apos; Replace the square brackets
+ sSql = Join(ScriptForge.SF_String.SplitNotQuoted(psSql, &quot;[&quot;, , sConstQuote), sQuote)
+ sSql = Join(ScriptForge.SF_String.SplitNotQuoted(sSql, &quot;]&quot;, , sConstQuote), sQuote)
+
+Finally:
+ _ReplaceSquareBrackets = sSql
+ Exit Function
+End Function &apos; SFDatabases.SF_Database._ReplaceSquareBrackets
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the Database instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[DATABASE]: Location (Statusbar)&quot;
+
+ _Repr = &quot;[DATABASE]: &quot; &amp; _Location &amp; &quot; (&quot; &amp; _URL &amp; &quot;)&quot;
+
+End Function &apos; SFDatabases.SF_Database._Repr
+
+REM ============================================ END OF SFDATABASES.SF_DATABASE
+</script:module> \ No newline at end of file
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
diff --git a/wizards/source/sfdatabases/SF_Datasheet.xba b/wizards/source/sfdatabases/SF_Datasheet.xba
new file mode 100644
index 0000000000..89e66aefd6
--- /dev/null
+++ b/wizards/source/sfdatabases/SF_Datasheet.xba
@@ -0,0 +1,952 @@
+<?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_Datasheet" 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_Datasheet
+&apos;&apos;&apos; ============
+&apos;&apos;&apos; A datasheet is the visual representation of tabular data produced by a database.
+&apos;&apos;&apos; In the user interface of LibreOffice it is the result of the opening of
+&apos;&apos;&apos; a table or a query. In this case the concerned Base document must be open.
+&apos;&apos;&apos;
+&apos;&apos;&apos; In the context of ScriptForge, a datasheet may be opened automatically by script code :
+&apos;&apos;&apos; - either by reproducing the behaviour of the user interface
+&apos;&apos;&apos; - or at any moment. In this case the Base document may or may not be opened.
+&apos;&apos;&apos; Additionally, any SELECT SQL statement may define the datasheet display.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The proposed API allows for either datasheets (opened manually of by code) in particular
+&apos;&apos;&apos; to know which cell is selected and its content.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation:
+&apos;&apos;&apos; 1) From an open Base document
+&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
+&apos;&apos;&apos; Set oBase = ui.getDocument(&quot;/home/user/Documents/myDb.odb&quot;)
+&apos;&apos;&apos; Set oSheet = oBase.OpenTable(&quot;Customers&quot;) &apos; or OpenQuery(...)
+&apos;&apos;&apos; &apos; May be executed also when the given table is already open
+&apos;&apos;&apos; 2) Independently from a Base document
+&apos;&apos;&apos; Set oDatabase = CreateScriptService(&quot;Database&quot;, &quot;/home/user/Documents/myDb.odb&quot;)
+&apos;&apos;&apos; Set oSheet = oDatabase.OpenTable(&quot;Customers&quot;)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_datasheet.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
+
+Private Const DOCUMENTDEADERROR = &quot;DOCUMENTDEADERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object &apos; Base instance when opened from a Base document by code
+ &apos; or Database instance when opened without Base document
+Private ObjectType As String &apos; Must be DATASHEET
+Private ServiceName As String
+
+Private _Component As Object &apos; com.sun.star.lang.XComponent - org.openoffice.comp.dbu.ODatasourceBrowser
+Private _Frame As Object &apos; com.sun.star.frame.XFrame
+Private _ParentBase As Object &apos; The parent SF_Base instance (may be void)
+Private _ParentDatabase As Object &apos; The parent SF_Database instance (must not be void)
+Private _SheetType As String &apos; TABLE, QUERY or SQL
+Private _ParentType As String &apos; BASE or DATABASE
+Private _BaseFileName As String &apos; URL format of parent Base file
+Private _Command As String &apos; Table name, query name or SQL statement
+Private _DirectSql As Boolean &apos; When True, SQL processed by RDBMS
+Private _TabControllerModel As Object &apos; com.sun.star.awt.XTabControllerModel - com.sun.star.comp.forms.ODatabaseForm
+Private _ControlModel As Object &apos; com.sun.star.awt.XControlModel - com.sun.star.form.OGridControlModel
+Private _ControlView As Object &apos; com.sun.star.awt.XControl - org.openoffice.comp.dbu.ODatasourceBrowser
+Private _ColumnHeaders As Variant &apos; List of column headers as an array of strings
+
+&apos; Cache for static toolbar descriptions
+Private _Toolbars As Object &apos; SF_Dictionary instance to hold toolbars stored in application or in document
+
+REM ============================================================ MODULE CONSTANTS
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;DATASHEET&quot;
+ ServiceName = &quot;SFDatabases.Datasheet&quot;
+ Set _Component = Nothing
+ Set _Frame = Nothing
+ Set _ParentBase = Nothing
+ Set _ParentDatabase = Nothing
+ _SheetType = &quot;&quot;
+ _ParentType = &quot;&quot;
+ _BaseFileName = &quot;&quot;
+ _Command = &quot;&quot;
+ _DirectSql = False
+ Set _TabControllerModel = Nothing
+ Set _ControlModel = Nothing
+ Set _ControlView = Nothing
+ _ColumnHeaders = Array()
+ Set _Toolbars = Nothing
+End Sub &apos; SFDatabases.SF_Datasheet Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDatabases.SF_Datasheet Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDatabases.SF_Datasheet Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get ColumnHeaders() As Variant
+&apos;&apos;&apos; Returns the list of column headers of the datasheet as an array of strings
+ ColumnHeaders = _PropertyGet(&quot;ColumnHeaders&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.ColumnHeaders
+
+REM -----------------------------------------------------------------------------
+Property Get CurrentColumn() As String
+&apos;&apos;&apos; Returns the currently selected column by its name
+ CurrentColumn = _PropertyGet(&quot;CurrentColumn&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.CurrentColumn
+
+REM -----------------------------------------------------------------------------
+Property Get CurrentRow() As Long
+&apos;&apos;&apos; Returns the currently selected row by its number &gt;= 1
+ CurrentRow = _PropertyGet(&quot;CurrentRow&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.CurrentRow
+
+REM -----------------------------------------------------------------------------
+Property Get DatabaseFileName() As String
+&apos;&apos;&apos; Returns the file name of the Base file in FSO.FileNaming format
+ DatabaseFileName = _PropertyGet(&quot;DatabaseFileName&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.DatabaseFileName
+
+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_Datasheet.Filter (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Filter(Optional ByVal pvFilter As Variant)
+&apos;&apos;&apos; Set the updatable property Filter
+&apos;&apos;&apos; Table and field names may be surrounded by square brackets
+&apos;&apos;&apos; When the argument is the zero-length string, the actual filter is removed
+ _PropertySet(&quot;Filter&quot;, pvFilter)
+End Property &apos; SFDatabases.SF_Datasheet.Filter (let)
+
+REM -----------------------------------------------------------------------------
+Property Get LastRow() As Long
+&apos;&apos;&apos; Returns the total number of rows
+&apos;&apos;&apos; The process may imply to move the cursor to the last available row.
+&apos;&apos;&apos; Afterwards the cursor is reset to the current row.
+ LastRow = _PropertyGet(&quot;LastRow&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.LastRow
+
+REM -----------------------------------------------------------------------------
+Property Get OrderBy() As Variant
+&apos;&apos;&apos; The Order is a SQL ORDER BY clause without the ORDER BY keywords
+ OrderBy = _PropertyGet(&quot;OrderBy&quot;)
+End Property &apos; SFDocuments.SF_Form.OrderBy (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OrderBy(Optional ByVal pvOrderBy As Variant)
+&apos;&apos;&apos; Set the updatable property OrderBy
+&apos;&apos;&apos; Table and field names may be surrounded by square brackets
+&apos;&apos;&apos; When the argument is the zero-length string, the actual sort is removed
+ _PropertySet(&quot;OrderBy&quot;, pvOrderBy)
+End Property &apos; SFDocuments.SF_Form.OrderBy (let)
+
+REM -----------------------------------------------------------------------------
+Property Get ParentDatabase() As Object
+&apos;&apos;&apos; Returns the database instance to which the datasheet belongs
+ Set ParentDatabase = _PropertyGet(&quot;ParentDatabase&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.ParentDatabase
+
+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_Datasheet.Source
+
+REM -----------------------------------------------------------------------------
+Property Get SourceType() As String
+&apos;&apos;&apos; Returns thetype of source of the data: TABLE, QUERY or SQL
+ SourceType = _PropertyGet(&quot;SourceType&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.SourceType
+
+REM -----------------------------------------------------------------------------
+Property Get XComponent() As Object
+&apos;&apos;&apos; Returns the com.sun.star.lang.XComponent UNO object representing the datasheet
+ XComponent = _PropertyGet(&quot;XComponent&quot;)
+End Property &apos; SFDocuments.SF_Document.XComponent
+
+REM -----------------------------------------------------------------------------
+Property Get XControlModel() As Object
+&apos;&apos;&apos; Returns the com.sun.star.lang.XControl UNO object representing the datasheet
+ XControlModel = _PropertyGet(&quot;XControlModel&quot;)
+End Property &apos; SFDocuments.SF_Document.XControlModel
+
+REM -----------------------------------------------------------------------------
+Property Get XTabControllerModel() As Object
+&apos;&apos;&apos; Returns the com.sun.star.lang.XTabControllerModel UNO object representing the datasheet
+ XTabControllerModel = _PropertyGet(&quot;XTabControllerModel&quot;)
+End Property &apos; SFDocuments.SF_Document.XTabControllerModel
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Sub Activate()
+&apos;&apos;&apos; Make the actual datasheet active
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.Activate()
+
+Dim oContainer As Object &apos; com.sun.star.awt.XWindow
+Const cstThisSub = &quot;SFDatabases.Datasheet.Activate&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+Try:
+ Set oContainer = _Component.Frame.ContainerWindow
+ With oContainer
+ If .isVisible() = False Then .setVisible(True)
+ .IsMinimized = False
+ .setFocus()
+ .toFront() &apos; Force window change in Linux
+ Wait 1 &apos; Bypass desynchro issue in Linux
+ End With
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; SFDatabases.SF_Datasheet.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDatasheet() As Boolean
+&apos;&apos;&apos; Close the actual datasheet
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.CloseDatasheet()
+
+Dim bClose As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Datasheet.CloseDatasheet&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bClose = False
+
+Check:
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+Try:
+ With _TabControllerModel
+ .ApplyFilter = False
+ .Filter = &quot;&quot;
+ .close()
+ End With
+ _Frame.close(True)
+ _Frame.dispose()
+ Dispose()
+ bClose = True
+
+Finally:
+ CloseDatasheet = bClose
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.CloseDatasheet
+
+REM -----------------------------------------------------------------------------
+Public Function CreateMenu(Optional ByVal MenuHeader As Variant _
+ , Optional ByVal Before As Variant _
+ , Optional ByVal SubmenuChar As Variant _
+ ) As Object
+&apos;&apos;&apos; Create a new menu entry in the datasheet&apos;s menubar
+&apos;&apos;&apos; The menu is not intended to be saved neither in the LibreOffice global environment, nor elsewhere
+&apos;&apos;&apos; The method returns a SFWidgets.Menu instance. Its methods let define the menu further.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; MenuHeader: the name/header of the menu
+&apos;&apos;&apos; Before: the place where to put the new menu on the menubar (string or number &gt;= 1)
+&apos;&apos;&apos; When not found =&gt; last position
+&apos;&apos;&apos; SubmenuChar: the delimiter used in menu trees. Default = &quot;&gt;&quot;
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A SFWidgets.Menu instance or Nothing
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim oMenu As Object
+&apos;&apos;&apos; Set oMenu = oDoc.CreateMenu(&quot;My menu&quot;, Before := &quot;Styles&quot;)
+&apos;&apos;&apos; With oMenu
+&apos;&apos;&apos; .AddItem(&quot;Item 1&quot;, Command := &quot;.uno:About&quot;)
+&apos;&apos;&apos; &apos;...
+&apos;&apos;&apos; .Dispose() &apos; When definition is complete, the menu instance may be disposed
+&apos;&apos;&apos; End With
+&apos;&apos;&apos; &apos; ...
+
+Dim oMenu As Object &apos; return value
+Const cstThisSub = &quot;SFDatabases.Datasheet.CreateMenu&quot;
+Const cstSubArgs = &quot;MenuHeader, [Before=&quot;&quot;&quot;&quot;], [SubmenuChar=&quot;&quot;&gt;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oMenu = Nothing
+
+Check:
+ If IsMissing(Before) Or IsEmpty(Before) Then Before = &quot;&quot;
+ If IsMissing(SubmenuChar) Or IsEmpty(SubmenuChar) Then SubmenuChar = &quot;&quot;
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(MenuHeader, &quot;MenuHeader&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Before, &quot;Before&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(SubmenuChar, &quot;SubmenuChar&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oMenu = ScriptForge.SF_Services.CreateScriptService(&quot;SFWidgets.Menu&quot;, _Component, MenuHeader, Before, SubmenuChar)
+
+Finally:
+ Set CreateMenu = oMenu
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Document.CreateMenu
+
+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.Datasheet.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_Datasheet.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function GetText(Optional ByVal Column As Variant) As String
+&apos;&apos;&apos; Get the text in the given column of the current row.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Column: the name of the column as a string or its position (&gt;= 1). Default = the current column
+&apos;&apos;&apos; If the argument exceeds the number of columns, the last column is selected.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The text in the cell as a string as how it is displayed
+&apos;&apos;&apos; Note that the position of the cursor is left unchanged.
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.GetText(&quot;ShipCity&quot;)) &apos; Extract the text on the current row from the column &quot;ShipCity&quot;
+
+Dim sText As String &apos; Return Text
+Dim lCol As Long &apos; Numeric index of Column in lists of columns
+Dim lMaxCol As Long &apos; Index of last column
+Const cstThisSub = &quot;SFDatabases.Datasheet.GetText&quot;
+Const cstSubArgs = &quot;[Column=0]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sText = &quot;&quot;
+
+Check:
+ If IsMissing(Column) Or IsEmpty(Column) Then Column = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If VarType(Column) &lt;&gt; V_STRING Then
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, V_STRING, _ColumnHeaders) Then GoTo Catch
+ End If
+ End If
+
+Try:
+ &apos; Position the column - The index to be passed starts at 0
+ With _ControlView
+ If VarType(Column) = V_STRING Then
+ lCol = ScriptForge.SF_Array.IndexOf(_ColumnHeaders, Column, CaseSensitive := False)
+ Else
+ lCol = -1
+ If Column &gt;= 1 Then
+ lMaxCol = .Count - 1
+ If Column &gt; lMaxCol + 1 Then lCol = lMaxCol Else lCol = Column - 1
+ Else
+ lCol = .getCurrentColumnPosition()
+ End If
+ End If
+
+ If lCol &gt;= 0 Then sText = .getByIndex(lCol).Text
+ End With
+
+Finally:
+ GetText = sText
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.GetText
+
+REM -----------------------------------------------------------------------------
+Public Function GetValue(Optional ByVal Column As Variant) As Variant
+&apos;&apos;&apos; Get the value in the given column of the current row.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Column: the name of the column as a string or its position (&gt;= 1). Default = the current column
+&apos;&apos;&apos; If the argument exceeds the number of columns, the last column is selected.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The value in the cell as a valid Basic type
+&apos;&apos;&apos; Typical types are: STRING, INTEGER, LONG, FLOAT, DOUBLE, DATE, NULL
+&apos;&apos;&apos; Binary types are returned as a LONG giving their length, not their content
+&apos;&apos;&apos; An EMPTY return value means that the value could not be retrieved.
+&apos;&apos;&apos; Note that the position of the cursor is left unchanged.
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.GetValue(&quot;ShipCity&quot;)) &apos; Extract the value on the current row from the column &quot;ShipCity&quot;
+
+Dim vValue As Variant &apos; Return value
+Dim lCol As Long &apos; Numeric index of Column in lists of columns
+Dim lMaxCol As Long &apos; Index of last column
+Const cstThisSub = &quot;SFDatabases.Datasheet.GetValue&quot;
+Const cstSubArgs = &quot;[Column=0]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vValue = Empty
+
+Check:
+ If IsMissing(Column) Or IsEmpty(Column) Then Column = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If VarType(Column) &lt;&gt; V_STRING Then
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, V_STRING, _ColumnHeaders) Then GoTo Catch
+ End If
+ End If
+
+Try:
+ &apos; Position the column - The index to be passed starts at 1
+ If VarType(Column) = V_STRING Then
+ lCol = ScriptForge.SF_Array.IndexOf(_ColumnHeaders, Column, CaseSensitive := False) + 1
+ Else
+ With _ControlView
+ lCol = 0
+ If Column &gt;= 1 Then
+ lMaxCol = .Count
+ If Column &gt; lMaxCol Then lCol = lMaxCol Else lCol = Column
+ Else
+ lCol = .getCurrentColumnPosition() + 1
+ End If
+ End With
+ End If
+
+ &apos; The _TabControllerModel acts exactly as a result set, from which the generic _GetColumnValue can extract the searched value
+ If lCol &gt;= 1 Then vValue = _ParentDatabase._GetColumnValue(_TabControllerModel, lCol)
+
+Finally:
+ GetValue = vValue
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.GetValue
+
+REM -----------------------------------------------------------------------------
+Public Function GoToCell(Optional ByVal Row As Variant _
+ , Optional ByVal Column As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set the cursor on the given row and the given column.
+&apos;&apos;&apos; If the requested row exceeds the number of available rows, the cursor is set on the last row.
+&apos;&apos;&apos; If the requested column exceeds the number of available columns, the selected column is the last one.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Row: the row number (&gt;= 1) as a numeric value. Default= no change
+&apos;&apos;&apos; Column: the name of the column as a string or its position (&gt;= 1). Default = the current column
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.GoToCell(1000000, &quot;ShipCity&quot;)) &apos; Set the cursor on he last row, column &quot;ShipCity&quot;
+
+Dim bGoTo As Boolean &apos; Return value
+Dim lCol As Long &apos; Numeric index of Column in list of columns
+Dim lMaxCol As Long &apos; Index of last column
+Const cstThisSub = &quot;SFDatabases.Datasheet.GoToCell&quot;
+Const cstSubArgs = &quot;[Row=0], [Column=0]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bGoTo = False
+
+Check:
+ If IsMissing(Row) Or IsEmpty(Row) Then Row = 0
+ If IsMissing(Column) Or IsEmpty(Column) Then Column = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Row, &quot;Row&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ If VarType(Column) &lt;&gt; V_STRING Then
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, V_STRING, _ColumnHeaders) Then GoTo Catch
+ End If
+ End If
+
+Try:
+ &apos; Position the row
+ With _TabControllerModel
+ If Row &lt;= 0 Then Row = .Row Else .absolute(Row)
+ &apos; Does Row exceed the total number of rows ?
+ If .IsRowCountFinal And Row &gt; .RowCount Then .absolute(.RowCount)
+ End With
+
+ &apos; Position the column
+ With _ControlView
+ If VarType(Column) = V_STRING Then
+ lCol = ScriptForge.SF_Array.IndexOf(_ColumnHeaders, Column, CaseSensitive := False)
+ Else
+ lCol = -1
+ If Column &gt;= 1 Then
+ lMaxCol = .Count - 1
+ If Column &gt; lMaxCol + 1 Then lCol = lMaxCol Else lCol = Column - 1
+ End If
+ End If
+ If lCol &gt;= 0 Then .setCurrentColumnPosition(lCol)
+ End With
+
+ bGoTo = True
+
+Finally:
+ GoToCell = bGoTo
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.GoToCell
+
+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;Activate&quot; _
+ , &quot;CloseDatasheet&quot; _
+ , &quot;CreateMenu&quot; _
+ , &quot;GetText&quot; _
+ , &quot;GetValue&quot; _
+ , &quot;GoToCell&quot; _
+ , &quot;RemoveMenu&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Datasheet.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Model class as an array
+
+ Properties = Array( _
+ &quot;ColumnHeaders&quot; _
+ , &quot;CurrentColumn&quot; _
+ , &quot;CurrentRow&quot; _
+ , &quot;DatabaseFileName&quot; _
+ , &quot;Filter&quot; _
+ , &quot;LastRow&quot; _
+ , &quot;OrderBy&quot; _
+ , &quot;ParentDatabase&quot; _
+ , &quot;Source&quot; _
+ , &quot;SourceType&quot; _
+ , &quot;XComponent&quot; _
+ , &quot;XControlModel&quot; _
+ , &quot;XTabControllerModel&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Datasheet.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function RemoveMenu(Optional ByVal MenuHeader As Variant) As Boolean
+&apos;&apos;&apos; Remove a menu entry in the document&apos;s menubar
+&apos;&apos;&apos; The removal is not intended to be saved neither in the LibreOffice global environment, nor in the document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; MenuHeader: the name/header of the menu, without tilde &quot;~&quot;, as a case-sensitive string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.RemoveMenu(&quot;File&quot;)
+&apos;&apos;&apos; &apos; ...
+
+Dim bRemove As Boolean &apos; Return value
+Dim oLayout As Object &apos; com.sun.star.comp.framework.LayoutManager
+Dim oMenuBar As Object &apos; com.sun.star.awt.XMenuBar or stardiv.Toolkit.VCLXMenuBar
+Dim sName As String &apos; Menu name
+Dim iMenuId As Integer &apos; Menu identifier
+Dim iMenuPosition As Integer &apos; Menu position &gt;= 0
+Dim i As Integer
+Const cstTilde = &quot;~&quot;
+
+Const cstThisSub = &quot;SFDatabases.Datasheet.RemoveMenu&quot;
+Const cstSubArgs = &quot;MenuHeader&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bRemove = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(MenuHeader, &quot;MenuHeader&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oLayout = _Component.Frame.LayoutManager
+ Set oMenuBar = oLayout.getElement(&quot;private:resource/menubar/menubar&quot;).XMenuBar
+
+ &apos; Search the menu identifier to remove by its name, Mark its position
+ With oMenuBar
+ iMenuPosition = -1
+ For i = 0 To .ItemCount - 1
+ iMenuId = .getItemId(i)
+ sName = Replace(.getItemText(iMenuId), cstTilde, &quot;&quot;)
+ If MenuHeader= sName Then
+ iMenuPosition = i
+ Exit For
+ End If
+ Next i
+ &apos; Remove the found menu item
+ If iMenuPosition &gt;= 0 Then
+ .removeItem(iMenuPosition, 1)
+ bRemove = True
+ End If
+ End With
+
+Finally:
+ RemoveMenu = bRemove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.RemoveMenu
+
+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.Datasheet.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_Datasheet.SetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Toolbars(Optional ByVal ToolbarName As Variant) As Variant
+&apos;&apos;&apos; Returns either a list of the available toolbar names in the actual document
+&apos;&apos;&apos; or a Toolbar object instance.
+&apos;&apos;&apos; [Function identical with SFDocuments.SF_Document.Toolbars()]
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; ToolbarName: the usual name of one of the available toolbars
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-based array of toolbar names when the argument is absent,
+&apos;&apos;&apos; or a new Toolbar object instance from the SF_Widgets library.
+
+Const cstThisSub = &quot;SFDatabases.Datasheet.Toolbars&quot;
+Const cstSubArgs = &quot;[ToolbarName=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(ToolbarName) Or IsEmpty(ToolbarName) Then ToolbarName = &quot;&quot;
+ If IsNull(_Toolbars) Then _Toolbars = ScriptForge.SF_UI._ListToolbars(_Component)
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If VarType(ToolbarName) = V_STRING Then
+ If Len(ToolbarName) &gt; 0 Then
+ If Not ScriptForge.SF_Utils._Validate(ToolbarName, &quot;ToolbarName&quot;, V_STRING, _Toolbars.Keys()) Then GoTo Finally
+ End If
+ Else
+ If Not ScriptForge.SF_Utils._Validate(ToolbarName, &quot;ToolbarName&quot;, V_STRING) Then GoTo Finally &apos; Manage here the VarType error
+ End If
+ End If
+
+Try:
+ If Len(ToolbarName) = 0 Then
+ Toolbars = _Toolbars.Keys()
+ Else
+ Toolbars = CreateScriptService(&quot;SFWidgets.Toolbar&quot;, _Toolbars.Item(ToolbarName))
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Databases.SF_Datasheet.Toolbars
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Sub _Initialize()
+&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
+
+Dim iType As Integer &apos; One of the com.sun.star.sdb.CommandType constants
+Dim oColumn As Object &apos; A single column
+Dim oColumnDescriptor As Object &apos; A single column descriptor
+Dim FSO As Object : Set FSO = ScriptForge.SF_FileSystem
+Dim i As Long
+
+Try:
+ If IsNull([_Parent]) Then _ParentType = &quot;&quot; Else _ParentType = [_Parent].ObjectType
+
+ With _Component
+ &apos; The existence of _Component.Selection must be checked upfront
+ _Command = ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;Command&quot;)
+
+ iType = ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;CommandType&quot;)
+ Select Case iType
+ Case com.sun.star.sdb.CommandType.TABLE : _SheetType = &quot;TABLE&quot;
+ Case com.sun.star.sdb.CommandType.QUERY : _SheetType = &quot;QUERY&quot;
+ Case com.sun.star.sdb.CommandType.COMMAND : _SheetType = &quot;SQL&quot;
+ End Select
+
+ _BaseFileName = ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;DataSourceName&quot;)
+ _DirectSql = Not ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;EscapeProcessing&quot;)
+
+ &apos; Useful UNO objects
+ Set _Frame = .Frame
+ Set _ControlView = .CurrentControl
+ Set _TabControllerModel = .com_sun_star_awt_XTabController_getModel()
+ Set _ControlModel = _ControlView.getModel()
+ End With
+
+ With _TabControllerModel
+ &apos; Retrieve the parent database instance
+ Select Case _ParentType
+ Case &quot;BASE&quot;
+ Set _ParentDatabase = [_Parent].GetDatabase(.User, .Password)
+ Set _ParentBase = [_Parent]
+ Case &quot;DATABASE&quot;
+ Set _ParentDatabase = [_Parent]
+ Set _ParentBase = Nothing
+ Case &quot;&quot; &apos; Derive the DATABASE instance from what can be found in the Component
+ Set _ParentDatabase = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Database&quot; _
+ , FSO._ConvertFromUrl(_BaseFileName), , , .User, .Password)
+ _ParentType = &quot;DATABASE&quot;
+ Set _ParentBase = Nothing
+ End Select
+ &apos; Load column headers
+ _ColumnHeaders = .getColumns().getElementNames()
+ End With
+
+Finally:
+ Exit Sub
+End Sub &apos; SFDatabases.SF_Datasheet._Initialize
+
+REM -----------------------------------------------------------------------------
+Private Function _IsStillAlive(Optional ByVal pbError As Boolean) As Boolean
+&apos;&apos;&apos; Returns True if the datasheet has not been closed manually or incidentally since the last use
+&apos;&apos;&apos; If dead the actual instance is disposed. The execution is cancelled when pbError = True (default)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pbError: if True (default), raise a fatal error
+
+Dim bAlive As Boolean &apos; Return value
+Dim sName As String &apos; Used in error message
+
+ On Local Error GoTo Catch &apos; Anticipate DisposedException errors or alike
+ If IsMissing(pbError) Then pbError = True
+
+Try:
+ &apos; Check existence of datasheet
+ bAlive = Not IsNull(_Component.ComponentWindow)
+
+Finally:
+ If pbError And Not bAlive Then
+ sName = _Command
+ Dispose()
+ If pbError Then ScriptForge.SF_Exception.RaiseFatal(DOCUMENTDEADERROR, sName)
+ End If
+ _IsStillAlive = bAlive
+ Exit Function
+Catch:
+ bAlive = False
+ On Error GoTo 0
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet._IsStillAlive
+
+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 lRow As Long &apos; Actual row number
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ cstThisSub = &quot;SFDatabases.Datasheet.get&quot; &amp; psProperty
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive(False) Then GoTo Finally
+
+ Select Case psProperty
+ Case &quot;ColumnHeaders&quot;
+ _PropertyGet = _ColumnHeaders
+ Case &quot;CurrentColumn&quot;
+ _PropertyGet = _ColumnHeaders(_ControlView.getCurrentColumnPosition())
+ Case &quot;CurrentRow&quot;
+ _PropertyGet = _TabControllerModel.Row
+ Case &quot;DatabaseFileName&quot;
+ _PropertyGet = ScriptForge.SF_FileSystem._ConvertFromUrl(_BaseFileName)
+ Case &quot;Filter&quot;
+ _PropertyGet = _TabControllerModel.Filter
+ Case &quot;LastRow&quot;
+ With _TabControllerModel
+ If .IsRowCountFinal Then
+ _PropertyGet = .RowCount
+ Else
+ lRow = .Row
+ If lRow &gt; 0 Then
+ .last()
+ _PropertyGet = .RowCount
+ .absolute(lRow)
+ Else
+ _PropertyGet = 0
+ End If
+ End If
+ End With
+ Case &quot;OrderBy&quot;
+ _PropertyGet = _TabControllerModel.Order
+ Case &quot;ParentDatabase&quot;
+ Set _PropertyGet = _ParentDatabase
+ Case &quot;Source&quot;
+ _PropertyGet = _Command
+ Case &quot;SourceType&quot;
+ _PropertyGet = _SheetType
+ Case &quot;XComponent&quot;
+ Set _PropertyGet = _Component
+ Case &quot;XControlModel&quot;
+ Set _PropertyGet = _ControlModel
+ Case &quot;XTabControllerModel&quot;
+ Set _PropertyGet = _TabControllerModel
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet._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.Datasheet.set&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+ bSet = True
+ Select Case UCase(psProperty)
+ Case UCase(&quot;Filter&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Filter&quot;, V_STRING) Then GoTo Finally
+ With _TabControllerModel
+ If Len(pvValue) &gt; 0 Then .Filter = _ParentDatabase._ReplaceSquareBrackets(pvValue) Else .Filter = &quot;&quot;
+ .ApplyFilter = ( Len(pvValue) &gt; 0 )
+ .reload()
+ End With
+ Case UCase(&quot;OrderBy&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;OrderBy&quot;, V_STRING) Then GoTo Finally
+ With _TabControllerModel
+ If Len(pvValue) &gt; 0 Then .Order = _ParentDatabase._ReplaceSquareBrackets(pvValue) Else .Order = &quot;&quot;
+ .reload()
+ End With
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ _PropertySet = bSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet._PropertySet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the Datasheet instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[DATASHEET]: tablename,base file url&quot;
+
+ _Repr = &quot;[DATASHEET]: &quot; &amp; _Command &amp; &quot;,&quot; &amp; _BaseFileName
+
+End Function &apos; SFDatabases.SF_Datasheet._Repr
+
+REM ============================================ END OF SFDATABASES.SF_DATASHEET
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdatabases/SF_Register.xba b/wizards/source/sfdatabases/SF_Register.xba
new file mode 100644
index 0000000000..e1b752f7f1
--- /dev/null
+++ b/wizards/source/sfdatabases/SF_Register.xba
@@ -0,0 +1,280 @@
+<?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_Register" 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 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_Register
+&apos;&apos;&apos; ===========
+&apos;&apos;&apos; The ScriptForge framework includes
+&apos;&apos;&apos; the master ScriptForge library
+&apos;&apos;&apos; a number of &quot;associated&quot; libraries SF*
+&apos;&apos;&apos; any user/contributor extension wanting to fit into the framework
+&apos;&apos;&apos;
+&apos;&apos;&apos; The main methods in this module allow the current library to cling to ScriptForge
+&apos;&apos;&apos; - RegisterScriptServices
+&apos;&apos;&apos; Register the list of services implemented by the current library
+&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
+
+Private Const BASEDOCUMENTOPENERROR = &quot;BASEDOCUMENTOPENERROR&quot;
+Private Const DBCONNECTERROR = &quot;DBCONNECTERROR&quot;
+
+REM ============================================================== PUBLIC METHODS
+
+REM -----------------------------------------------------------------------------
+Public Sub RegisterScriptServices() As Variant
+&apos;&apos;&apos; Register into ScriptForge the list of the services implemented by the current library
+&apos;&apos;&apos; Each library pertaining to the framework must implement its own version of this method
+&apos;&apos;&apos;
+&apos;&apos;&apos; It consists in successive calls to the RegisterService() and RegisterEventManager() methods
+&apos;&apos;&apos; with 2 arguments:
+&apos;&apos;&apos; ServiceName: the name of the service as a case-insensitive string
+&apos;&apos;&apos; ServiceReference: the reference as an object
+&apos;&apos;&apos; If the reference refers to a module, then return the module as an object:
+&apos;&apos;&apos; GlobalScope.Library.Module
+&apos;&apos;&apos; If the reference is a class instance, then return a string referring to the method
+&apos;&apos;&apos; containing the New statement creating the instance
+&apos;&apos;&apos; &quot;libraryname.modulename.function&quot;
+
+ With GlobalScope.ScriptForge.SF_Services
+ .RegisterService(&quot;Database&quot;, &quot;SFDatabases.SF_Register._NewDatabase&quot;) &apos; Reference to the function initializing the service
+ .RegisterService(&quot;DatabaseFromDocument&quot;, &quot;SFDatabases.SF_Register._NewDatabaseFromSource&quot;)
+ .RegisterService(&quot;Datasheet&quot;, &quot;SFDatabases.SF_Register._NewDatasheet&quot;)
+ End With
+
+End Sub &apos; SFDatabases.SF_Register.RegisterScriptServices
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Function _NewDatabase(Optional ByVal pvArgs As Variant) As Object
+&apos;&apos;&apos; Create a new instance of the SF_Database class
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName : the name of the file (compliant with the SF_FileSystem.FileNaming notation)
+&apos;&apos;&apos; RegistrationName: mutually exclusive with FileName. Used when database is registered
+&apos;&apos;&apos; ReadOnly : (boolean). Default = True
+&apos;&apos;&apos; User : connection parameters
+&apos;&apos;&apos; Password
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The instance or Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; BASEDOCUMENTOPENERROR The database file could not be opened
+&apos;&apos;&apos; DBCONNECTERROR The database could not be connected, credentials are probably wrong
+
+Dim oDatabase As Object &apos; Return value
+Dim vFileName As Variant &apos; alias of pvArgs(0)
+Dim vRegistration As Variant &apos; Alias of pvArgs(1)
+Dim vReadOnly As Variant &apos; Alias of pvArgs(2)
+Dim vUser As Variant &apos; Alias of pvArgs(3)
+Dim vPassword As Variant &apos; Alias of pvArgs(4)
+Dim oDBContext As Object &apos; com.sun.star.sdb.DatabaseContext
+Const cstService = &quot;SFDatabases.Database&quot;
+Const cstGlobal = &quot;GlobalScope&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
+ If UBound(pvArgs) &gt;= 0 Then vFileName = pvArgs(0) Else vFileName = &quot;&quot;
+ If IsEmpty(vFileName) Then vFileName = &quot;&quot;
+ If UBound(pvArgs) &gt;= 1 Then vRegistration = pvArgs(1) Else vRegistration = &quot;&quot;
+ If IsEmpty(vRegistration) Then vRegistration = &quot;&quot;
+ If UBound(pvArgs) &gt;= 2 Then vReadOnly = pvArgs(2) Else vReadOnly = True
+ If IsEmpty(vReadOnly) Then vReadOnly = True
+ If UBound(pvArgs) &gt;= 3 Then vUser = pvArgs(3) Else vUser = &quot;&quot;
+ If IsEmpty(vUser) Then vUser = &quot;&quot;
+ If UBound(pvArgs) &gt;= 4 Then vPassword = pvArgs(4) Else vPassword = &quot;&quot;
+ If IsEmpty(vPassword) Then vPassword = &quot;&quot;
+ If Not ScriptForge.SF_Utils._Validate(vFileName, &quot;FileName&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(vRegistration, &quot;RegistrationName&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(vReadOnly, &quot;ReadOnly&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(vUser, &quot;User&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(vPassword, &quot;Password&quot;, V_STRING) Then GoTo Finally
+ Set oDatabase = Nothing
+
+ &apos; Check the existence of FileName
+ With ScriptForge
+ Set oDBContext = .SF_Utils._GetUNOService(&quot;DatabaseContext&quot;)
+ If Len(vFileName) = 0 Then &apos; FileName has precedence over RegistrationName
+ If Len(vRegistration) = 0 Then GoTo CatchError
+ If Not oDBContext.hasRegisteredDatabase(vRegistration) Then GoTo CatchError
+ vFileName = .SF_FileSystem._ConvertFromUrl(oDBContext.getDatabaseLocation(vRegistration))
+ End If
+ If Not .SF_FileSystem.FileExists(vFileName) Then GoTo CatchError
+ End With
+
+Try:
+ &apos; Create the database Basic object and initialize attributes
+ Set oDatabase = New SF_Database
+ With oDatabase
+ Set .[Me] = oDatabase
+ ._Location = ConvertToUrl(vFileName)
+ Set ._DataSource = oDBContext.getByName(._Location)
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo CatchConnect
+ Set ._Connection = ._DataSource.getConnection(vUser, vPassword)
+ If IsNull(._Connection) Then GoTo CatchConnect
+ ._User = vUser
+ ._Password = vPassword
+ ._ReadOnly = vReadOnly
+ Set ._MetaData = ._Connection.MetaData
+ ._URL = ._MetaData.URL
+ End With
+
+Finally:
+ Set _NewDatabase = oDatabase
+ Exit Function
+Catch:
+ GoTo Finally
+CatchError:
+ ScriptForge.SF_Exception.RaiseFatal(BASEDOCUMENTOPENERROR, &quot;FileName&quot;, vFileName, &quot;RegistrationName&quot;, vRegistration)
+ GoTo Finally
+CatchConnect:
+ ScriptForge.SF_Exception.RaiseFatal(DBCONNECTERROR, &quot;User&quot;, vUser, &quot;Password&quot;, vPassword, vFileName)
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Register._NewDatabase
+
+REM -----------------------------------------------------------------------------
+Public Function _NewDatabaseFromSource(Optional ByVal pvArgs As Variant) As Object
+&apos; ByRef oDataSource As Object _
+&apos; , ByVal sUser As String _
+&apos; , ByVal sPassword As String _
+&apos; ) As Object
+&apos;&apos;&apos; Create a new instance of the SF_Database class from the given datasource
+&apos;&apos;&apos; established in the SFDocuments.Base service
+&apos;&apos;&apos; THIS SERVICE MUST NOT BE CALLED FROM A USER SCRIPT
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; oDataSource: com.sun.star.sdbc.XDataSource
+&apos;&apos;&apos; sUser, sPassword : connection parameters
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The instance or Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; managed in the calling routines when Nothing is returned
+
+Dim oDatabase As Object &apos; Return value
+Dim oConnection As Object &apos; com.sun.star.sdbc.XConnection
+Dim oDataSource As Object &apos; Alias of pvArgs(0)
+Dim sUser As String &apos; Alias of pvArgs(1)
+Dim sPassword As String &apos; Alias of pvArgs(2)
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oDatabase = Nothing
+
+Try:
+ &apos; Get arguments
+ Set oDataSource = pvArgs(0)
+ sUser = pvArgs(1)
+ sPassword = pvArgs(2)
+
+ &apos; Setup the connection
+ If oDataSource.IsPasswordRequired Then
+ Set oConnection = oDataSource.getConnection(sUser, sPassword)
+ Else
+ Set oConnection = oDataSource.getConnection(&quot;&quot;, &quot;&quot;)
+ End If
+
+ &apos; Create the database Basic object and initialize attributes
+ If Not IsNull(oConnection) Then
+ Set oDatabase = New SF_Database
+ With oDatabase
+ Set .[Me] = oDatabase
+ ._Location = &quot;&quot;
+ Set ._DataSource = oDataSource
+ Set ._Connection = oConnection
+ ._ReadOnly = oConnection.isReadOnly()
+ Set ._MetaData = oConnection.MetaData
+ ._URL = ._MetaData.URL
+ End With
+ End If
+
+Finally:
+ Set _NewDatabaseFromSource = oDatabase
+ Exit Function
+Catch:
+ ScriptForge.SF_Exception.Clear()
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Register._NewDatabaseFromSource
+
+REM -----------------------------------------------------------------------------
+Public Function _NewDatasheet(Optional ByVal pvArgs As Variant) As Object
+&apos; Optional ByRef poComponent As Object _
+&apos; , Optional ByRef poParent As Object _
+&apos; ) As Object
+&apos;&apos;&apos; Create a new instance of the SF_Datasheet class
+&apos;&apos;&apos; Called from
+&apos;&apos;&apos; base.Datasheets()
+&apos;&apos;&apos; base.OpenTable()
+&apos;&apos;&apos; base.OpenQuery()
+&apos;&apos;&apos; database.OpenTable()
+&apos;&apos;&apos; database.OpenQuery()
+&apos;&apos;&apos; database.OpenSql()
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Component: the component of the new datasheet
+&apos;&apos;&apos; com.sun.star.lang.XComponent - org.openoffice.comp.dbu.ODatasourceBrowser
+&apos;&apos;&apos; Parent: the parent SF_Database or SF_Base instance having produced the new datasheet
+&apos;&apos;&apos; When absent, the SF_Database instance will be derived from the component
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The instance or Nothing
+
+Dim oDatasheet As Object &apos; Return value
+Dim oParent As Object &apos; The parent SF_Database or SF_Base instance having produced the new datasheet
+Dim oComponent As Object &apos; The component of the new datasheet
+Dim oWindow As Object &apos; ui.Window user-defined type
+Dim oUi As Object : Set oUi = ScriptForge.SF_Services.CreateScriptService(&quot;ScriptForge.UI&quot;)
+
+Const TABLEDATA = &quot;TableData&quot;
+Const QUERYDATA = &quot;QueryData&quot;
+Const SQLDATA = &quot;SqlData&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oDatasheet = Nothing
+
+Check:
+ &apos; Get, check and assign arguments
+ If Not IsArray(pvArgs) Then GoTo Catch
+ If UBound(pvArgs) &gt;= 0 Then
+ Set oComponent = pvArgs(0)
+ End If
+ If UBound(pvArgs) = 0 Then
+ Set oParent = Nothing
+ ElseIf UBound(pvArgs) = 1 Then
+ Set oParent = pvArgs(1)
+ Else
+ GoTo Catch
+ End If
+
+ &apos; Check the validity of the proposed window: is it really a datasheet ? Otherwise, do nothing
+ If IsNull(oComponent) Then GoTo Catch
+ Set oWindow = oUi._IdentifyWindow(oComponent)
+ With oWindow
+ If .DocumentType &lt;&gt; TABLEDATA And .DocumentType &lt;&gt; QUERYDATA And .DocumentType &lt;&gt; SQLDATA Then GoTo Catch
+ End With
+ If IsEmpty(oComponent.Selection) Then GoTo Catch
+
+Try:
+ Set oDatasheet = New SF_Datasheet
+ With oDatasheet
+ Set .[Me] = oDatasheet
+ Set .[_Parent] = oParent
+ Set ._Component = oComponent
+ &apos; Achieve the initialization
+ ._Initialize()
+ End With
+
+Finally:
+ Set _NewDatasheet = oDatasheet
+ Exit Function
+Catch:
+ Set oDatasheet = Nothing
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Register._NewDatasheet
+
+REM ============================================== END OF SFDATABASES.SF_REGISTER
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdatabases/__License.xba b/wizards/source/sfdatabases/__License.xba
new file mode 100644
index 0000000000..3b0c64d04a
--- /dev/null
+++ b/wizards/source/sfdatabases/__License.xba
@@ -0,0 +1,26 @@
+<?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="__License" script:language="StarBasic" script:moduleType="normal">
+&apos;&apos;&apos; Copyright 2019-2022 Jean-Pierre LEDURE, Rafael LIMA, Alain ROMEDENNE
+
+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 =======================================================================================================================
+
+&apos;&apos;&apos; ScriptForge is distributed in the hope that it will be useful,
+&apos;&apos;&apos; but WITHOUT ANY WARRANTY; without even the implied warranty of
+&apos;&apos;&apos; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+&apos;&apos;&apos; ScriptForge is free software; you can redistribute it and/or modify it under the terms of either (at your option):
+
+&apos;&apos;&apos; 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not
+&apos;&apos;&apos; distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ .
+
+&apos;&apos;&apos; 2) The GNU Lesser General Public License as published by
+&apos;&apos;&apos; the Free Software Foundation, either version 3 of the License, or
+&apos;&apos;&apos; (at your option) any later version. If a copy of the LGPL was not
+&apos;&apos;&apos; distributed with this file, see http://www.gnu.org/licenses/ .
+
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdatabases/dialog.xlb b/wizards/source/sfdatabases/dialog.xlb
new file mode 100644
index 0000000000..8b62d721a8
--- /dev/null
+++ b/wizards/source/sfdatabases/dialog.xlb
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
+<library:library xmlns:library="http://openoffice.org/2000/library" library:name="SFDatabases" library:readonly="false" library:passwordprotected="false"/> \ No newline at end of file
diff --git a/wizards/source/sfdatabases/script.xlb b/wizards/source/sfdatabases/script.xlb
new file mode 100644
index 0000000000..8e12f56515
--- /dev/null
+++ b/wizards/source/sfdatabases/script.xlb
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
+<library:library xmlns:library="http://openoffice.org/2000/library" library:name="SFDatabases" library:readonly="false" library:passwordprotected="false">
+ <library:element library:name="SF_Register"/>
+ <library:element library:name="__License"/>
+ <library:element library:name="SF_Database"/>
+ <library:element library:name="SF_Datasheet"/>
+ <library:element library:name="SF_Dataset"/>
+</library:library> \ No newline at end of file