diff options
Diffstat (limited to 'wizards/source/sfdatabases')
-rw-r--r-- | wizards/source/sfdatabases/SF_Database.xba | 825 | ||||
-rw-r--r-- | wizards/source/sfdatabases/SF_Register.xba | 195 | ||||
-rw-r--r-- | wizards/source/sfdatabases/__License.xba | 26 | ||||
-rw-r--r-- | wizards/source/sfdatabases/dialog.xlb | 3 | ||||
-rw-r--r-- | wizards/source/sfdatabases/script.xlb | 7 |
5 files changed, 1056 insertions, 0 deletions
diff --git a/wizards/source/sfdatabases/SF_Database.xba b/wizards/source/sfdatabases/SF_Database.xba new file mode 100644 index 000000000..804084aff --- /dev/null +++ b/wizards/source/sfdatabases/SF_Database.xba @@ -0,0 +1,825 @@ +<?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 + +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +''' SF_Database +''' ========= +''' Management of databases embedded in or related to Base documents +''' Each instance of the current class represents a single database, with essentially its tables, queries and data +''' +''' The exchanges with the database are done in SQL only. +''' To make them more readable, use optionally square brackets to surround table/query/field names +''' instead of the (RDBMS-dependent) normal surrounding character (usually, double-quote, single-quote or other). +''' SQL statements may be run in direct or indirect mode. In direct mode the statement is transferred literally +''' without syntax checking nor review to the database system. +''' +''' The provided interfaces include simple tables, queries and fields lists, and access to database metadata. +''' +''' Service invocation and usage: +''' 1) To access any database at anytime +''' Dim myDatabase As Object +''' Set myDatabase = CreateScriptService("SFDatabases.Database", FileName, , [ReadOnly], [User, [Password]]) +''' ' Args: +''' ' FileName: the name of the Base file compliant with the SF_FileSystem.FileNaming notation +''' ' RegistrationName: the name of a registered database (mutually exclusive with FileName) +''' ' ReadOnly: Default = True +''' ' User, Password: additional connection arguments to the database server +''' ' ... Run queries, SQL statements, ... +''' myDatabase.CloseDatabase() +''' +''' 2) To access the database related to the current Base document +''' Dim myDoc As Object, myDatabase As Object, ui As Object +''' Set ui = CreateScriptService("UI") +''' Set myDoc = ui.OpenBaseDocument("myDb.odb") +''' Set myDatabase = myDoc.GetDatabase() ' user and password are supplied here, if needed +''' ' ... Run queries, SQL statements, ... +''' myDoc.CloseDocument() +''' +''' Detailed user documentation: +''' https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_database.html?DbPAR=BASIC +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +REM ================================================================== EXCEPTIONS + +Private Const DBREADONLYERROR = "DBREADONLYERROR" +Private Const SQLSYNTAXERROR = "SQLSYNTAXERROR" + +REM ============================================================= PRIVATE MEMBERS + +Private [Me] As Object +Private [_Parent] As Object +Private ObjectType As String ' Must be DATABASE +Private ServiceName As String +Private _DataSource As Object ' com.sun.star.comp.dba.ODatabaseSource +Private _Connection As Object ' com.sun.star.sdbc.XConnection +Private _URL As String ' Text on status bar +Private _Location As String ' File name +Private _ReadOnly As Boolean +Private _MetaData As Object ' com.sun.star.sdbc.XDatabaseMetaData + +REM ============================================================ MODULE CONSTANTS + +REM ===================================================== CONSTRUCTOR/DESTRUCTOR + +REM ----------------------------------------------------------------------------- +Private Sub Class_Initialize() + Set [Me] = Nothing + Set [_Parent] = Nothing + ObjectType = "DATABASE" + ServiceName = "SFDatabases.Database" + Set _DataSource = Nothing + Set _Connection = Nothing + _URL = "" + _Location = "" + _ReadOnly = True + Set _MetaData = Nothing +End Sub ' SFDatabases.SF_Database Constructor + +REM ----------------------------------------------------------------------------- +Private Sub Class_Terminate() + Call Class_Initialize() +End Sub ' SFDatabases.SF_Database Destructor + +REM ----------------------------------------------------------------------------- +Public Function Dispose() As Variant + Call Class_Terminate() + Set Dispose = Nothing +End Function ' SFDatabases.SF_Database Explicit Destructor + +REM ================================================================== PROPERTIES + +REM ----------------------------------------------------------------------------- +Property Get Queries() As Variant +''' Return the list of available queries in the database + Queries = _PropertyGet("Queries") +End Property ' SFDatabases.SF_Database.Queries (get) + +REM ----------------------------------------------------------------------------- +Property Get Tables() As Variant +''' Return the list of available Tables in the database + Tables = _PropertyGet("Tables") +End Property ' SFDatabases.SF_Database.Tables (get) + +REM ----------------------------------------------------------------------------- +Property Get XConnection() As Variant +''' Return a com.sun.star.sdbc.XConnection UNO object + XConnection = _PropertyGet("XConnection") +End Property ' SFDatabases.SF_Database.XConnection (get) + +REM ----------------------------------------------------------------------------- +Property Get XMetaData() As Variant +''' Return a com.sun.star.sdbc.XDatabaseMetaData UNO object + XMetaData = _PropertyGet("XMetaData") +End Property ' SFDatabases.SF_Database.XMetaData (get) + +REM ===================================================================== METHODS + +REM ----------------------------------------------------------------------------- +Public Sub CloseDatabase() +''' Close the current database connection + +Const cstThisSub = "SFDatabases.Database.CloseDatabase" +Const cstSubArgs = "" + + On Local Error GoTo 0 ' Disable useless error checking + +Check: + ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + With _Connection + If Not IsNull(_Connection) Then + If ScriptForge.SF_Session.HasUnoMethod(_Connection, "flush") Then .flush() + .close() + .dispose() + End If + Dispose() + End With + +Finally: + ScriptForge.SF_Utils._ExitFunction(cstThisSub) + Exit Sub +End Sub + +REM ----------------------------------------------------------------------------- +Public Function DAvg(Optional ByVal Expression As Variant _ + , Optional ByVal TableName As Variant _ + , Optional ByVal Criteria As Variant _ + ) As Variant +''' Compute the aggregate function AVG() on a field or expression belonging to a table +''' filtered by a WHERE-clause. +''' Args: +''' Expression: an SQL expression +''' TableName: the name of a table +''' Criteria: an optional WHERE clause without the word WHERE + + DAvg = _DFunction("Avg", Expression, TableName, Criteria) + +End Function ' 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 +''' Compute the aggregate function COUNT() on a field or expression belonging to a table +''' filtered by a WHERE-clause. +''' Args: +''' Expression: an SQL expression +''' TableName: the name of a table +''' Criteria: an optional WHERE clause without the word WHERE + + DCount = _DFunction("Count", Expression, TableName, Criteria) + +End Function ' 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 +''' Compute the aggregate function Lookup() on a field or expression belonging to a table +''' filtered by a WHERE-clause. +''' To order the results, a pvOrderClause may be precised. The 1st record will be retained. +''' Args: +''' Expression: an SQL expression +''' TableName: the name of a table +''' Criteria: an optional WHERE clause without the word WHERE +''' pvOrderClause: an optional order clause incl. "DESC" if relevant + + DLookup = _DFunction("Lookup", Expression, TableName, Criteria, OrderClause) + +End Function ' 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 +''' Compute the aggregate function MAX() on a field or expression belonging to a table +''' filtered by a WHERE-clause. +''' Args: +''' Expression: an SQL expression +''' TableName: the name of a table +''' Criteria: an optional WHERE clause without the word WHERE + + DMax = _DFunction("Max", Expression, TableName, Criteria) + +End Function ' 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 +''' Compute the aggregate function MIN() on a field or expression belonging to a table +''' filtered by a WHERE-clause. +''' Args: +''' Expression: an SQL expression +''' TableName: the name of a table +''' Criteria: an optional WHERE clause without the word WHERE + + DMin = _DFunction("Min", Expression, TableName, Criteria) + +End Function ' 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 +''' Compute the aggregate function Sum() on a field or expression belonging to a table +''' filtered by a WHERE-clause. +''' Args: +''' Expression: an SQL expression +''' TableName: the name of a table +''' Criteria: an optional WHERE clause without the word WHERE + + DSum = _DFunction("Sum", Expression, TableName, Criteria) + +End Function ' SFDatabases.SF_Database.DSum + +REM ----------------------------------------------------------------------------- +Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant +''' Return the actual value of the given property +''' Args: +''' PropertyName: the name of the property as a string +''' Returns: +''' The actual value of the property +''' Exceptions: +''' ARGUMENTERROR The property does not exist +''' Examples: +''' myDatabase.GetProperty("Queries") + +Const cstThisSub = "SFDatabases.Database.GetProperty" +Const cstSubArgs = "" + + 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, "PropertyName", 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 ' 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 +''' Return the content of a table, a query or a SELECT SQL statement as an array +''' Args: +''' SQLCommand: a table name, a query name or a SELECT SQL statement +''' DirectSQL: when True, no syntax conversion is done by LO. Default = False +''' Ignored when SQLCommand is a table or a query name +''' Header: When True, a header row is inserted on the top of the array with the column names. Default = False +''' MaxRows: The maximum number of returned rows. If absent, all records are returned +''' Returns: +''' a 2D array(row, column), even if only 1 column and/or 1 record +''' an empty array if no records returned +''' Example: +''' Dim a As Variant +''' a = myDatabase.GetRows("SELECT [First Name], [Last Name] FROM [Employees] ORDER BY [Last Name]", Header := True) + +Dim vResult As Variant ' Return value +Dim oResult As Object ' com.sun.star.sdbc.XResultSet +Dim oQuery As Object ' com.sun.star.ucb.XContent +Dim sSql As String ' SQL statement +Dim bDirect ' Alias of DirectSQL +Dim lCols As Long ' Number of columns +Dim lRows As Long ' Number of rows +Dim oColumns As Object +Dim i As Long +Const cstThisSub = "SFDatabases.Database.GetRows" +Const cstSubArgs = "SQLCommand, [DirectSQL=False], [Header=False], [MaxRows=0]" + + 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, "SQLCommand", V_STRING) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(DirectSQL, "DirectSQL", ScriptForge.V_BOOLEAN) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(Header, "Header", ScriptForge.V_BOOLEAN) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(MaxRows, "MaxRows", ScriptForge.V_NUMERIC) Then GoTo Finally + End If + +Try: + ' Table, query of SQL ? Prepare resultset + If ScriptForge.SF_Array.Contains(Tables, SQLCommand, CaseSensitive := True, SortOrder := "ASC") Then + sSql = "SELECT * FROM [" & SQLCommand & "]" + bDirect = True + ElseIf ScriptForge.SF_Array.Contains(Queries, SQLCommand, CaseSensitive := True, SortOrder := "ASC") Then + Set oQuery = _Connection.Queries.getByName(SQLCommand) + sSql = oQuery.Command + bDirect = Not oQuery.EscapeProcessing + ElseIf ScriptForge.SF_String.StartsWith(SQLCommand, "SELECT", CaseSensitive := False) Then + sSql = SQLCommand + bDirect = DirectSQL + Else + GoTo Finally + End If + + ' Execute command + Set oResult = _ExecuteSql(sSql, bDirect) + If IsNull(oResult) Then GoTo Finally + + With oResult + 'Initialize output array with header row + Set oColumns = oResult.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 > 0 Then MaxRows = MaxRows + 1 + Else + lRows = -1 + End If + + ' Load data + .first() + Do While Not .isAfterLast() And (MaxRows = 0 Or lRows < 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 + .next() + Loop + End With + +Finally: + GetRows = vResult + ScriptForge.SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' SFDatabases.SF_Database.GetRows + +REM ----------------------------------------------------------------------------- +Public Function Methods() As Variant +''' Return the list of public methods of the Database service as an array + + Methods = Array( _ + "CloseDatabase" _ + , "DAvg" _ + , "DCount" _ + , "DLookup" _ + , "DMax" _ + , "DMin" _ + , "DSum" _ + , "GetRows" _ + , "RunSql" _ + ) + +End Function ' SFDatabases.SF_Database.Methods + +REM ----------------------------------------------------------------------------- +Public Function Properties() As Variant +''' Return the list or properties of the Database class as an array + + Properties = Array( _ + "Queries" _ + , "Tables" _ + , "XConnection" _ + , "XMetaData" _ + ) + +End Function ' SFDatabases.SF_Database.Properties + +REM ----------------------------------------------------------------------------- +Public Function RunSql(Optional ByVal SQLCommand As Variant _ + , Optional ByVal DirectSQL As Variant _ + ) As Boolean +''' Execute an action query (table creation, record insertion, ...) or SQL statement on the current database +''' Args: +''' SQLCommand: a query name or an SQL statement +''' DirectSQL: when True, no syntax conversion is done by LO. Default = False +''' Ignored when SQLCommand is a query name +''' Exceptions: +''' DBREADONLYERROR The method is not applicable on a read-only database +''' Example: +''' myDatabase.RunSql("INSERT INTO [EMPLOYEES] VALUES(25, 'SMITH', 'John')", DirectSQL := True) + +Dim bResult As Boolean ' Return value +Dim oStatement As Object ' com.sun.star.sdbc.XStatement +Dim oQuery As Object ' com.sun.star.ucb.XContent +Dim sSql As String ' SQL statement +Dim bDirect ' Alias of DirectSQL +Const cstQuery = 2, cstSql = 3 +Const cstThisSub = "SFDatabases.Database.RunSql" +Const cstSubArgs = "SQLCommand, [DirectSQL=False]" + + 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, "SQLCommand", V_STRING) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(DirectSQL, "DirectSQL", ScriptForge.V_BOOLEAN) Then GoTo Finally + End If + If _ReadOnly Then GoTo Catch_ReadOnly + +Try: + ' Query of SQL ? + If ScriptForge.SF_Array.Contains(Queries, SQLCommand, CaseSensitive := True, SortOrder := "ASC") Then + Set oQuery = _Connection.Queries.getByName(SQLCommand) + sSql = oQuery.Command + bDirect = Not oQuery.EscapeProcessing + ElseIf Not ScriptForge.SF_String.StartsWith(SQLCommand, "SELECT", CaseSensitive := False) Then + sSql = SQLCommand + bDirect = DirectSQL + Else + GoTo Finally + End If + + ' 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 ' SFDatabases.SF_Database.RunSql + +REM ----------------------------------------------------------------------------- +Public Function SetProperty(Optional ByVal PropertyName As Variant _ + , Optional ByRef Value As Variant _ + ) As Boolean +''' Set a new value to the given property +''' Args: +''' PropertyName: the name of the property as a string +''' Value: its new value +''' Exceptions +''' ARGUMENTERROR The property does not exist + +Const cstThisSub = "SFDatabases.Database.SetProperty" +Const cstSubArgs = "PropertyName, Value" + + 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, "PropertyName", 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 ' SFDatabases.SF_Database.SetProperty + +REM =========================================================== PRIVATE FUNCTIONS + +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 +''' Build and execute a SQL statement computing the aggregate function psFunction +''' on a field or expression pvExpression belonging to a table pvTableName +''' filtered by a WHERE-clause pvCriteria. +''' To order the results, a pvOrderClause may be precised. +''' Only the 1st record will be retained anyway. +''' Args: +''' psFunction an optional aggregate function: SUM, COUNT, AVG, LOOKUP +''' pvExpression: an SQL expression +''' pvTableName: the name of a table, NOT surrounded with quoting char +''' pvCriteria: an optional WHERE clause without the word WHERE +''' pvOrderClause: an optional order clause incl. "DESC" if relevant +''' (meaningful only for LOOKUP) + +Dim vResult As Variant ' Return value +Dim oResult As Object ' com.sun.star.sdbc.XResultSet +Dim sSql As String ' SQL statement. +Dim sExpr As String ' For inclusion of aggregate function +Dim sTarget as String ' Alias of pvExpression +Dim sWhere As String ' Alias of pvCriteria +Dim sOrderBy As String ' Alias of pvOrderClause +Dim sLimit As String ' TOP 1 clause +Dim sProductName As String ' RDBMS as a string +Const cstAliasField = "[" & "TMP_ALIAS_ANY_FIELD" & "]" ' Alias field in SQL expression +Dim cstThisSub As String : cstThisSub = "SFDatabases.SF_Database.D" & psFunction +Const cstSubArgs = "Expression, TableName, [Criteria=""""], [OrderClause=""""]" +Const cstLookup = "Lookup" + + If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + vResult = Null + +Check: + If IsMissing(pvCriteria) Or IsEmpty(pvCriteria) Then pvCriteria = "" + If IsMissing(pvOrderClause) Or IsEmpty(pvOrderClause) Then pvOrderClause = "" + If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then + If Not ScriptForge.SF_Utils._Validate(pvExpression, "Expression", V_STRING) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(pvTableName, "TableName", V_STRING, Tables) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(pvCriteria, "Criteria", V_STRING) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(pvOrderClause, "OrderClause", V_STRING) Then GoTo Finally + End If + +Try: + If pvCriteria <> "" Then sWhere = " WHERE " & pvCriteria Else sWhere = "" + If pvOrderClause <> "" Then sOrderBy = " ORDER BY " & pvOrderClause Else sOrderBy = "" + sLimit = "" + + pvTableName = "[" & pvTableName & "]" + + sProductName = UCase(_MetaData.getDatabaseProductName()) + + Select Case sProductName + Case "MYSQL", "SQLITE" + If psFunction = cstLookup Then + sTarget = pvExpression + sLimit = " LIMIT 1" + Else + sTarget = UCase(psFunction) & "(" & pvExpression & ")" + End If + sSql = "SELECT " & sTarget & " AS " & cstAliasField & " FROM " & psTableName & sWhere & sOrderBy & sLimit + Case "FIREBIRD (ENGINE12)" + If psFunction = cstLookup Then sTarget = "FIRST 1 " & pvExpression Else sTarget = UCase(psFunction) & "(" & pvExpression & ")" + sSql = "SELECT " & sTarget & " AS " & cstAliasField & " FROM " & pvTableName & sWhere & sOrderBy + Case Else ' Standard syntax - Includes HSQLDB + If psFunction = cstLookup Then sTarget = "TOP 1 " & pvExpression Else sTarget = UCase(psFunction) & "(" & pvExpression & ")" + sSql = "SELECT " & sTarget & " AS " & cstAliasField & " FROM " & pvTableName & sWhere & sOrderBy + End Select + + ' 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) ' Force return of binary field + End If + Set oResult = Nothing + +Finally: + _DFunction = vResult + ScriptForge.SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' SFDatabases.SF_Database._DFunction + +REM ----------------------------------------------------------------------------- +Private Function _ExecuteSql(ByVal psSql As String _ + , ByVal pbDirect As Boolean _ + ) As Variant +''' Return a read-only Resultset based on a SELECT SQL statement or execute the given action SQL (INSERT, CREATE TABLE, ...) +''' The method raises a fatal error when the SQL statement cannot be interpreted +''' Args: +''' psSql : the SQL statement. Square brackets are replaced by the correct field surrounding character +''' pbDirect: when True, no syntax conversion is done by LO. Default = False +''' Exceptions +''' SQLSYNTAXERROR The given SQL statement is incorrect + +Dim vResult As Variant ' Return value - com.sun.star.sdbc.XResultSet or Boolean +Dim oStatement As Object ' com.sun.star.sdbc.XStatement +Dim sSql As String ' Alias of psSql +Dim bSelect As Boolean ' True when SELECT statement +Dim bErrorHandler As Boolean ' 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, "SELECT", 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 + + ' 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: + ScriptForge.SF_Exception.RaiseFatal(SQLSYNTAXERROR, sSql) + GoTo Finally +Catch: + GoTo Finally +End Function ' SFDatabases.SF_Database._ExecuteSql + +REM ----------------------------------------------------------------------------- +Private Function _GetColumnValue(ByRef poResultSet As Object _ + , ByVal plColIndex As Long _ + , Optional ByVal pbReturnBinary As Boolean _ + ) As Variant +''' Get the data stored in the current record of a result set in a given column +''' The type of the column is found in the resultset's metadata +''' Args: +''' poResultSet: com.sun.star.sdbc.XResultSet +''' plColIndex: the index of the column to extract the value from +''' pbReturnBinary: when True, the method returns the content of a binary field, +''' as long as its length does not exceed a maximum length. +''' Default = False: binary fields are not returned, only their length +''' Returns: +''' The Variant value found in the column +''' Dates and times are returned as Basic dates +''' Null values are returned as Null +''' Errors or strange data types are returned as Null as well + +Dim vValue As Variant ' Return value +Dim lType As Long ' SQL column type: com.sun.star.sdbc.DataType +Dim vDateTime As Variant ' com.sun.star.util.DateTime +Dim oStream As Object ' Long character or binary streams +Dim bNullable As Boolean ' The field is defined as accepting Null values +Dim lSize As Long ' Binary field length + +Const cstMaxBinlength = 2 * 65535 + + On Local Error Goto 0 ' Disable error handler + vValue = Null ' Default value if error + If IsMissing(pbReturnBinary) Then pbReturnBinary = False + + 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 bNullable Then + If Not poResultSet.wasNull() Then + If Not ScriptForge.SF_Session.HasUNOMethod(oStream, "getLength") Then ' When no recordset + lSize = cstMaxBinLength + Else + lSize = CLng(oValue.getLength()) + End If + If lSize <= cstMaxBinLength And pbReturnBinary Then + vValue = Array() + oValue.readBytes(vValue, lSize) + Else ' Return length of field, not content + vValue = lSize + End If + End If + End If + oValue.closeInput() + 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 .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 = "" + End If + Case .TIME + vDateTime = poResultSet.getTime(plColIndex) + If Not poResultSet.wasNull() Then vValue = TimeSerial(vDateTime.Hours, vDateTime.Minutes, vDateTime.Seconds)', 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)', vDateTime.HundredthSeconds) + Case Else + vValue = poResultSet.getString(plColIndex) 'GIVE STRING A TRY + If IsNumeric(vValue) Then vValue = Val(vValue) 'Required when type = "", sometimes numeric fields are returned as strings (query/MSAccess) + End Select + If bNullable Then + If poResultSet.wasNull() Then vValue = Null + End If + End With + + _GetColumnValue = vValue + +End Function ' SFDatabases.SF_Database.GetColumnValue + +REM ----------------------------------------------------------------------------- +Private Function _PropertyGet(Optional ByVal psProperty As String) As Variant +''' Return the value of the named property +''' Args: +''' psProperty: the name of the property + +Dim cstThisSub As String +Const cstSubArgs = "" + + cstThisSub = "SFDatabases.Database.get" & psProperty + If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + + ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + + Select Case psProperty + Case "Queries" + If Not IsNull(_Connection) Then _PropertyGet = _Connection.Queries.getElementNames() Else _PropertyGet = Array() + Case "Tables" + If Not IsNull(_Connection) Then _PropertyGet = _Connection.Tables.getElementNames() Else _PropertyGet = Array() + Case "XConnection" + Set _PropertyGet = _Connection + Case "XMetaData" + Set _PropertyGet = _MetaData + Case Else + _PropertyGet = Null + End Select + +Finally: + ScriptForge.SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' SFDatabases.SF_Database._PropertyGet + +REM ----------------------------------------------------------------------------- +Private Function _ReplaceSquareBrackets(ByVal psSql As String) As String +''' Returns the input SQL command after replacement of square brackets by the table/field names quoting character + +Dim sSql As String ' Return value +Dim sQuote As String ' RDBMS specific table/field surrounding character +Dim sConstQuote As String ' Delimiter for string constants in SQL - usually the single quote +Const cstDouble = """" : Const cstSingle = "'" + +Try: + sQuote = _MetaData.IdentifierQuoteString + sConstQuote = Iif(sQuote = cstSingle, cstDouble, cstSingle) + + ' Replace the square brackets + sSql = Join(ScriptForge.SF_String.SplitNotQuoted(psSql, "[", , sConstQuote), sQuote) + sSql = Join(ScriptForge.SF_String.SplitNotQuoted(sSql, "]", , sConstQuote), sQuote) + +Finally: + _ReplaceSquareBrackets = sSql + Exit Function +End Function ' SFDatabases.SF_Database._ReplaceSquareBrackets + +REM ----------------------------------------------------------------------------- +Private Function _Repr() As String +''' Convert the Database instance to a readable string, typically for debugging purposes (DebugPrint ...) +''' Args: +''' Return: +''' "[DATABASE]: Location (Statusbar)" + + _Repr = "[DATABASE]: " & _Location & " (" & _URL & ")" + +End Function ' 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_Register.xba b/wizards/source/sfdatabases/SF_Register.xba new file mode 100644 index 000000000..c9b3f03d7 --- /dev/null +++ b/wizards/source/sfdatabases/SF_Register.xba @@ -0,0 +1,195 @@ +<?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 + +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +''' SF_Register +''' =========== +''' The ScriptForge framework includes +''' the master ScriptForge library +''' a number of "associated" libraries SF* +''' any user/contributor extension wanting to fit into the framework +''' +''' The main methods in this module allow the current library to cling to ScriptForge +''' - RegisterScriptServices +''' Register the list of services implemented by the current library +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +REM ================================================================== EXCEPTIONS + +Private Const BASEDOCUMENTOPENERROR = "BASEDOCUMENTOPENERROR" + +REM ============================================================== PUBLIC METHODS + +REM ----------------------------------------------------------------------------- +Public Sub RegisterScriptServices() As Variant +''' Register into ScriptForge the list of the services implemented by the current library +''' Each library pertaining to the framework must implement its own version of this method +''' +''' It consists in successive calls to the RegisterService() and RegisterEventManager() methods +''' with 2 arguments: +''' ServiceName: the name of the service as a case-insensitive string +''' ServiceReference: the reference as an object +''' If the reference refers to a module, then return the module as an object: +''' GlobalScope.Library.Module +''' If the reference is a class instance, then return a string referring to the method +''' containing the New statement creating the instance +''' "libraryname.modulename.function" + + With GlobalScope.ScriptForge.SF_Services + .RegisterService("Database", "SFDatabases.SF_Register._NewDatabase") ' Reference to the function initializing the service + .RegisterService("DatabaseFromDocument", "SFDatabases.SF_Register._NewDatabaseFromSource") + End With + +End Sub ' SFDatabases.SF_Register.RegisterScriptServices + +REM =========================================================== PRIVATE FUNCTIONS + +REM ----------------------------------------------------------------------------- +Public Function _NewDatabase(Optional ByVal pvArgs As Variant) As Object +''' Create a new instance of the SF_Database class +' Args: +''' FileName : the name of the file (compliant with the SF_FileSystem.FileNaming notation) +''' RegistrationName: mutually exclusive with FileName. Used when database is registered +''' ReadOnly : (boolean). Default = True +''' User : connection parameters +''' Password +''' Returns: +''' The instance or Nothing +''' Exceptions: +''' BASEDOCUMENTOPENERROR The database file could not be opened or connected + +Dim oDatabase As Object ' Return value +Dim vFileName As Variant ' alias of pvArgs(0) +Dim vRegistration As Variant ' Alias of pvArgs(1) +Dim vReadOnly As Variant ' Alias of pvArgs(2) +Dim vUser As Variant ' Alias of pvArgs(3) +Dim vPassword As Variant ' Alias of pvArgs(4) +Dim oDBContext As Object ' com.sun.star.sdb.DatabaseContext +Const cstService = "SFDatabases.Database" +Const cstGlobal = "GlobalScope" + + If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + +Check: + If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array() + If UBound(pvArgs) >= 0 Then vFileName = pvArgs(0) Else vFileName = "" + If IsEmpty(vFileName) Then vFileName = "" + If UBound(pvArgs) >= 1 Then vRegistration = pvArgs(1) Else vRegistration = "" + If IsEmpty(vRegistration) Then vRegistration = "" + If UBound(pvArgs) >= 2 Then vReadOnly = pvArgs(2) Else vReadOnly = True + If IsEmpty(vReadOnly) Then vReadOnly = True + If UBound(pvArgs) >= 3 Then vUser = pvArgs(3) Else vUser = "" + If IsEmpty(vUser) Then vUser = "" + If UBound(pvArgs) >= 4 Then vPassword = pvArgs(4) Else vPassword = "" + If IsEmpty(vPassword) Then vPassword = "" + If Not ScriptForge.SF_Utils._Validate(vFileName, "FileName", V_STRING) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(vRegistration, "RegistrationName", V_STRING) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(vReadOnly, "ReadOnly", ScriptForge.V_BOOLEAN) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(vUser, "User", V_STRING) Then GoTo Finally + If Not ScriptForge.SF_Utils._Validate(vPassword, "Password", V_STRING) Then GoTo Finally + Set oDatabase = Nothing + + ' Check the existence of FileName + With ScriptForge + Set oDBContext = .SF_Utils._GetUNOService("DatabaseContext") + If Len(vFileName) = 0 Then ' 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: + ' 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) + Set ._Connection = ._DataSource.getConnection(vUser, 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, "FileName", vFileName, "RegistrationName", vRegistration) + GoTo Finally +End Function ' SFDatabases.SF_Register._NewDatabase + +REM ----------------------------------------------------------------------------- +Public Function _NewDatabaseFromSource(Optional ByVal pvArgs As Variant) As Object +' ByRef poDataSource As Object _ +' , ByVal psUser As String _ +' , ByVal psPassword As String _ +' ) As Object +''' Create a new instance of the SF_Database class from the given datasource +''' established in the SFDocuments.Base service +''' THIS SERVICE MUST NOT BE CALLED FROM A USER SCRIPT +' Args: +''' DataSource: com.sun.star.sdbc.XDataSource +''' User, Password : connection parameters +''' Returns: +''' The instance or Nothing +''' Exceptions: +''' managed in the calling routines when Nothing is returned + +Dim oDatabase As Object ' Return value +Dim oConnection As Object ' com.sun.star.sdbc.XConnection +Dim oDataSource As Object ' Alias of pvArgs(0) +Dim sUser As String ' Alias of pvARgs(1) +Dim sPassword As String ' Alias of pvARgs(2) + + If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + Set oDatabase = Nothing + +Try: + ' Get arguments + Set oDataSource = pvArgs(0) + sUser = pvArgs(1) + sPassword = pvArgs(2) + + ' Setup the connection + If oDataSource.IsPasswordRequired Then + Set oConnection = oDataSource.getConnection(sUser, sPassword) + Else + Set oConnection = oDataSource.getConnection("", "") + End If + + ' Create the database Basic object and initialize attributes + If Not IsNull(oConnection) Then + Set oDatabase = New SF_Database + With oDatabase + Set .[Me] = oDatabase + ._Location = "" + 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: + GoTo Finally +End Function ' SFDatabases.SF_Register._NewDatabaseFromSource + +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 000000000..3b0c64d04 --- /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"> +''' 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 ======================================================================================================================= + +''' ScriptForge is distributed in the hope that it will be useful, +''' but WITHOUT ANY WARRANTY; without even the implied warranty of +''' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +''' ScriptForge is free software; you can redistribute it and/or modify it under the terms of either (at your option): + +''' 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not +''' distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ . + +''' 2) The GNU Lesser General Public License as published by +''' the Free Software Foundation, either version 3 of the License, or +''' (at your option) any later version. If a copy of the LGPL was not +''' 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 000000000..8b62d721a --- /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 000000000..6cea80d2a --- /dev/null +++ b/wizards/source/sfdatabases/script.xlb @@ -0,0 +1,7 @@ +<?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:library>
\ No newline at end of file |