OA_Optimizer.bas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. Option Compare Database
  2. Option Explicit
  3. '****
  4. '*
  5. '* Optimizer for Open Access: only import/export objects which were updated since last import/export
  6. '*
  7. '****
  8. ' *** activates the optimizer
  9. Private p_optimizer As Boolean
  10. 'needs_export
  11. Public Const NoExportNeeded = 0
  12. Public Const MissingFiles = 1
  13. Public Const UpdateNeeded = 2
  14. Public Sub activate_optimizer()
  15. p_optimizer = True
  16. End Sub
  17. Public Function optimizer_activated()
  18. optimizer_activated = p_optimizer
  19. End Function
  20. '***
  21. '*** main methods
  22. Public Function needs_export(acType As Integer, name As String) As Integer
  23. ' a file needs to be export if it has been updated since last export, or if its source files are missing
  24. ' returns an integer (see constants)
  25. If Not files_exist_for(acType, name) Then
  26. needs_export = MissingFiles
  27. ElseIf get_last_update_date(acType, name) > get_sources_date() Then
  28. needs_export = UpdateNeeded
  29. Else
  30. needs_export = NoExportNeeded
  31. End If
  32. End Function
  33. Public Function get_last_update_date(ByVal acType As Integer, ByVal name As String)
  34. On Error GoTo err
  35. 'get the date of the last update of an object
  36. Select Case acType
  37. 'case table or query: get [DateUpdate] in MSysObjects
  38. Case acTable
  39. get_last_update_date = DFirst("DateUpdate", "MSysObjects", "([Type]=1 or [Type]=4 or [Type]=6) and [name]=" & Chr(34) & name & Chr(34) & "")
  40. Case acQuery
  41. get_last_update_date = DFirst("DateUpdate", "MSysObjects", "[Type]=5 and [name]=" & Chr(34) & name & Chr(34) & "")
  42. 'MSysObjects is not reliable for other objects,
  43. 'So we used the DateModified property:
  44. Case acForm
  45. get_last_update_date = CurrentProject.AllForms(name).DateModified
  46. Case acReport
  47. get_last_update_date = CurrentProject.AllReports(name).DateModified
  48. Case acMacro
  49. get_last_update_date = CurrentProject.AllMacros(name).DateModified
  50. Case acModule
  51. get_last_update_date = CurrentProject.AllModules(name).DateModified
  52. End Select
  53. Exit Function
  54. err:
  55. Debug.Print "get_last_update_date - erreur - " & acType & ", " & name & ": " & err.Description
  56. get_last_update_date = #1/1/1900#
  57. End Function
  58. '*** displays modified (dirties) objects
  59. Public Function list_to_export(acType As Integer)
  60. ' returns a list (string with ';' separator) of the objects wich will be exported
  61. Dim sources_date As Date
  62. list_to_export = ""
  63. sources_date = get_sources_date()
  64. Dim rs As DAO.Recordset
  65. Set rs = CurrentDb.OpenRecordset("SELECT * FROM MSysObjects WHERE " & msys_type_filter(acType) & ";", _
  66. dbOpenSnapshot)
  67. If rs.RecordCount = 0 Then GoTo emptylist
  68. rs.MoveFirst
  69. Do Until rs.EOF
  70. If Left$(rs![name], 4) <> "MSys" And _
  71. Not rs![name] Like "f_*_data" And _
  72. Left$(rs![name], 1) <> "~" Then
  73. If get_last_update_date(acType, rs![name]) > sources_date Or _
  74. Not files_exist_for(acType, rs![name]) Then
  75. If Len(list_to_export) > 0 Then
  76. list_to_export = list_to_export & ";" & rs![name]
  77. Else
  78. list_to_export = rs![name]
  79. End If
  80. End If
  81. End If
  82. rs.MoveNext
  83. Loop
  84. Exit Function
  85. emptylist:
  86. End Function
  87. Public Function msg_list_to_export() As String
  88. 'returns a formatted text listing all of the objects which were updated since last export of the sources
  89. Dim lstmod, obj_type_split, obj_type_label, obj_type_num As String
  90. Dim obj_type, objname As Variant
  91. msg_list_to_export = ""
  92. For Each obj_type In Split( _
  93. "tables|" & acTable & "," & _
  94. "queries|" & acQuery & "," & _
  95. "forms|" & acForm & "," & _
  96. "reports|" & acReport & "," & _
  97. "macros|" & acMacro & "," & _
  98. "modules|" & acModule _
  99. , "," _
  100. )
  101. obj_type_split = Split(obj_type, "|")
  102. obj_type_label = obj_type_split(0)
  103. obj_type_num = obj_type_split(1)
  104. msg_list_to_export = msg_list_to_export & "> " & UCase(obj_type_label) & ": "
  105. If p_optimizer = False Then
  106. msg_list_to_export = msg_list_to_export & "(All)"
  107. Else
  108. lstmod = list_to_export(CInt(obj_type_num))
  109. If Len(lstmod) > 0 Then
  110. Dim count, total As Integer
  111. count = UBound(Split(lstmod, ";")) + 1
  112. total = DCount("name", "MSysObjects", "[name] not like 'MSys*' and [name] not like '~*' and " & msys_type_filter(obj_type_num))
  113. If count = total Then
  114. msg_list_to_export = msg_list_to_export & "(All)"
  115. Else
  116. If count < 12 Then
  117. For Each objname In Split(lstmod, ";")
  118. msg_list_to_export = msg_list_to_export & objname
  119. msg_list_to_export = msg_list_to_export & ", "
  120. Next objname
  121. msg_list_to_export = Left(msg_list_to_export, Len(msg_list_to_export) - 2)
  122. Else
  123. msg_list_to_export = msg_list_to_export & CStr(count) & " on " & CStr(total)
  124. End If
  125. End If
  126. Else
  127. msg_list_to_export = msg_list_to_export & "(None)"
  128. End If
  129. End If
  130. msg_list_to_export = msg_list_to_export & vbNewLine
  131. Next obj_type
  132. Dim include_tables As String
  133. include_tables = get_include_tables()
  134. If UBound(Split(include_tables, ",")) < 5 Then
  135. msg_list_to_export = msg_list_to_export & "> DATA: " & include_tables & vbNewLine
  136. Else
  137. msg_list_to_export = msg_list_to_export & "> DATA: (more than 5)" & vbNewLine
  138. End If
  139. msg_list_to_export = msg_list_to_export & "> RELATIONS, DB PROPERTIES"
  140. End Function
  141. '******
  142. '*** sources_date is the date of the last export of the sources files
  143. Public Function get_sources_date() As Date
  144. ' get the registered sources date
  145. get_sources_date = CDate(oa_param("sources_date", "01/01/1900 00:00:00"))
  146. End Function
  147. Public Sub update_sources_date()
  148. ' update the sources date with Now()
  149. Dim new_val As String
  150. new_val = CStr(Now)
  151. Call update_oa_param("sources_date", CStr(Now))
  152. logger "update_sources_date", "DEBUG", "Source's date updated to " & new_val
  153. End Sub
  154. '*****
  155. '**** cleans sources or objects after differential import/export
  156. Public Function CleanDirs(Optional ByVal sim As Boolean = False)
  157. ' cleans the directories after a differential export
  158. ' returns a list of the deleted relative file paths (string with '|' separator)
  159. ' if 'sim' is set to True, doesn't process to the delete but still return the list
  160. CleanDirs = ""
  161. Dim source_path As String
  162. source_path = VCS_Dir.ProjectPath() & "source\"
  163. logger "CleanDirs", "INFO", "Optimizer ON: cleans the directories from " & source_path
  164. Dim rsSys As DAO.Recordset
  165. Dim sql As String
  166. sql = "SELECT name, type FROM MSysObjects;"
  167. Set rsSys = CurrentDb.OpenRecordset(sql, dbOpenSnapshot)
  168. Dim subdir, filename, objectname, dirname, short_path As String
  169. Dim obj_type, obj_type_split As Variant
  170. Dim obj_type_num As Integer
  171. Dim oFSO As Scripting.FileSystemObject
  172. Dim oFld As Scripting.Folder
  173. Dim file As Scripting.file
  174. 'Instanciation du FSO
  175. Set oFSO = New Scripting.FileSystemObject
  176. For Each obj_type In Split( _
  177. "tables|" & acTable & "," & _
  178. "tbldef|" & acTable & "," & _
  179. "queries|" & acQuery & "," & _
  180. "forms|" & acForm & "," & _
  181. "reports|" & acReport & "," & _
  182. "macros|" & acMacro & "," & _
  183. "modules|" & acModule _
  184. , "," _
  185. )
  186. obj_type_split = Split(obj_type, "|")
  187. dirname = obj_type_split(0)
  188. obj_type_num = obj_type_split(1)
  189. subdir = source_path & dirname
  190. If Not oFSO.FolderExists(subdir) Then GoTo next_obj_type
  191. Set oFld = oFSO.GetFolder(subdir)
  192. For Each file In oFld.Files
  193. objectname = remove_ext(file.name)
  194. objectname = to_accessname(objectname)
  195. If InStr("[34]", objectname) > 0 Then
  196. logger "CleanDirs", "DEBUG", "> " & short_path & " ignored because of [64] (double quotes)"
  197. GoTo next_file
  198. End If
  199. rsSys.FindFirst (msys_type_filter(obj_type_num) & " AND [name]=" & Chr(34) & objectname & Chr(34) & "")
  200. If rsSys.NoMatch Then
  201. 'object doesn't exist anymore
  202. If Len(CleanDirs) > 0 Then CleanDirs = CleanDirs & "|"
  203. short_path = Replace(file.Path, CurrentProject.Path, ".")
  204. CleanDirs = CleanDirs & short_path
  205. If Not sim Then
  206. oFSO.DeleteFile file
  207. logger "CleanDirs", "DEBUG", "> removed: " & short_path
  208. End If
  209. End If
  210. next_file:
  211. Next file
  212. next_obj_type:
  213. Next obj_type
  214. logger "CleanDirs", "INFO", "> Cleaned: " & CleanDirs
  215. End Function
  216. Public Function files_exist_for(acType As Integer, name As String) As Boolean
  217. 'does the object has its files in sources
  218. Dim source_path As String
  219. source_path = VCS_Dir.ProjectPath() & "source\"
  220. name = to_filename(name)
  221. Select Case acType
  222. Case acForm
  223. files_exist_for = (dir(source_path & "forms\" & name & ".bas") <> "")
  224. Case acReport
  225. files_exist_for = (dir(source_path & "reports\" & name & ".bas") <> "" _
  226. And _
  227. dir(source_path & "reports\" & name & ".pv") <> "")
  228. Case acQuery
  229. files_exist_for = (dir(source_path & "queries\" & name & ".bas") <> "")
  230. Case acTable
  231. files_exist_for = ( _
  232. dir(source_path & "tbldef\" & name & ".sql") <> "" _
  233. Or _
  234. dir(source_path & "tbldef\" & name & ".lnkd") <> "" _
  235. )
  236. Case acMacro
  237. files_exist_for = (dir(source_path & "macros\" & name & ".bas") <> "")
  238. Case acModule
  239. files_exist_for = (dir(source_path & "modules\" & name & ".bas") <> "")
  240. End Select
  241. End Function
  242. Public Function CleanApp(Optional ByVal sim As Boolean = False) As String
  243. ' cleans the directories after a differential export
  244. ' returns a list of the deleted relative file paths (string with '|' separator)
  245. ' if 'sim' is set to True, doesn't process to the delete but still return the list
  246. On Error GoTo err
  247. Dim subdir, filename, objectname, dirname, short_path As String
  248. Dim obj_type, obj_type_split As Variant
  249. Dim obj_type_num As Integer
  250. Dim acType As Integer
  251. CleanApp = ""
  252. logger "CleanApp", "INFO", "Cleans the application objects"
  253. Dim rsSys As DAO.Recordset
  254. Dim sql As String
  255. sql = "SELECT name, type FROM MSysObjects WHERE " & _
  256. "(name not like '~*' and name not like 'MSys*' and name not like 'f_*_Data' and name <> 'USysOpenAccess');"
  257. Set rsSys = CurrentDb.OpenRecordset(sql, dbOpenSnapshot)
  258. On Error GoTo next_record
  259. rsSys.MoveFirst
  260. Do Until rsSys.EOF = True
  261. Select Case rsSys![Type]
  262. Case -32768 'form
  263. acType = acForm
  264. Case -32766 'macro
  265. acType = acMacro
  266. Case -32764 'report
  267. acType = acReport
  268. Case 32761 'module
  269. acType = acModule
  270. Case 1 'local table
  271. acType = acTable
  272. Case 4 'linked table
  273. acType = acTable
  274. Case 5 'queries
  275. acType = acQuery
  276. Case Else
  277. GoTo next_record
  278. End Select
  279. If Not files_exist_for(acType, rsSys![name]) Then
  280. If sim = False Then
  281. logger "CleanApp", "DEBUG", "> remove: " & rsSys![name] & " (" & acType & ")"
  282. DoCmd.DeleteObject acType, rsSys![name]
  283. End If
  284. If Len(CleanApp) > 0 Then CleanApp = CleanApp & "|"
  285. CleanApp = CleanApp & rsSys![name]
  286. End If
  287. next_record:
  288. If err.Number > 0 Then
  289. logger "CleanApp", "ERROR", "Unable to delete " & rsSys![name] & " (" & acType & "): " & err.Description
  290. err.Clear
  291. On Error GoTo next_record
  292. End If
  293. rsSys.MoveNext
  294. Loop
  295. On Error GoTo err
  296. logger "CleanApp", "INFO", "> Cleaned: " & CleanApp
  297. Exit Function
  298. err:
  299. logger "CleanApp", "ERROR", err.Description
  300. End Function