SF_Database.xba 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
  3. <script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_Database" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
  4. REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
  5. REM === The SFDatabases library is one of the associated libraries. ===
  6. REM === Full documentation is available on https://help.libreoffice.org/ ===
  7. REM =======================================================================================================================
  8. Option Compatible
  9. Option ClassModule
  10. Option Explicit
  11. &apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
  12. &apos;&apos;&apos; SF_Database
  13. &apos;&apos;&apos; =========
  14. &apos;&apos;&apos; Management of databases embedded in or related to Base documents
  15. &apos;&apos;&apos; Each instance of the current class represents a single database, with essentially its tables, queries and data
  16. &apos;&apos;&apos;
  17. &apos;&apos;&apos; The exchanges with the database are done in SQL only.
  18. &apos;&apos;&apos; To make them more readable, use optionally square brackets to surround table/query/field names
  19. &apos;&apos;&apos; instead of the (RDBMS-dependent) normal surrounding character (usually, double-quote, single-quote or other).
  20. &apos;&apos;&apos; SQL statements may be run in direct or indirect mode. In direct mode the statement is transferred literally
  21. &apos;&apos;&apos; without syntax checking nor review to the database system.
  22. &apos;&apos;&apos;
  23. &apos;&apos;&apos; The provided interfaces include simple tables, queries and fields lists, and access to database metadata.
  24. &apos;&apos;&apos;
  25. &apos;&apos;&apos; Service invocation and usage:
  26. &apos;&apos;&apos; 1) To access any database at anytime
  27. &apos;&apos;&apos; Dim myDatabase As Object
  28. &apos;&apos;&apos; Set myDatabase = CreateScriptService(&quot;SFDatabases.Database&quot;, FileName, , [ReadOnly], [User, [Password]])
  29. &apos;&apos;&apos; &apos; Args:
  30. &apos;&apos;&apos; &apos; FileName: the name of the Base file compliant with the SF_FileSystem.FileNaming notation
  31. &apos;&apos;&apos; &apos; RegistrationName: the name of a registered database (mutually exclusive with FileName)
  32. &apos;&apos;&apos; &apos; ReadOnly: Default = True
  33. &apos;&apos;&apos; &apos; User, Password: additional connection arguments to the database server
  34. &apos;&apos;&apos; &apos; ... Run queries, SQL statements, ...
  35. &apos;&apos;&apos; myDatabase.CloseDatabase()
  36. &apos;&apos;&apos;
  37. &apos;&apos;&apos; 2) To access the database related to the current Base document
  38. &apos;&apos;&apos; Dim myDoc As Object, myDatabase As Object, ui As Object
  39. &apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
  40. &apos;&apos;&apos; Set myDoc = ui.OpenBaseDocument(&quot;myDb.odb&quot;)
  41. &apos;&apos;&apos; Set myDatabase = myDoc.GetDatabase() &apos; user and password are supplied here, if needed
  42. &apos;&apos;&apos; &apos; ... Run queries, SQL statements, ...
  43. &apos;&apos;&apos; myDoc.CloseDocument()
  44. &apos;&apos;&apos;
  45. &apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
  46. REM ================================================================== EXCEPTIONS
  47. Private Const DBREADONLYERROR = &quot;DBREADONLYERROR&quot;
  48. Private Const SQLSYNTAXERROR = &quot;SQLSYNTAXERROR&quot;
  49. REM ============================================================= PRIVATE MEMBERS
  50. Private [Me] As Object
  51. Private [_Parent] As Object
  52. Private ObjectType As String &apos; Must be DATABASE
  53. Private ServiceName As String
  54. Private _DataSource As Object &apos; com.sun.star.comp.dba.ODatabaseSource
  55. Private _Connection As Object &apos; com.sun.star.sdbc.XConnection
  56. Private _URL As String &apos; Text on status bar
  57. Private _Location As String &apos; File name
  58. Private _ReadOnly As Boolean
  59. Private _MetaData As Object &apos; com.sun.star.sdbc.XDatabaseMetaData
  60. REM ============================================================ MODULE CONSTANTS
  61. REM ===================================================== CONSTRUCTOR/DESTRUCTOR
  62. REM -----------------------------------------------------------------------------
  63. Private Sub Class_Initialize()
  64. Set [Me] = Nothing
  65. Set [_Parent] = Nothing
  66. ObjectType = &quot;DATABASE&quot;
  67. ServiceName = &quot;SFDatabases.Database&quot;
  68. Set _DataSource = Nothing
  69. Set _Connection = Nothing
  70. _URL = &quot;&quot;
  71. _Location = &quot;&quot;
  72. _ReadOnly = True
  73. Set _MetaData = Nothing
  74. End Sub &apos; SFDatabases.SF_Database Constructor
  75. REM -----------------------------------------------------------------------------
  76. Private Sub Class_Terminate()
  77. Call Class_Initialize()
  78. End Sub &apos; SFDatabases.SF_Database Destructor
  79. REM -----------------------------------------------------------------------------
  80. Public Function Dispose() As Variant
  81. Call Class_Terminate()
  82. Set Dispose = Nothing
  83. End Function &apos; SFDatabases.SF_Database Explicit Destructor
  84. REM ================================================================== PROPERTIES
  85. REM -----------------------------------------------------------------------------
  86. Property Get Queries() As Variant
  87. &apos;&apos;&apos; Return the list of available queries in the database
  88. Queries = _PropertyGet(&quot;Queries&quot;)
  89. End Property &apos; SFDatabases.SF_Database.Queries (get)
  90. REM -----------------------------------------------------------------------------
  91. Property Get Tables() As Variant
  92. &apos;&apos;&apos; Return the list of available Tables in the database
  93. Tables = _PropertyGet(&quot;Tables&quot;)
  94. End Property &apos; SFDatabases.SF_Database.Tables (get)
  95. REM -----------------------------------------------------------------------------
  96. Property Get XConnection() As Variant
  97. &apos;&apos;&apos; Return a com.sun.star.sdbc.XConnection UNO object
  98. XConnection = _PropertyGet(&quot;XConnection&quot;)
  99. End Property &apos; SFDatabases.SF_Database.XConnection (get)
  100. REM -----------------------------------------------------------------------------
  101. Property Get XMetaData() As Variant
  102. &apos;&apos;&apos; Return a com.sun.star.sdbc.XDatabaseMetaData UNO object
  103. XMetaData = _PropertyGet(&quot;XMetaData&quot;)
  104. End Property &apos; SFDatabases.SF_Database.XMetaData (get)
  105. REM ===================================================================== METHODS
  106. REM -----------------------------------------------------------------------------
  107. Public Sub CloseDatabase()
  108. &apos;&apos;&apos; Close the current database connection
  109. Const cstThisSub = &quot;SFDatabases.Database.CloseDatabase&quot;
  110. Const cstSubArgs = &quot;&quot;
  111. On Local Error GoTo 0 &apos; Disable useless error checking
  112. Check:
  113. ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
  114. Try:
  115. With _Connection
  116. If Not IsNull(_Connection) Then
  117. If ScriptForge.SF_Session.HasUnoMethod(_Connection, &quot;flush&quot;) Then .flush()
  118. .close()
  119. .dispose()
  120. End If
  121. Dispose()
  122. End With
  123. Finally:
  124. ScriptForge.SF_Utils._ExitFunction(cstThisSub)
  125. Exit Sub
  126. End Sub
  127. REM -----------------------------------------------------------------------------
  128. Public Function DAvg(Optional ByVal Expression As Variant _
  129. , Optional ByVal TableName As Variant _
  130. , Optional ByVal Criteria As Variant _
  131. ) As Variant
  132. &apos;&apos;&apos; Compute the aggregate function AVG() on a field or expression belonging to a table
  133. &apos;&apos;&apos; filtered by a WHERE-clause.
  134. &apos;&apos;&apos; Args:
  135. &apos;&apos;&apos; Expression: an SQL expression
  136. &apos;&apos;&apos; TableName: the name of a table
  137. &apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
  138. DAvg = _DFunction(&quot;Avg&quot;, Expression, TableName, Criteria)
  139. End Function &apos; SFDatabases.SF_Database.DAvg
  140. REM -----------------------------------------------------------------------------
  141. Public Function DCount(Optional ByVal Expression As Variant _
  142. , Optional ByVal TableName As Variant _
  143. , Optional ByVal Criteria As Variant _
  144. ) As Variant
  145. &apos;&apos;&apos; Compute the aggregate function COUNT() on a field or expression belonging to a table
  146. &apos;&apos;&apos; filtered by a WHERE-clause.
  147. &apos;&apos;&apos; Args:
  148. &apos;&apos;&apos; Expression: an SQL expression
  149. &apos;&apos;&apos; TableName: the name of a table
  150. &apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
  151. DCount = _DFunction(&quot;Count&quot;, Expression, TableName, Criteria)
  152. End Function &apos; SFDatabases.SF_Database.DCount
  153. REM -----------------------------------------------------------------------------
  154. Public Function DLookup(Optional ByVal Expression As Variant _
  155. , Optional ByVal TableName As Variant _
  156. , Optional ByVal Criteria As Variant _
  157. , Optional ByVal OrderClause As Variant _
  158. ) As Variant
  159. &apos;&apos;&apos; Compute the aggregate function Lookup() on a field or expression belonging to a table
  160. &apos;&apos;&apos; filtered by a WHERE-clause.
  161. &apos;&apos;&apos; To order the results, a pvOrderClause may be precised. The 1st record will be retained.
  162. &apos;&apos;&apos; Args:
  163. &apos;&apos;&apos; Expression: an SQL expression
  164. &apos;&apos;&apos; TableName: the name of a table
  165. &apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
  166. &apos;&apos;&apos; pvOrderClause: an optional order clause incl. &quot;DESC&quot; if relevant
  167. DLookup = _DFunction(&quot;Lookup&quot;, Expression, TableName, Criteria, OrderClause)
  168. End Function &apos; SFDatabases.SF_Database.DLookup
  169. REM -----------------------------------------------------------------------------
  170. Public Function DMax(Optional ByVal Expression As Variant _
  171. , Optional ByVal TableName As Variant _
  172. , Optional ByVal Criteria As Variant _
  173. ) As Variant
  174. &apos;&apos;&apos; Compute the aggregate function MAX() on a field or expression belonging to a table
  175. &apos;&apos;&apos; filtered by a WHERE-clause.
  176. &apos;&apos;&apos; Args:
  177. &apos;&apos;&apos; Expression: an SQL expression
  178. &apos;&apos;&apos; TableName: the name of a table
  179. &apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
  180. DMax = _DFunction(&quot;Max&quot;, Expression, TableName, Criteria)
  181. End Function &apos; SFDatabases.SF_Database.DMax
  182. REM -----------------------------------------------------------------------------
  183. Public Function DMin(Optional ByVal Expression As Variant _
  184. , Optional ByVal TableName As Variant _
  185. , Optional ByVal Criteria As Variant _
  186. ) As Variant
  187. &apos;&apos;&apos; Compute the aggregate function MIN() on a field or expression belonging to a table
  188. &apos;&apos;&apos; filtered by a WHERE-clause.
  189. &apos;&apos;&apos; Args:
  190. &apos;&apos;&apos; Expression: an SQL expression
  191. &apos;&apos;&apos; TableName: the name of a table
  192. &apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
  193. DMin = _DFunction(&quot;Min&quot;, Expression, TableName, Criteria)
  194. End Function &apos; SFDatabases.SF_Database.DMin
  195. REM -----------------------------------------------------------------------------
  196. Public Function DSum(Optional ByVal Expression As Variant _
  197. , Optional ByVal TableName As Variant _
  198. , Optional ByVal Criteria As Variant _
  199. ) As Variant
  200. &apos;&apos;&apos; Compute the aggregate function Sum() on a field or expression belonging to a table
  201. &apos;&apos;&apos; filtered by a WHERE-clause.
  202. &apos;&apos;&apos; Args:
  203. &apos;&apos;&apos; Expression: an SQL expression
  204. &apos;&apos;&apos; TableName: the name of a table
  205. &apos;&apos;&apos; Criteria: an optional WHERE clause without the word WHERE
  206. DSum = _DFunction(&quot;Sum&quot;, Expression, TableName, Criteria)
  207. End Function &apos; SFDatabases.SF_Database.DSum
  208. REM -----------------------------------------------------------------------------
  209. Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
  210. &apos;&apos;&apos; Return the actual value of the given property
  211. &apos;&apos;&apos; Args:
  212. &apos;&apos;&apos; PropertyName: the name of the property as a string
  213. &apos;&apos;&apos; Returns:
  214. &apos;&apos;&apos; The actual value of the property
  215. &apos;&apos;&apos; Exceptions:
  216. &apos;&apos;&apos; ARGUMENTERROR The property does not exist
  217. &apos;&apos;&apos; Examples:
  218. &apos;&apos;&apos; myDatabase.GetProperty(&quot;Queries&quot;)
  219. Const cstThisSub = &quot;SFDatabases.Database.GetProperty&quot;
  220. Const cstSubArgs = &quot;&quot;
  221. If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  222. GetProperty = Null
  223. Check:
  224. If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  225. If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
  226. End If
  227. Try:
  228. GetProperty = _PropertyGet(PropertyName)
  229. Finally:
  230. ScriptForge.SF_Utils._ExitFunction(cstThisSub)
  231. Exit Function
  232. Catch:
  233. GoTo Finally
  234. End Function &apos; SFDatabases.SF_Database.GetProperty
  235. REM -----------------------------------------------------------------------------
  236. Public Function GetRows(Optional ByVal SQLCommand As Variant _
  237. , Optional ByVal DirectSQL As Variant _
  238. , Optional ByVal Header As Variant _
  239. , Optional ByVal MaxRows As Variant _
  240. ) As Variant
  241. &apos;&apos;&apos; Return the content of a table, a query or a SELECT SQL statement as an array
  242. &apos;&apos;&apos; Args:
  243. &apos;&apos;&apos; SQLCommand: a table name, a query name or a SELECT SQL statement
  244. &apos;&apos;&apos; DirectSQL: when True, no syntax conversion is done by LO. Default = False
  245. &apos;&apos;&apos; Ignored when SQLCommand is a table or a query name
  246. &apos;&apos;&apos; Header: When True, a header row is inserted on the top of the array with the column names. Default = False
  247. &apos;&apos;&apos; MaxRows: The maximum number of returned rows. If absent, all records are returned
  248. &apos;&apos;&apos; Returns:
  249. &apos;&apos;&apos; a 2D array(row, column), even if only 1 column and/or 1 record
  250. &apos;&apos;&apos; an empty array if no records returned
  251. &apos;&apos;&apos; Example:
  252. &apos;&apos;&apos; Dim a As Variant
  253. &apos;&apos;&apos; a = myDatabase.GetRows(&quot;SELECT [First Name], [Last Name] FROM [Employees] ORDER BY [Last Name]&quot;, Header := True)
  254. Dim vResult As Variant &apos; Return value
  255. Dim oResult As Object &apos; com.sun.star.sdbc.XResultSet
  256. Dim oQuery As Object &apos; com.sun.star.ucb.XContent
  257. Dim sSql As String &apos; SQL statement
  258. Dim bDirect &apos; Alias of DirectSQL
  259. Dim lCols As Long &apos; Number of columns
  260. Dim lRows As Long &apos; Number of rows
  261. Dim oColumns As Object
  262. Dim i As Long
  263. Const cstThisSub = &quot;SFDatabases.Database.GetRows&quot;
  264. Const cstSubArgs = &quot;SQLCommand, [DirectSQL=False], [Header=False], [MaxRows=0]&quot;
  265. If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  266. vResult = Array()
  267. Check:
  268. If IsMissing(DirectSQL) Or IsEmpty(DirectSQL) Then DirectSQL = False
  269. If IsMissing(Header) Or IsEmpty(Header) Then Header = False
  270. If IsMissing(MaxRows) Or IsEmpty(MaxRows) Then MaxRows = 0
  271. If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  272. If Not ScriptForge.SF_Utils._Validate(SQLCommand, &quot;SQLCommand&quot;, V_STRING) Then GoTo Finally
  273. If Not ScriptForge.SF_Utils._Validate(DirectSQL, &quot;DirectSQL&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
  274. If Not ScriptForge.SF_Utils._Validate(Header, &quot;Header&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
  275. If Not ScriptForge.SF_Utils._Validate(MaxRows, &quot;MaxRows&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
  276. End If
  277. Try:
  278. &apos; Table, query of SQL ? Prepare resultset
  279. If ScriptForge.SF_Array.Contains(Tables, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
  280. sSql = &quot;SELECT * FROM [&quot; &amp; SQLCommand &amp; &quot;]&quot;
  281. bDirect = True
  282. ElseIf ScriptForge.SF_Array.Contains(Queries, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
  283. Set oQuery = _Connection.Queries.getByName(SQLCommand)
  284. sSql = oQuery.Command
  285. bDirect = Not oQuery.EscapeProcessing
  286. ElseIf ScriptForge.SF_String.StartsWith(SQLCommand, &quot;SELECT&quot;, CaseSensitive := False) Then
  287. sSql = SQLCommand
  288. bDirect = DirectSQL
  289. Else
  290. GoTo Finally
  291. End If
  292. &apos; Execute command
  293. Set oResult = _ExecuteSql(sSql, bDirect)
  294. If IsNull(oResult) Then GoTo Finally
  295. With oResult
  296. &apos;Initialize output array with header row
  297. Set oColumns = oResult.getColumns()
  298. lCols = oColumns.Count - 1
  299. If Header Then
  300. lRows = 0
  301. ReDim vResult(0 To lRows, 0 To lCols)
  302. For i = 0 To lCols
  303. vResult(lRows, i) = oColumns.getByIndex(i).Name
  304. Next i
  305. If MaxRows &gt; 0 Then MaxRows = MaxRows + 1
  306. Else
  307. lRows = -1
  308. End If
  309. &apos; Load data
  310. .first()
  311. Do While Not .isAfterLast() And (MaxRows = 0 Or lRows &lt; MaxRows - 1)
  312. lRows = lRows + 1
  313. If lRows = 0 Then
  314. ReDim vResult(0 To lRows, 0 To lCols)
  315. Else
  316. ReDim Preserve vResult(0 To lRows, 0 To lCols)
  317. End If
  318. For i = 0 To lCols
  319. vResult(lRows, i) = _GetColumnValue(oResult, i + 1)
  320. Next i
  321. .next()
  322. Loop
  323. End With
  324. Finally:
  325. GetRows = vResult
  326. ScriptForge.SF_Utils._ExitFunction(cstThisSub)
  327. Exit Function
  328. Catch:
  329. GoTo Finally
  330. End Function &apos; SFDatabases.SF_Database.GetRows
  331. REM -----------------------------------------------------------------------------
  332. Public Function Methods() As Variant
  333. &apos;&apos;&apos; Return the list of public methods of the Database service as an array
  334. Methods = Array( _
  335. &quot;DAvg&quot; _
  336. , &quot;DCount&quot; _
  337. , &quot;DLookup&quot; _
  338. , &quot;DMax&quot; _
  339. , &quot;DMin&quot; _
  340. , &quot;DSum&quot; _
  341. , &quot;GetRows&quot; _
  342. , &quot;RunSql&quot; _
  343. )
  344. End Function &apos; SFDatabases.SF_Database.Methods
  345. REM -----------------------------------------------------------------------------
  346. Public Function Properties() As Variant
  347. &apos;&apos;&apos; Return the list or properties of the Database class as an array
  348. Properties = Array( _
  349. &quot;Queries&quot; _
  350. , &quot;Tables&quot; _
  351. , &quot;XConnection&quot; _
  352. , &quot;XMetaData&quot; _
  353. )
  354. End Function &apos; SFDatabases.SF_Database.Properties
  355. REM -----------------------------------------------------------------------------
  356. Public Function RunSql(Optional ByVal SQLCommand As Variant _
  357. , Optional ByVal DirectSQL As Variant _
  358. ) As Boolean
  359. &apos;&apos;&apos; Execute an action query (table creation, record insertion, ...) or SQL statement on the current database
  360. &apos;&apos;&apos; Args:
  361. &apos;&apos;&apos; SQLCommand: a query name or an SQL statement
  362. &apos;&apos;&apos; DirectSQL: when True, no syntax conversion is done by LO. Default = False
  363. &apos;&apos;&apos; Ignored when SQLCommand is a query name
  364. &apos;&apos;&apos; Exceptions:
  365. &apos;&apos;&apos; DBREADONLYERROR The method is not applicable on a read-only database
  366. &apos;&apos;&apos; Example:
  367. &apos;&apos;&apos; myDatabase.RunSql(&quot;INSERT INTO [EMPLOYEES] VALUES(25, &apos;SMITH&apos;, &apos;John&apos;)&quot;, DirectSQL := True)
  368. Dim bResult As Boolean &apos; Return value
  369. Dim oStatement As Object &apos; com.sun.star.sdbc.XStatement
  370. Dim iCommandType &apos; 1 = Table, 2 = Query, 3 = SQL
  371. Dim oQuery As Object &apos; com.sun.star.ucb.XContent
  372. Dim sSql As String &apos; SQL statement
  373. Dim bDirect &apos; Alias of DirectSQL
  374. Const cstQuery = 2, cstSql = 3
  375. Const cstThisSub = &quot;SFDatabases.Database.RunSql&quot;
  376. Const cstSubArgs = &quot;SQLCommand, [DirectSQL=False]&quot;
  377. If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  378. bResult = False
  379. Check:
  380. If IsMissing(DirectSQL) Or IsEmpty(DirectSQL) Then DirectSQL = False
  381. If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  382. If Not ScriptForge.SF_Utils._Validate(SQLCommand, &quot;SQLCommand&quot;, V_STRING) Then GoTo Finally
  383. If Not ScriptForge.SF_Utils._Validate(DirectSQL, &quot;DirectSQL&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
  384. End If
  385. If _ReadOnly Then GoTo Catch_ReadOnly
  386. Try:
  387. &apos; Query of SQL ?
  388. If ScriptForge.SF_Array.Contains(Queries, SQLCommand, CaseSensitive := True, SortOrder := &quot;ASC&quot;) Then
  389. Set oQuery = _Connection.Queries.getByName(SQLCommand)
  390. sSql = oQuery.Command
  391. bDirect = Not oQuery.EscapeProcessing
  392. ElseIf Not ScriptForge.SF_String.StartsWith(SQLCommand, &quot;SELECT&quot;, CaseSensitive := False) Then
  393. sSql = SQLCommand
  394. bDirect = DirectSQL
  395. Else
  396. GoTo Finally
  397. End If
  398. &apos; Execute command
  399. bResult = _ExecuteSql(sSql, bDirect)
  400. Finally:
  401. RunSql = bResult
  402. ScriptForge.SF_Utils._ExitFunction(cstThisSub)
  403. Exit Function
  404. Catch:
  405. GoTo Finally
  406. Catch_ReadOnly:
  407. ScriptForge.SF_Exception.RaiseFatal(DBREADONLYERROR)
  408. GoTo Finally
  409. End Function &apos; SFDatabases.SF_Database.RunSql
  410. REM -----------------------------------------------------------------------------
  411. Public Function SetProperty(Optional ByVal PropertyName As Variant _
  412. , Optional ByRef Value As Variant _
  413. ) As Boolean
  414. &apos;&apos;&apos; Set a new value to the given property
  415. &apos;&apos;&apos; Args:
  416. &apos;&apos;&apos; PropertyName: the name of the property as a string
  417. &apos;&apos;&apos; Value: its new value
  418. &apos;&apos;&apos; Exceptions
  419. &apos;&apos;&apos; ARGUMENTERROR The property does not exist
  420. Const cstThisSub = &quot;SFDatabases.Database.SetProperty&quot;
  421. Const cstSubArgs = &quot;PropertyName, Value&quot;
  422. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  423. SetProperty = False
  424. Check:
  425. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  426. If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
  427. End If
  428. Try:
  429. Select Case UCase(PropertyName)
  430. Case Else
  431. End Select
  432. Finally:
  433. SF_Utils._ExitFunction(cstThisSub)
  434. Exit Function
  435. Catch:
  436. GoTo Finally
  437. End Function &apos; SFDatabases.SF_Database.SetProperty
  438. REM =========================================================== PRIVATE FUNCTIONS
  439. REM -----------------------------------------------------------------------------------------------------------------------
  440. Private Function _DFunction(ByVal psFunction As String _
  441. , Optional ByVal pvExpression As Variant _
  442. , Optional ByVal pvTableName As Variant _
  443. , Optional ByVal pvCriteria As Variant _
  444. , Optional ByVal pvOrderClause As Variant _
  445. ) As Variant
  446. &apos;&apos;&apos; Build and execute a SQL statement computing the aggregate function psFunction
  447. &apos;&apos;&apos; on a field or expression pvExpression belonging to a table pvTableName
  448. &apos;&apos;&apos; filtered by a WHERE-clause pvCriteria.
  449. &apos;&apos;&apos; To order the results, a pvOrderClause may be precised.
  450. &apos;&apos;&apos; Only the 1st record will be retained anyway.
  451. &apos;&apos;&apos; Args:
  452. &apos;&apos;&apos; psFunction an optional aggregate function: SUM, COUNT, AVG, LOOKUP
  453. &apos;&apos;&apos; pvExpression: an SQL expression
  454. &apos;&apos;&apos; pvTableName: the name of a table, NOT surrounded with quoting char
  455. &apos;&apos;&apos; pvCriteria: an optional WHERE clause without the word WHERE
  456. &apos;&apos;&apos; pvOrderClause: an optional order clause incl. &quot;DESC&quot; if relevant
  457. &apos;&apos;&apos; (meaningful only for LOOKUP)
  458. Dim vResult As Variant &apos; Return value
  459. Dim oResult As Object &apos; com.sun.star.sdbc.XResultSet
  460. Dim sSql As String &apos; SQL statement.
  461. Dim sExpr As String &apos; For inclusion of aggregate function
  462. Dim sTarget as String &apos; Alias of pvExpression
  463. Dim sWhere As String &apos; Alias of pvCriteria
  464. Dim sOrderBy As String &apos; Alias of pvOrderClause
  465. Dim sLimit As String &apos; TOP 1 clause
  466. Dim sProductName As String &apos; RDBMS as a string
  467. Const cstAliasField = &quot;[&quot; &amp; &quot;TMP_ALIAS_ANY_FIELD&quot; &amp; &quot;]&quot; &apos; Alias field in SQL expression
  468. Dim cstThisSub As String : cstThisSub = &quot;SFDatabases.SF_Database.D&quot; &amp; psFunction
  469. Const cstSubArgs = &quot;Expression, TableName, [Criteria=&quot;&quot;&quot;&quot;], [OrderClause=&quot;&quot;&quot;&quot;]&quot;
  470. Const cstLookup = &quot;Lookup&quot;
  471. If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  472. vResult = Null
  473. Check:
  474. If IsMissing(pvCriteria) Or IsEmpty(pvCriteria) Then pvCriteria = &quot;&quot;
  475. If IsMissing(pvOrderClause) Or IsEmpty(pvOrderClause) Then pvOrderClause = &quot;&quot;
  476. If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  477. If Not ScriptForge.SF_Utils._Validate(pvExpression, &quot;Expression&quot;, V_STRING) Then GoTo Finally
  478. If Not ScriptForge.SF_Utils._Validate(pvTableName, &quot;TableName&quot;, V_STRING, Tables) Then GoTo Finally
  479. If Not ScriptForge.SF_Utils._Validate(pvCriteria, &quot;Criteria&quot;, V_STRING) Then GoTo Finally
  480. If Not ScriptForge.SF_Utils._Validate(pvOrderClause, &quot;OrderClause&quot;, V_STRING) Then GoTo Finally
  481. End If
  482. Try:
  483. If pvCriteria &lt;&gt; &quot;&quot; Then sWhere = &quot; WHERE &quot; &amp; pvCriteria Else sWhere = &quot;&quot;
  484. If pvOrderClause &lt;&gt; &quot;&quot; Then sOrderBy = &quot; ORDER BY &quot; &amp; pvOrderClause Else sOrderBy = &quot;&quot;
  485. sLimit = &quot;&quot;
  486. pvTableName = &quot;[&quot; &amp; pvTableName &amp; &quot;]&quot;
  487. sProductName = UCase(_MetaData.getDatabaseProductName())
  488. Select Case sProductName
  489. Case &quot;MYSQL&quot;, &quot;SQLITE&quot;
  490. If psFunction = cstLookup Then
  491. sTarget = pvExpression
  492. sLimit = &quot; LIMIT 1&quot;
  493. Else
  494. sTarget = UCase(psFunction) &amp; &quot;(&quot; &amp; pvExpression &amp; &quot;)&quot;
  495. End If
  496. sSql = &quot;SELECT &quot; &amp; sTarget &amp; &quot; AS &quot; &amp; cstAliasField &amp; &quot; FROM &quot; &amp; psTableName &amp; sWhere &amp; sOrderBy &amp; sLimit
  497. Case &quot;FIREBIRD (ENGINE12)&quot;
  498. If psFunction = cstLookup Then sTarget = &quot;FIRST 1 &quot; &amp; pvExpression Else sTarget = UCase(psFunction) &amp; &quot;(&quot; &amp; pvExpression &amp; &quot;)&quot;
  499. sSql = &quot;SELECT &quot; &amp; sTarget &amp; &quot; AS &quot; &amp; cstAliasField &amp; &quot; FROM &quot; &amp; pvTableName &amp; sWhere &amp; sOrderBy
  500. Case Else &apos; Standard syntax - Includes HSQLDB
  501. If psFunction = cstLookup Then sTarget = &quot;TOP 1 &quot; &amp; pvExpression Else sTarget = UCase(psFunction) &amp; &quot;(&quot; &amp; pvExpression &amp; &quot;)&quot;
  502. sSql = &quot;SELECT &quot; &amp; sTarget &amp; &quot; AS &quot; &amp; cstAliasField &amp; &quot; FROM &quot; &amp; pvTableName &amp; sWhere &amp; sOrderBy
  503. End Select
  504. &apos; Execute the SQL statement and retain the first column of the first record
  505. Set oResult = _ExecuteSql(sSql, True)
  506. If Not IsNull(oResult) And Not IsEmpty(oResult) Then
  507. If Not oResult.first() Then Goto Finally
  508. If oResult.isAfterLast() Then GoTo Finally
  509. vResult = _GetColumnValue(oResult, 1, True) &apos; Force return of binary field
  510. End If
  511. Set oResult = Nothing
  512. Finally:
  513. _DFunction = vResult
  514. ScriptForge.SF_Utils._ExitFunction(cstThisSub)
  515. Exit Function
  516. Catch:
  517. GoTo Finally
  518. End Function &apos; SFDatabases.SF_Database._DFunction
  519. REM -----------------------------------------------------------------------------
  520. Private Function _ExecuteSql(ByVal psSql As String _
  521. , ByVal pbDirect As Boolean _
  522. ) As Variant
  523. &apos;&apos;&apos; Return a read-only Resultset based on a SELECT SQL statement or execute the given action SQL (INSERT, CREATE TABLE, ...)
  524. &apos;&apos;&apos; The method raises a fatal error when the SQL statement cannot be interpreted
  525. &apos;&apos;&apos; Args:
  526. &apos;&apos;&apos; psSql : the SQL statement. Square brackets are replaced by the correct field surrounding character
  527. &apos;&apos;&apos; pbDirect: when True, no syntax conversion is done by LO. Default = False
  528. &apos;&apos;&apos; Exceptions
  529. &apos;&apos;&apos; SQLSYNTAXERROR The given SQL statement is incorrect
  530. Dim vResult As Variant &apos; Return value - com.sun.star.sdbc.XResultSet or Boolean
  531. Dim oStatement As Object &apos; com.sun.star.sdbc.XStatement
  532. Dim sSql As String &apos; Alias of psSql
  533. Dim bSelect As Boolean &apos; True when SELECT statement
  534. Dim bErrorHandler As Boolean &apos; Can be set off to ease debugging of complex SQL statements
  535. Set vResult = Nothing
  536. bErrorHandler = ScriptForge.SF_Utils._ErrorHandling()
  537. If bErrorHandler Then On Local Error GoTo Catch
  538. Try:
  539. sSql = _ReplaceSquareBrackets(psSql)
  540. bSelect = ScriptForge.SF_String.StartsWith(sSql, &quot;SELECT&quot;, CaseSensitive := False)
  541. Set oStatement = _Connection.createStatement()
  542. With oStatement
  543. If bSelect Then
  544. .ResultSetType = com.sun.star.sdbc.ResultSetType.SCROLL_INSENSITIVE
  545. .ResultSetConcurrency = com.sun.star.sdbc.ResultSetConcurrency.READ_ONLY
  546. End If
  547. .EscapeProcessing = Not pbDirect
  548. &apos; Setup the result set
  549. If bErrorHandler Then On Local Error GoTo Catch_Sql
  550. If bSelect Then Set vResult = .executeQuery(sSql) Else vResult = .execute(sSql)
  551. End With
  552. Finally:
  553. _ExecuteSql = vResult
  554. Set oStatement = Nothing
  555. Exit Function
  556. Catch_Sql:
  557. ScriptForge.SF_Exception.RaiseFatal(SQLSYNTAXERROR, sSql)
  558. GoTo Finally
  559. Catch:
  560. GoTo Finally
  561. End Function &apos; SFDatabases.SF_Database._ExecuteSql
  562. REM -----------------------------------------------------------------------------
  563. Private Function _GetColumnValue(ByRef poResultSet As Object _
  564. , ByVal plColIndex As Long _
  565. , Optional ByVal pbReturnBinary As Boolean _
  566. ) As Variant
  567. &apos;&apos;&apos; Get the data stored in the current record of a result set in a given column
  568. &apos;&apos;&apos; The type of the column is found in the resultset&apos;s metadata
  569. &apos;&apos;&apos; Args:
  570. &apos;&apos;&apos; poResultSet: com.sun.star.sdbc.XResultSet
  571. &apos;&apos;&apos; plColIndex: the index of the column to extract the value from
  572. &apos;&apos;&apos; pbReturnBinary: when True, the method returns the content of a binary field,
  573. &apos;&apos;&apos; as long as its length does not exceed a maximum length.
  574. &apos;&apos;&apos; Default = False: binary fields are not returned, only their length
  575. &apos;&apos;&apos; Returns:
  576. &apos;&apos;&apos; The variant value found in the column
  577. &apos;&apos;&apos; Dates and times are returned as Basic dates
  578. &apos;&apos;&apos; Null values are returned as Null
  579. &apos;&apos;&apos; Errors or strange data types are returned as Null as well
  580. Dim vValue As Variant &apos; Return value
  581. Dim lType As Long &apos; SQL column type: com.sun.star.sdbc.DataType
  582. Dim vDateTime As Variant &apos; com.sun.star.util.DateTime
  583. Dim oStream As Object &apos; Long character or binary streams
  584. Dim bNullable As Boolean &apos; The field is defined as accepting Null values
  585. Dim lSize As Long &apos; Binary field length
  586. Const cstMaxBinlength = 2 * 65535
  587. On Local Error Goto 0 &apos; Disable error handler
  588. vValue = Null &apos; Default value if error
  589. If IsMissing(pbReturnBinary) Then pbReturnBinary = False
  590. With com.sun.star.sdbc.DataType
  591. lType = poResultSet.MetaData.getColumnType(plColIndex)
  592. bNullable = ( poResultSet.MetaData.IsNullable(plColIndex) = com.sun.star.sdbc.ColumnValue.NULLABLE )
  593. Select Case lType
  594. Case .ARRAY : vValue = poResultSet.getArray(plColIndex)
  595. Case .BINARY, .VARBINARY, .LONGVARBINARY, .BLOB
  596. Set oStream = poResultSet.getBinaryStream(plColIndex)
  597. If bNullable Then
  598. If Not poResultSet.wasNull() Then
  599. If Not ScriptForge.SF_Session.HasUNOMethod(oStream, &quot;getLength&quot;) Then &apos; When no recordset
  600. lSize = cstMaxBinLength
  601. Else
  602. lSize = CLng(oValue.getLength())
  603. End If
  604. If lSize &lt;= cstMaxBinLength And pbReturnBinary Then
  605. vValue = Array()
  606. oValue.readBytes(vValue, lSize)
  607. Else &apos; Return length of field, not content
  608. vValue = lSize
  609. End If
  610. End If
  611. End If
  612. oValue.closeInput()
  613. Case .BIT, .BOOLEAN : vValue = poResultSet.getBoolean(plColIndex)
  614. Case .DATE
  615. vDateTime = poResultSet.getDate(plColIndex)
  616. If Not poResultSet.wasNull() Then vValue = DateSerial(CInt(vDateTime.Year), CInt(vDateTime.Month), CInt(vDateTime.Day))
  617. Case .DISTINCT, .OBJECT, .OTHER, .STRUCT
  618. vValue = Null
  619. Case .DOUBLE, .REAL : vValue = poResultSet.getDouble(plColIndex)
  620. Case .FLOAT : vValue = poResultSet.getFloat(plColIndex)
  621. Case .INTEGER, .SMALLINT : vValue = poResultSet.getInt(plColIndex)
  622. Case .BIGINT : vValue = CLng(poResultSet.getLong(plColIndex))
  623. Case .DECIMAL, .NUMERIC : vValue = poResultSet.getDouble(plColIndex)
  624. Case .SQLNULL : vValue = poResultSet.getNull(plColIndex)
  625. Case .OBJECT, .OTHER, .STRUCT : vValue = Null
  626. Case .REF : vValue = poResultSet.getRef(plColIndex)
  627. Case .TINYINT : vValue = poResultSet.getShort(plColIndex)
  628. Case .CHAR, .VARCHAR : vValue = poResultSet.getString(plColIndex)
  629. Case .LONGVARCHAR, .CLOB
  630. If bNullable Then
  631. If Not poResultSet.wasNull() Then vValue = poResultSet.getString(plColIndex)
  632. Else
  633. vValue = &quot;&quot;
  634. End If
  635. Case .TIME
  636. vDateTime = poResultSet.getTime(plColIndex)
  637. If Not poResultSet.wasNull() Then vValue = TimeSerial(vDateTime.Hours, vDateTime.Minutes, vDateTime.Seconds)&apos;, vDateTime.HundredthSeconds)
  638. Case .TIMESTAMP
  639. vDateTime = poResultSet.getTimeStamp(plColIndex)
  640. If Not poResultSet.wasNull() Then vValue = DateSerial(CInt(vDateTime.Year), CInt(vDateTime.Month), CInt(vDateTime.Day)) _
  641. + TimeSerial(vDateTime.Hours, vDateTime.Minutes, vDateTime.Seconds)&apos;, vDateTime.HundredthSeconds)
  642. Case Else
  643. vValue = poResultSet.getString(plColIndex) &apos;GIVE STRING A TRY
  644. If IsNumeric(vValue) Then vValue = Val(vValue) &apos;Required when type = &quot;&quot;, sometimes numeric fields are returned as strings (query/MSAccess)
  645. End Select
  646. If bNullable Then
  647. If poResultSet.wasNull() Then vValue = Null
  648. End If
  649. End With
  650. _GetColumnValue = vValue
  651. End Function &apos; SFDatabases.SF_Database.GetColumnValue
  652. REM -----------------------------------------------------------------------------
  653. Public Sub _Initialize()
  654. &apos;&apos;&apos; Complete the object creation process:
  655. &apos;&apos;&apos; - Initialization of private members
  656. &apos;&apos;&apos; - Creation of the dialog graphical interface
  657. &apos;&apos;&apos; - Addition of the new object in the Dialogs buffer
  658. Try:
  659. &apos; Create the graphical interface
  660. Set _DialogControl = CreateUnoDialog(_DialogProvider)
  661. Set _DialogModel = _DialogControl.Model
  662. &apos; Add dialog reference to cache
  663. _CacheIndex = SF_Register._AddDialogToCache(_DialogControl, [Me])
  664. 85
  665. Finally:
  666. Exit Sub
  667. End Sub &apos; SFDatabases.SF_Database._Initialize
  668. REM -----------------------------------------------------------------------------
  669. Private Function _PropertyGet(Optional ByVal psProperty As String) As Variant
  670. &apos;&apos;&apos; Return the value of the named property
  671. &apos;&apos;&apos; Args:
  672. &apos;&apos;&apos; psProperty: the name of the property
  673. Dim cstThisSub As String
  674. Const cstSubArgs = &quot;&quot;
  675. cstThisSub = &quot;SFDatabases.Database.get&quot; &amp; psProperty
  676. If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  677. ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
  678. Select Case psProperty
  679. Case &quot;Queries&quot;
  680. If Not IsNull(_Connection) Then _PropertyGet = _Connection.Queries.getElementNames() Else _PropertyGet = Array()
  681. Case &quot;Tables&quot;
  682. If Not IsNull(_Connection) Then _PropertyGet = _Connection.Tables.getElementNames() Else _PropertyGet = Array()
  683. Case &quot;XConnection&quot;
  684. Set _PropertyGet = _Connection
  685. Case &quot;XMetaData&quot;
  686. Set _PropertyGet = _MetaData
  687. Case Else
  688. _PropertyGet = Null
  689. End Select
  690. Finally:
  691. ScriptForge.SF_Utils._ExitFunction(cstThisSub)
  692. Exit Function
  693. Catch:
  694. GoTo Finally
  695. End Function &apos; SFDatabases.SF_Database._PropertyGet
  696. REM -----------------------------------------------------------------------------
  697. Private Function _ReplaceSquareBrackets(ByVal psSql As String) As String
  698. &apos;&apos;&apos; Returns the input SQL command after replacement of square brackets by the table/field names quoting character
  699. Dim sSql As String &apos; Return value
  700. Dim sQuote As String &apos; RDBMS specific table/field surrounding character
  701. Dim sConstQuote As String &apos; Delimiter for string constants in SQL - usually the single quote
  702. Const cstDouble = &quot;&quot;&quot;&quot; : Const cstSingle = &quot;&apos;&quot;
  703. Try:
  704. sQuote = _MetaData.IdentifierQuoteString
  705. sConstQuote = Iif(sQuote = cstSingle, cstDouble, cstSingle)
  706. &apos; Replace the square brackets
  707. sSql = Join(ScriptForge.SF_String.SplitNotQuoted(psSql, &quot;[&quot;, , sConstQuote), sQuote)
  708. sSql = Join(ScriptForge.SF_String.SplitNotQuoted(sSql, &quot;]&quot;, , sConstQuote), sQuote)
  709. Finally:
  710. _ReplaceSquareBrackets = sSql
  711. Exit Function
  712. End Function &apos; SFDatabases.SF_Database._ReplaceSquareBrackets
  713. REM -----------------------------------------------------------------------------
  714. Private Function _Repr() As String
  715. &apos;&apos;&apos; Convert the Database instance to a readable string, typically for debugging purposes (DebugPrint ...)
  716. &apos;&apos;&apos; Args:
  717. &apos;&apos;&apos; Return:
  718. &apos;&apos;&apos; &quot;[DATABASE]: Location (Statusbar)&quot;
  719. _Repr = &quot;[DATABASE]: &quot; &amp; _Location &amp; &quot; (&quot; &amp; _URL &amp; &quot;)&quot;
  720. End Function &apos; SFDatabases.SF_Database._Repr
  721. REM ============================================ END OF SFDATABASES.SF_DATABASE
  722. </script:module>