ScriptForge (SFDatabases) new Dataset service

A dataset represents a set of tabular data
stored/produced by a database.
To use datasets, the database instance must exist
but the Base document may not be open.

A Dataset instance is create either with
- the (new) database.CreateDataset() method
- from an existing dataset with the (new)
  dataset.CreateDataset() method.

The proposed API supports next main purposes:
- browse for- and backward thru the dataset to get its content
- update any record with new values
- create new records or delete some.
In summary, the AKA "CRUD" operations
(create, read, update, delete).

The originality of the proposed API is the use
of a dense syntax to make insertions and updates
easy and readable:
Example:
(BASIC)
  Dim newID As Long
  newID = dataset.Insert("LastName", "Doe", "FirstName", "John")
  '     ... is equivalent to:
  Dim dict As Object, newID As Long
  Set dict = CreateScriptService("ScriptForge.Dictionary")
  dict.Add("LastName", "Doe")
  dict.Add("FirstName", "John")
  newID = dataset.Insert(dict)
(PYTHON) - next statements are equivalent
  newid = dataset.Insert('LastName', 'Doe', 'FirstName', 'John')
  newid = dataset.Insert({'LastName': 'Doe', 'FirstName': 'John'})
  newid = dataset.Insert(dict(LastName = 'Doe', FirstName = 'John'))
  newid = dataset.Insert(LastName = 'Doe', FirstName = 'John')

You will notice that the returned value is the AutoValue primery
key (when it exists) which makes it reuse as a foreign key
immediate.

The API is fully available both in Basic and Python user scripts.

The new service will require its inclusion in the user documentation.

Change-Id: I4f834c4234e5b96ec8fddfffbad791ecf31899df
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159325
Reviewed-by: Jean-Pierre Ledure <jp@ledure.be>
Tested-by: Jenkins
This commit is contained in:
Jean-Pierre Ledure 2023-11-11 17:02:38 +01:00
parent c45779ce3d
commit 7b2a6f0444
11 changed files with 2066 additions and 44 deletions

View file

@ -21,6 +21,7 @@ $(eval $(call gb_Package_Package,wizards_basicsrvsfdatabases,$(SRCDIR)/wizards/s
$(eval $(call gb_Package_add_files,wizards_basicsrvsfdatabases,$(LIBO_SHARE_FOLDER)/basic/SFDatabases,\
SF_Database.xba \
SF_Dataset.xba \
SF_Datasheet.xba \
SF_Register.xba \
__License.xba \

View file

@ -132,6 +132,10 @@ Const DUPLICATECONTROLERROR = &quot;DUPLICATECONTROLERROR&quot;
&apos; SF_Database
Const DBREADONLYERROR = &quot;DBREADONLYERROR&quot;
Const SQLSYNTAXERROR = &quot;SQLSYNTAXERROR&quot;
Const SQLSYNTAX2ERROR = &quot;SQLSYNTAX2ERROR&quot;
Const NOCURRENTRECORDERROR = &quot;NOCURRENTRECORDERROR&quot;
Const RECORDUPDATEERROR = &quot;RECORDUPDATEERROR&quot;
Const FIELDEXPORTERROR = &quot;FIELDEXPORTERROR&quot;
&apos; Python
Const PYTHONSHELLERROR = &quot;PYTHONSHELLERROR&quot;
@ -1035,12 +1039,25 @@ Try:
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;VALIDATEERROR&quot;, pvArgs(0)) _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;DUPLICATECONTROL&quot;, pvArgs(0), pvArgs(1), pvArgs(2))
Case DBREADONLYERROR &apos; SF_Database.RunSql()
Case DBREADONLYERROR &apos; SF_Database.RunSql(), SF_Dataset.Delete(), Insert(), Update()
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;DBREADONLY&quot;, vLocation(2))
Case SQLSYNTAXERROR &apos; SF_Database._ExecuteSql(SQL)
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;SQLSYNTAX&quot;, pvArgs(0))
Case SQLSYNTAX2ERROR &apos; SF_Dataset.Reload/_Initialize(SQL, Filter, OrderBy)
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;SQLSYNTAX2&quot;, pvArgs(0), pvArgs(1), pvArgs(2))
Case NOCURRENTRECORDERROR &apos; SF_Dataset.Insert/Update/GetValue/Delete
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;NOCURRENTRECORD&quot;)
Case RECORDUPDATEERROR &apos; SF_Dataset.Insert/Update(FieldName, FieldValue, FieldType)
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;RECORDUPDATE&quot;, pvArgs(0), pvArgs(1), pvARgs(2))
Case FIELDEXPORTERROR &apos; SF_Dataset.ExportFieldToFile(Arg1Name, FileName, Arg2, Overwrite)
pvArgs(0) = _RightCase(pvArgs(0)) : pvArgs(2) = _RightCase(pvArgs(2))
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;FIELDEXPORT&quot;, pvArgs(0), pvArgs(1), pvArgs(2), pvArgs(3))
Case PYTHONSHELLERROR &apos; SF_Exception.PythonShell (Python only)
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;PYTHONSHELL&quot;)

View file

@ -776,6 +776,8 @@ Try:
Select Case sServiceName
Case &quot;SFDatabases.Database&quot;
If Script = &quot;GetRows&quot; Then vReturn = vBasicObject.GetRows(vArgs(0), vArgs(1), vArgs(2), vArgs(3))
Case &quot;SFDatabases.Dataset&quot;
If Script = &quot;GetRows&quot; Then vReturn = vBasicObject.GetRows(vArgs(0), vArgs(1))
Case &quot;SFDialogs.Dialog&quot;
If Script = &quot;Controls&quot; Then vReturn = vBasicObject.Controls(vArgs(0))
Case &quot;SFDialogs.DialogControl&quot;
@ -945,7 +947,7 @@ Try:
vReturnArray(1) = V_OBJECT
Select Case True
Case bUno : vReturnArray(2) = objUNO
Case bDICT : vReturnArray(2) = objDICT
Case bDict : vReturnArray(2) = objDICT
Case bBasicClass : vReturnArray(2) = objCLASS
Case Else : vReturnArray(2) = objMODULE
End Select

View file

@ -1028,7 +1028,7 @@ Try:
&amp; &quot;%2: A string\n&quot; _
&amp; &quot;%3: A dialog name&quot; _
)
&apos; SF_Database.RunSql
&apos; SF_Database.RunSql, SF_Dataset.Delete/Insert/Update
.AddText( Context := &quot;DBREADONLY&quot; _
, MsgId := &quot;The database has been opened in read-only mode.\n&quot; _
&amp; &quot;The &apos;%1&apos; method must not be executed in this context.&quot; _
@ -1043,6 +1043,53 @@ Try:
, Comment := &quot;SF_Database can&apos;t interpret SQL statement\n&quot; _
&amp; &quot;%1: The statement&quot; _
)
&apos; SF_Dataset.Reload/_Initialize
.AddText( Context := &quot;SQLSYNTAX2&quot; _
, MsgId := &quot;An SQL statement could not be interpreted or executed by the database system.\n&quot; _
&amp; &quot;Check its syntax, table and/or field names, ...\n\n&quot; _
&amp; &quot;SQL Statement : « %1 »\n&quot; _
&amp; &quot;combined with\n&quot; _
&amp; &quot; « %2 »\n&quot; _
&amp; &quot; « %3 »&quot; _
, Comment := &quot;SF_Database can&apos;t interpret SQL statement\n&quot; _
&amp; &quot;%1: The statement\n&quot; _
&amp; &quot;%2: a WHERE clause\n&quot; _
&amp; &quot;%3: a ORDER BY clause&quot; _
)
&apos; SF_Dataset.Update/Insert/Delete/GetValue
.AddText( Context := &quot;NOCURRENTRECORD&quot; _
, MsgId := &quot;A database record could not be retrieved, inserted or updated by the database system.\n&quot; _
&amp; &quot;The current record could not be determined.\n&quot; _
, Comment := &quot;SF_Dataset can&apos;t read field values or store field updates&quot; _
)
&apos; SF_Dataset._SetColumnValue
.AddText( Context := &quot;RECORDUPDATE&quot; _
, MsgId := &quot;A database record could not be inserted or updated by the database system.\n&quot; _
&amp; &quot;Possible reasons:\n&quot; _
&amp; &quot;- the field is not updatable\n&quot; _
&amp; &quot;- a [NULL] value is provided which is forbidden for the field\n&quot; _
&amp; &quot;- the type of value and the type of field are incompatible\n&quot; _
&amp; &quot;- the input binary file does not exist or is empty\n&quot; _
&amp; &quot;- the field type is not supported\n\n&quot; _
&amp; &quot;Field name : « %1 »\n&quot; _
&amp; &quot;Field value : « %2 »\n&quot; _
&amp; &quot;Field type : « %3 »&quot; _
, Comment := &quot;SF_Database can&apos;t store field updates\n&quot; _
&amp; &quot;%1: The field name\n&quot; _
&amp; &quot;%2: the value to store in the field&quot; _
)
&apos; SF_Dataset.ExportFieldToFile
.AddText( Context := &quot;FIELDEXPORT&quot; _
, MsgId := &quot;The database field could not be exported.\n&quot; _
&amp; &quot;Either the destination file must not be overwritten, or it has a read-only attribute set.\n\n&quot; _
&amp; &quot;%1 = &apos;%2&apos;\n&quot; _
&amp; &quot;%3 = %4&quot; _
, Comment := &quot;SF_Dataset.ExportToFile error message\n&quot; _
&amp; &quot;%1: An identifier\n&quot; _
&amp; &quot;%2: A file name\n&quot; _
&amp; &quot;%3: An identifier\n&quot; _
&amp; &quot;%4: True or False\n&quot; _
)
&apos; SF_Exception.PythonShell (Python only)
.AddText( Context := &quot;PYTHONSHELL&quot; _
, MsgId := &quot;The APSO extension could not be located in your LibreOffice installation.&quot; _

View file

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.libreoffice.org/enter_bug.cgi?product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
"POT-Creation-Date: 2023-09-03 13:05:04\n"
"POT-Creation-Date: 2023-11-11 15:24:14\n"
"PO-Revision-Date: YYYY-MM-DD HH:MM:SS\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
@ -990,6 +990,69 @@ msgid ""
"SQL Statement : « %1 »"
msgstr ""
#. SF_Database can't interpret SQL statement
#. %1: The statement
#. %2: a WHERE clause
#. %3: a ORDER BY clause
#, kde-format
msgctxt "SQLSYNTAX2"
msgid ""
"An SQL statement could not be interpreted or executed by the "
"database system.\n"
"Check its syntax, table and/or field names, ...\n"
"\n"
"SQL Statement : « %1 »\n"
"combined with\n"
" « %2 »\n"
" « %3 »"
msgstr ""
#. SF_Dataset can't read field values or store field updates
msgctxt "NOCURRENTRECORD"
msgid ""
"A database record could not be retrieved, inserted or updated by the "
"database system.\n"
"The current record could not be determined.\n"
""
msgstr ""
#. SF_Database can't store field updates
#. %1: The field name
#. %2: the value to store in the field
#, kde-format
msgctxt "RECORDUPDATE"
msgid ""
"A database record could not be inserted or updated by the database "
"system.\n"
"Possible reasons:\n"
"- the field is not updatable\n"
"- a [NULL] value is provided which is forbidden for the field\n"
"- the type of value and the type of field are incompatible\n"
"- the input binary file does not exist or is empty\n"
"- the field type is not supported\n"
"\n"
"Field name : « %1 »\n"
"Field value : « %2 »\n"
"Field type : « %3 »"
msgstr ""
#. SF_Dataset.ExportToFile error message
#. %1: An identifier
#. %2: A file name
#. %3: An identifier
#. %4: True or False
#.
#, kde-format
msgctxt "FIELDEXPORT"
msgid ""
"The database field could not be exported.\n"
"Either the destination file must not be overwritten, or it has a "
"read-only attribute set.\n"
"\n"
"%1 = '%2'\n"
"%3 = %4"
msgstr ""
#. SF_Exception.PythonShell error messageAPSO: to leave unchanged
msgctxt "PYTHONSHELL"
msgid ""

View file

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.libreoffice.org/enter_bug.cgi?product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
"POT-Creation-Date: 2023-09-03 13:05:04\n"
"POT-Creation-Date: 2023-11-11 15:24:14\n"
"PO-Revision-Date: YYYY-MM-DD HH:MM:SS\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
@ -990,6 +990,69 @@ msgid ""
"SQL Statement : « %1 »"
msgstr ""
#. SF_Database can't interpret SQL statement
#. %1: The statement
#. %2: a WHERE clause
#. %3: a ORDER BY clause
#, kde-format
msgctxt "SQLSYNTAX2"
msgid ""
"An SQL statement could not be interpreted or executed by the "
"database system.\n"
"Check its syntax, table and/or field names, ...\n"
"\n"
"SQL Statement : « %1 »\n"
"combined with\n"
" « %2 »\n"
" « %3 »"
msgstr ""
#. SF_Dataset can't read field values or store field updates
msgctxt "NOCURRENTRECORD"
msgid ""
"A database record could not be retrieved, inserted or updated by the "
"database system.\n"
"The current record could not be determined.\n"
""
msgstr ""
#. SF_Database can't store field updates
#. %1: The field name
#. %2: the value to store in the field
#, kde-format
msgctxt "RECORDUPDATE"
msgid ""
"A database record could not be inserted or updated by the database "
"system.\n"
"Possible reasons:\n"
"- the field is not updatable\n"
"- a [NULL] value is provided which is forbidden for the field\n"
"- the type of value and the type of field are incompatible\n"
"- the input binary file does not exist or is empty\n"
"- the field type is not supported\n"
"\n"
"Field name : « %1 »\n"
"Field value : « %2 »\n"
"Field type : « %3 »"
msgstr ""
#. SF_Dataset.ExportToFile error message
#. %1: An identifier
#. %2: A file name
#. %3: An identifier
#. %4: True or False
#.
#, kde-format
msgctxt "FIELDEXPORT"
msgid ""
"The database field could not be exported.\n"
"Either the destination file must not be overwritten, or it has a "
"read-only attribute set.\n"
"\n"
"%1 = '%2'\n"
"%3 = %4"
msgstr ""
#. SF_Exception.PythonShell error messageAPSO: to leave unchanged
msgctxt "PYTHONSHELL"
msgid ""

View file

@ -29,7 +29,7 @@
program document macros with much less hassle and get quicker results.
The use of the ScriptForge interfaces in user scripts hides the complexity of the usual UNO interfaces.
However it does not replace them. At the opposite their coexistence is ensured.
However, it does not replace them. At the opposite their coexistence is ensured.
Indeed, ScriptForge provides a number of shortcuts to key UNO objects.
The scriptforge.py module
@ -216,7 +216,7 @@ class ScriptForge(object, metaclass = _Singleton):
"""
def ParseScript(_script):
# Check ParamArray arguments
# Check ParamArray, scope, script to run, arguments
_paramarray = False
if _script[0] == '@':
_script = _script[1:]
@ -241,7 +241,7 @@ class ScriptForge(object, metaclass = _Singleton):
lib = cls.library + '.' # Default library = ScriptForge
uri = 'vnd.sun.star.script:{0}{1}?language=Basic&location={2}'.format(lib, _script, scope)
# Get the script object
_fullscript = ('@' if _paramarray else '') + scope + ':' + _script
_fullscript = ('@' if _paramarray else '') + scope + '#' + _script
try:
_xscript = cls.scriptprovider.getScript(uri) # com.sun.star.script.provider.XScript
except Exception:
@ -1784,6 +1784,9 @@ class SFDatabases:
def CloseDatabase(self):
return self.ExecMethod(self.vbMethod, 'CloseDatabase')
def CreateDataset(self, sqlcommand, directsql = False, filter = '', orderby = ''):
return self.ExecMethod(self.vbMethod, 'CreateDataset', sqlcommand, directsql, filter, orderby)
def DAvg(self, expression, tablename, criteria = ''):
return self.ExecMethod(self.vbMethod, 'DAvg', expression, tablename, criteria)
@ -1820,6 +1823,85 @@ class SFDatabases:
def RunSql(self, sqlcommand, directsql = False):
return self.ExecMethod(self.vbMethod, 'RunSql', sqlcommand, directsql)
# #########################################################################
# SF_Dataset CLASS
# #########################################################################
class SF_Dataset(SFServices):
"""
A dataset represents a set of tabular data produced by a database.
In the user interface of LibreOffice a dataset corresponds with the data
displayed in a form, a data sheet (table, query).
To use datasets, the database instance must exist but the Base document may not be open.
"""
# Mandatory class properties for service registration
serviceimplementation = 'basic'
servicename = 'SFDatabases.Dataset'
servicesynonyms = () # CreateScriptService is not applicable here
serviceproperties = dict(BOF = True, DefaultValues = False, EOF = True, Fields = False, Filter = False,
OrderBy = False, ParentDatabase = False, RowCount = False, RowNumber = False,
Source = False, SourceType = False, UpdatableFields = False, Values = False,
XRowSet = False)
forceGetProperty = True
@classmethod
def _dictargs(cls, args, kwargs):
"""
Convert a set of keyword arguments to a dictionary to pass to the Basic world
"""
if len(args) == 0 and len(kwargs) > 0:
return kwargs
if len(args) > 0:
if len(kwargs) == 0:
if isinstance(args[0], dict):
return args[0]
return {args[i]: args[i + 1] for i in range(0, len(args), 2)}
return None
def CloseDataset(self):
return self.ExecMethod(self.vbMethod, 'CloseDataset')
def CreateDataset(self, filter = ScriptForge.cstSymMissing, orderby = ScriptForge.cstSymMissing):
return self.ExecMethod(self.vbMethod, 'CreateDataset', filter, orderby)
def Delete(self):
return self.ExecMethod(self.vbMethod, 'Delete')
def ExportValueToFile(self, fieldname, filename, overwrite = False):
return self.ExecMethod(self.vbMethod, 'ExportValueToFile', fieldname, filename, overwrite)
def GetRows(self, header = False, maxrows = 0):
return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'GetRows', header, maxrows)
def GetValue(self, fieldname):
return self.ExecMethod(self.vbMethod, 'GetValue', fieldname)
def Insert(self, *args, **kwargs):
updateslist = self._dictargs(args, kwargs)
if updateslist is None:
return -1 # The insertion could not be done
return self.ExecMethod(self.vbMethod + self.flgDictArg, 'Insert', updateslist)
def MoveFirst(self):
return self.ExecMethod(self.vbMethod, 'MoveFirst')
def MoveLast(self):
return self.ExecMethod(self.vbMethod, 'MoveLast')
def MoveNext(self, offset = 1):
return self.ExecMethod(self.vbMethod, 'MoveNext', offset)
def MovePrevious(self, offset = 1):
return self.ExecMethod(self.vbMethod, 'MovePrevious', offset)
def Reload(self, filter = ScriptForge.cstSymMissing, orderby = ScriptForge.cstSymMissing):
return self.ExecMethod(self.vbMethod, 'Reload', filter, orderby)
def Update(self, *args, **kwargs):
updateslist = self._dictargs(args, kwargs)
if updateslist is None:
return False # The update could not be done
return self.ExecMethod(self.vbMethod + self.flgDictArg, 'Update', updateslist)
# #########################################################################
# SF_Datasheet CLASS
# #########################################################################

View file

@ -53,6 +53,7 @@ REM ================================================================== EXCEPTION
Private Const DBREADONLYERROR = &quot;DBREADONLYERROR&quot;
Private Const SQLSYNTAXERROR = &quot;SQLSYNTAXERROR&quot;
Private Const SQLSYNTAX2ERROR = &quot;SQLSYNTAX2ERROR&quot;
REM ============================================================= PRIVATE MEMBERS
@ -153,6 +154,105 @@ Finally:
Exit Sub
End Sub
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 srrounded
&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 _
@ -354,7 +454,7 @@ Try:
With oResult
&apos;Initialize output array with header row
Set oColumns = oResult.getColumns()
Set oColumns = .getColumns()
lCols = oColumns.Count - 1
If Header Then
lRows = 0
@ -397,6 +497,7 @@ Public Function Methods() As Variant
Methods = Array( _
&quot;CloseDatabase&quot; _
, &quot;CreateDataset&quot; _
, &quot;DAvg&quot; _
, &quot;DCount&quot; _
, &quot;DLookup&quot; _
@ -803,9 +904,7 @@ Try:
&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 Not oResult.first() Then Goto Finally
If oResult.isAfterLast() Then GoTo Finally
vResult = _GetColumnValue(oResult, 1, True) &apos; Force return of binary field
If oResult.first() Then vResult = _GetColumnValue(oResult, 1) Else GoTo Finally
End If
Set oResult = Nothing
@ -861,6 +960,7 @@ Finally:
Set oStatement = Nothing
Exit Function
Catch_Sql:
On Local Error GoTo 0
ScriptForge.SF_Exception.RaiseFatal(SQLSYNTAXERROR, sSql)
GoTo Finally
Catch:
@ -870,20 +970,17 @@ End Function &apos; SFDatabases.SF_Database._ExecuteSql
REM -----------------------------------------------------------------------------
Private Function _GetColumnValue(ByRef poResultSet As Object _
, ByVal plColIndex As Long _
, Optional ByVal pbReturnBinary As Boolean _
) 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; pbReturnBinary: when True, the method returns the content of a binary field,
&apos;&apos;&apos; as long as its length does not exceed a maximum length.
&apos;&apos;&apos; Default = False: binary fields are not returned, only their length
&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
@ -897,7 +994,6 @@ Const cstMaxBinlength = 2 * 65535
On Local Error Goto 0 &apos; Disable error handler
vValue = Empty &apos; Default value if error
If IsMissing(pbReturnBinary) Then pbReturnBinary = False
With com.sun.star.sdbc.DataType
lType = poResultSet.MetaData.getColumnType(plColIndex)
@ -908,20 +1004,11 @@ Const cstMaxBinlength = 2 * 65535
Case .BINARY, .VARBINARY, .LONGVARBINARY, .BLOB
Set oStream = poResultSet.getBinaryStream(plColIndex)
If bNullable Then
If Not poResultSet.wasNull() Then
If Not ScriptForge.SF_Session.HasUNOMethod(oStream, &quot;getLength&quot;) Then &apos; When no recordset
lSize = cstMaxBinLength
Else
lSize = CLng(oStream.getLength())
End If
If lSize &lt;= cstMaxBinLength And pbReturnBinary Then
vValue = Array()
oStream.readBytes(vValue, lSize)
Else &apos; Return length of field, not content
vValue = lSize
End If
End If
If Not poResultSet.wasNull() Then lSize = CLng(oStream.getLength()) Else lSize = 0
Else
lSize = CLng(oStream.getLength())
End If
vValue = lSize &apos; Return length of field, not content
If Not IsNull(oStream) Then oStream.closeInput()
Case .BIT, .BOOLEAN : vValue = poResultSet.getBoolean(plColIndex)
Case .DATE
@ -935,16 +1022,10 @@ Const cstMaxBinlength = 2 * 65535
Case .BIGINT : vValue = CLng(poResultSet.getLong(plColIndex))
Case .DECIMAL, .NUMERIC : vValue = poResultSet.getDouble(plColIndex)
Case .SQLNULL : vValue = poResultSet.getNull(plColIndex)
Case .OBJECT, .OTHER, .STRUCT : vValue = Null
Case .REF : vValue = poResultSet.getRef(plColIndex)
Case .TINYINT : vValue = poResultSet.getShort(plColIndex)
Case .CHAR, .VARCHAR : vValue = poResultSet.getString(plColIndex)
Case .LONGVARCHAR, .CLOB
If bNullable Then
If Not poResultSet.wasNull() Then vValue = poResultSet.getString(plColIndex)
Else
vValue = &quot;&quot;
End If
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)
@ -956,6 +1037,7 @@ Const cstMaxBinlength = 2 * 65535
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
@ -963,7 +1045,7 @@ Const cstMaxBinlength = 2 * 65535
_GetColumnValue = vValue
End Function &apos; SFDatabases.SF_Database.GetColumnValue
End Function &apos; SFDatabases.SF_Database._GetColumnValue
REM -----------------------------------------------------------------------------
Public Function _OpenDatasheet(Optional ByVal psCommand As Variant _
@ -1088,4 +1170,4 @@ Private Function _Repr() As String
End Function &apos; SFDatabases.SF_Database._Repr
REM ============================================ END OF SFDATABASES.SF_DATABASE
</script:module>
</script:module>

File diff suppressed because it is too large Load diff

View file

@ -668,19 +668,19 @@ Public Function SetProperty(Optional ByVal PropertyName As Variant _
Const cstThisSub = &quot;SFDatabases.Datasheet.SetProperty&quot;
Const cstSubArgs = &quot;PropertyName, Value&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
If ScriptForge.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
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:
SF_Utils._ExitFunction(cstThisSub)
ScriptForge.SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally

View file

@ -5,4 +5,5 @@
<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>