| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- Option Compare Database
- Option Private Module
- Option Explicit
- ' --------------------------------
- ' Structures
- ' --------------------------------
- ' Structure to keep track of "on Update" and "on Delete" clauses
- ' Access does not in all cases execute such queries
- Private Type structEnforce
- foreignTable As String
- foreignFields() As String
- table As String
- refFields() As String
- isUpdate As Boolean
- End Type
- ' keeping "on Update" relations to be complemented after table creation
- Private k() As structEnforce
- Public Sub ExportLinkedTable(ByVal tbl_name As String, ByVal obj_path As String)
- On Error GoTo Err_LinkedTable
-
- Dim tempFilePath As String
- tempFilePath = VCS_File.TempFile()
-
- Dim fso As Object
- Dim OutFile As Object
- Set fso = CreateObject("Scripting.FileSystemObject")
- ' open file for writing with Create=True, Unicode=True (USC-2 Little Endian format)
- VCS_Dir.MkDirIfNotExist obj_path
-
- Set OutFile = fso.CreateTextFile(tempFilePath, overwrite:=True, unicode:=True)
-
- OutFile.Write CurrentDb.TableDefs(tbl_name).name
- OutFile.Write vbCrLf
-
- If InStr(1, CurrentDb.TableDefs(tbl_name).connect, "DATABASE=" & CurrentProject.path) Then
- 'change to relatave path
- Dim connect() As String
- connect = Split(CurrentDb.TableDefs(tbl_name).connect, CurrentProject.path)
- OutFile.Write connect(0) & "." & connect(1)
- Else
- OutFile.Write CurrentDb.TableDefs(tbl_name).connect
- End If
-
- OutFile.Write vbCrLf
- OutFile.Write CurrentDb.TableDefs(tbl_name).SourceTableName
- OutFile.Write vbCrLf
-
- Dim Db As DAO.Database
- Set Db = CurrentDb
- Dim td As DAO.TableDef
- Set td = Db.TableDefs(tbl_name)
- Dim idx As DAO.Index
-
- For Each idx In td.Indexes
- If idx.Primary Then
- OutFile.Write Right$(idx.Fields, Len(idx.Fields) - 1)
- OutFile.Write vbCrLf
- End If
- Next
-
- Err_LinkedTable_Fin:
- On Error Resume Next
- OutFile.Close
- 'save files as .odbc
- Dim path As String
- path = obj_path & VCS_IE_Functions.to_filename(tbl_name) & ".LNKD"
- VCS_File.ConvertUcs2Utf8 tempFilePath, path
-
- logger "ExportLinkedTable", "DEBUG", "LinkedTable " & tbl_name & " exported to " & path
-
- Exit Sub
-
- Err_LinkedTable:
- OutFile.Close
- logger "ImportLinkedTable", "CRITICAL", "ERROR: IMPORT LINKED TABLE: " & err.Description
- Call err.Raise(60000, "Critical error", "Critical error occured, see the log file for more informations")
- Resume Err_LinkedTable_Fin
- End Sub
- ' This requires Microsoft ADO Ext. 2.x for DLL and Security
- ' See reference: https://social.msdn.microsoft.com/Forums/office/en-US/883087ba-2c25-4571-bd3c-706061466a11/how-can-i-programmatically-access-scale-property-of-a-decimal-data-type-field?forum=accessdev
- Private Function formatDecimal(ByVal tableName As String, ByVal fieldName As String) As String
- Dim cnn As ADODB.Connection
- Dim cat As ADOX.Catalog
- Dim col As ADOX.Column
-
- Set cnn = New ADODB.Connection
- Set cat = New ADOX.Catalog
-
- Set cnn = CurrentProject.Connection
- Set cat.ActiveConnection = cnn
- Set col = cat.Tables(tableName).Columns(fieldName)
- formatDecimal = "(" & col.Precision & ", " & col.NumericScale & ")"
- Set col = Nothing
- Set cat = Nothing
- Set cnn = Nothing
- End Function
- ' Save a Table Definition as SQL statement
- Public Sub ExportTableDef(Db As DAO.Database, td As DAO.TableDef, ByVal tableName As String, _
- ByVal directory As String)
- Dim filename As String
- filename = directory & tableName & ".sql"
- Dim sql As String
- Dim fieldAttributeSql As String
- Dim idx As DAO.Index
- Dim fi As DAO.Field
- Dim fso As Object
- Dim OutFile As Object
- Dim ff As Object
-
- 'Debug.Print tableName
- Set fso = CreateObject("Scripting.FileSystemObject")
- Set OutFile = fso.CreateTextFile(filename, overwrite:=True, unicode:=False)
-
- sql = "CREATE TABLE " & strName(tableName) & " (" & vbCrLf
-
- For Each fi In td.Fields
- sql = sql & " " & strName(fi.name) & " "
- If (fi.Attributes And dbAutoIncrField) Then
- sql = sql & "AUTOINCREMENT"
- Else
- sql = sql & strType(fi.Type) & " "
- End If
- Select Case fi.Type
- Case dbText, dbVarBinary
- sql = sql & "(" & fi.Size & ")"
- Case dbDecimal
- sql = sql & formatDecimal(tableName, fi.name)
- Case Else
- End Select
- For Each idx In td.Indexes
- fieldAttributeSql = vbNullString
- If idx.Fields.count = 1 And idx.Fields(0).name = fi.name Then
- If idx.Primary Then fieldAttributeSql = fieldAttributeSql & " PRIMARY KEY "
- If idx.Unique Then fieldAttributeSql = fieldAttributeSql & " UNIQUE "
- If idx.Required Then fieldAttributeSql = fieldAttributeSql & " NOT NULL "
- If idx.Foreign Then
- Set ff = idx.Fields
- fieldAttributeSql = fieldAttributeSql & formatReferences(Db, ff, tableName)
- End If
- If Len(fieldAttributeSql) > 0 Then fieldAttributeSql = " CONSTRAINT " & strName(idx.name) & fieldAttributeSql
- End If
- sql = sql & fieldAttributeSql
- Next
- sql = sql & "," & vbCrLf
- Next
- sql = Left$(sql, Len(sql) - 3) ' strip off last comma and crlf
-
- Dim constraintSql As String
- For Each idx In td.Indexes
- If idx.Fields.count > 1 Then
- If Len(constraintSql) = 0 Then constraintSql = constraintSql & " CONSTRAINT "
- If idx.Primary Then constraintSql = constraintSql & formatConstraint("PRIMARY KEY", idx)
- If Not idx.Foreign Then
- If Len(constraintSql) > 0 Then
- sql = sql & "," & vbCrLf & " " & constraintSql
- sql = sql & formatReferences(Db, idx.Fields, tableName)
- End If
- End If
- End If
- Next
- sql = sql & vbCrLf & ")"
- 'Debug.Print sql
- OutFile.WriteLine sql
-
- OutFile.Close
-
- 'exort Data Macros
- VCS_DataMacro.ExportDataMacros tableName, directory
-
- logger "ExportTableDef", "DEBUG", "TblDef '" & tableName & "' exported to " & filename
-
- End Sub
- Private Function formatReferences(Db As DAO.Database, ff As Object, _
- ByVal tableName As String) As String
- Dim rel As DAO.Relation
- Dim sql As String
- Dim f As DAO.Field
-
- For Each rel In Db.Relations
- If (rel.foreignTable = tableName) Then
- If FieldsIdentical(ff, rel.Fields) Then
- sql = " REFERENCES "
- sql = sql & strName(rel.table) & " ("
- For Each f In rel.Fields
- sql = sql & strName(f.name) & ","
- Next
- sql = Left$(sql, Len(sql) - 1) & ")"
- If rel.Attributes And dbRelationUpdateCascade Then
- sql = sql + " ON UPDATE CASCADE "
- End If
- If rel.Attributes And dbRelationDeleteCascade Then
- sql = sql + " ON DELETE CASCADE "
- End If
- Exit For
- End If
- End If
- Next
-
- formatReferences = sql
- End Function
- Private Function formatConstraint(ByVal keyw As String, ByVal idx As DAO.Index) As String
- Dim sql As String
- Dim fi As DAO.Field
-
- sql = strName(idx.name) & " " & keyw & " ("
- For Each fi In idx.Fields
- sql = sql & strName(fi.name) & ", "
- Next
- sql = Left$(sql, Len(sql) - 2) & ")" 'strip off last comma and close brackets
-
- 'return value
- formatConstraint = sql
- End Function
- Private Function strName(ByVal s As String) As String
- strName = "[" & s & "]"
- End Function
- Private Function strType(ByVal i As Integer) As String
- Select Case i
- Case dbLongBinary
- strType = "LONGBINARY"
- Case dbBinary
- strType = "BINARY"
- Case dbBoolean
- strType = "BIT"
- Case dbAutoIncrField
- strType = "COUNTER"
- Case dbCurrency
- strType = "CURRENCY"
- Case dbDate, dbTime
- strType = "DATETIME"
- Case dbGUID
- strType = "GUID"
- Case dbMemo
- strType = "LONGTEXT"
- Case dbDouble
- strType = "DOUBLE"
- Case dbSingle
- strType = "SINGLE"
- Case dbByte
- strType = "BYTE"
- Case dbInteger
- strType = "SHORT"
- Case dbLong
- strType = "LONG"
- Case dbNumeric
- strType = "NUMERIC"
- Case dbText
- strType = "VARCHAR"
- Case dbDecimal
- strType = "DECIMAL"
- Case Else
- strType = "VARCHAR"
- End Select
- End Function
- Private Function FieldsIdentical(ff As Object, gg As Object) As Boolean
- Dim f As DAO.Field
- If ff.count <> gg.count Then
- FieldsIdentical = False
- Exit Function
- End If
- For Each f In ff
- If Not FieldInFields(f, gg) Then
- FieldsIdentical = False
- Exit Function
- End If
- Next
-
- FieldsIdentical = True
- End Function
- Private Function FieldInFields(fi As DAO.Field, ff As DAO.Fields) As Boolean
- Dim f As DAO.Field
- For Each f In ff
- If f.name = fi.name Then
- FieldInFields = True
- Exit Function
- End If
- Next
-
- FieldInFields = False
- End Function
- ' Determine if a table or exists.
- ' based on sample code of support.microsoftcom
- ' ARGUMENTS:
- ' TName: The name of a table or query.
- '
- ' RETURNS: True (it exists) or False (it does not exist).
- Private Function TableExists(ByVal TName As String) As Boolean
- Dim Db As DAO.Database
- Dim Found As Boolean
- Dim test As String
-
- Const NAME_NOT_IN_COLLECTION As Integer = 3265
-
- ' Assume the table or query does not exist.
- Found = False
- Set Db = CurrentDb()
-
- ' Trap for any errors.
- On Error Resume Next
-
- ' See if the name is in the Tables collection.
- test = Db.TableDefs(TName).name
- If err.number <> NAME_NOT_IN_COLLECTION Then Found = True
-
- ' Reset the error variable.
- err = 0
-
- TableExists = Found
- End Function
- ' Build SQL to export `tbl_name` sorted by each field from first to last
- Private Function TableExportSql(ByVal tbl_name As String) As String
- Dim rs As Object ' DAO.Recordset
- Dim fieldObj As Object ' DAO.Field
- Dim sb() As String, count As Integer
- Set rs = CurrentDb.OpenRecordset(tbl_name)
-
- sb = VCS_String.Sb_Init()
- VCS_String.Sb_Append sb, "SELECT "
-
- count = 0
- For Each fieldObj In rs.Fields
- If count > 0 Then VCS_String.Sb_Append sb, ", "
- VCS_String.Sb_Append sb, "[" & fieldObj.name & "]"
- count = count + 1
- Next
-
- VCS_String.Sb_Append sb, " FROM [" & tbl_name & "] ORDER BY "
-
- count = 0
- For Each fieldObj In rs.Fields
- DoEvents
- If count > 0 Then VCS_String.Sb_Append sb, ", "
- VCS_String.Sb_Append sb, "[" & fieldObj.name & "]"
- count = count + 1
- Next
- TableExportSql = VCS_String.Sb_Get(sb)
- End Function
- ' Export the lookup table `tblName` to `source\tables`.
- Public Sub ExportTableData(ByVal tbl_name As String, ByVal obj_path As String)
- Dim fso As Object
- Dim OutFile As Object
- Dim rs As DAO.Recordset ' DAO.Recordset
- Dim fieldObj As Object ' DAO.Field
- Dim c As Long, value As Variant
-
- ' Checks first
- If Not TableExists(tbl_name) Then
- logger "ExportTableData", "ERROR", "Table " & tbl_name & " missing"
- Exit Sub
- End If
-
- Set rs = CurrentDb.OpenRecordset(TableExportSql(tbl_name))
- If rs.RecordCount = 0 Then
- 'why is this an error? Debug.Print "Error: Table " & tbl_name & " empty"
- rs.Close
- Exit Sub
- End If
- Set fso = CreateObject("Scripting.FileSystemObject")
- ' open file for writing with Create=True, Unicode=True (USC-2 Little Endian format)
- VCS_Dir.MkDirIfNotExist obj_path
- Dim tempFileName As String
- tempFileName = VCS_File.TempFile()
- Set OutFile = fso.CreateTextFile(tempFileName, overwrite:=True, unicode:=True)
- c = 0
- For Each fieldObj In rs.Fields
- If c <> 0 Then OutFile.Write vbTab
- c = c + 1
- OutFile.Write fieldObj.name
- Next
- OutFile.Write vbCrLf
- rs.MoveFirst
- Do Until rs.EOF
- c = 0
- For Each fieldObj In rs.Fields
- DoEvents
- If c <> 0 Then OutFile.Write vbTab
- c = c + 1
- value = rs(fieldObj.name)
- If IsNull(value) Then
- value = vbNullString
- Else
- value = Replace(value, "\", "\\")
- value = Replace(value, vbCrLf, "\n")
- value = Replace(value, vbCr, "\n")
- value = Replace(value, vbLf, "\n")
- value = Replace(value, vbTab, "\t")
- End If
- OutFile.Write value
- Next
- OutFile.Write vbCrLf
- rs.MoveNext
- Loop
- rs.Close
- OutFile.Close
-
- Dim path As String
- path = obj_path & VCS_IE_Functions.to_filename(tbl_name) & ".txt"
- VCS_File.ConvertUcs2Utf8 tempFileName, path
- logger "ExportTableData", "DEBUG", "Data from '" & tbl_name & "' exported to " & path
- fso.DeleteFile tempFileName
- End Sub
- ' Kill Table if Exists
- Private Sub KillTable(ByVal tblName As String, Db As Object)
- If TableExists(tblName) Then
- Db.execute "DROP TABLE [" & tblName & "]"
- End If
- End Sub
- Public Sub ImportLinkedTable(ByVal tblName As String, ByRef obj_path As String)
- Dim Db As DAO.Database
- Dim fso As Object
- Dim InFile As Object
-
- Set Db = CurrentDb
- Set fso = CreateObject("Scripting.FileSystemObject")
-
- Dim tempFilePath As String
- tempFilePath = VCS_File.TempFile()
-
- ConvertUtf8Ucs2 obj_path & tblName & ".LNKD", tempFilePath
-
- ' open file for reading with Create=False, Unicode=True (USC-2 Little Endian format)
- Set InFile = fso.OpenTextFile(tempFilePath, iomode:=ForReading, create:=False, Format:=TristateTrue)
-
- On Error GoTo err_notable:
- DoCmd.DeleteObject acTable, tblName
-
- GoTo err_notable_fin
-
- err_notable:
- err.Clear
- Resume err_notable_fin
-
- err_notable_fin:
- On Error GoTo Err_CreateLinkedTable:
-
- Dim td As DAO.TableDef
- Set td = Db.CreateTableDef(InFile.readline())
-
- Dim connect As String
- connect = InFile.readline()
- If InStr(1, connect, "DATABASE=.\") Then 'replace relative path with literal path
- connect = Replace(connect, "DATABASE=.\", "DATABASE=" & CurrentProject.path & "\")
- End If
- td.connect = connect
-
- td.SourceTableName = InFile.readline()
- Db.TableDefs.Append td
-
- GoTo Err_CreateLinkedTable_Fin
-
- Err_CreateLinkedTable:
- logger "ImportLinkedTable", "CRITICAL", "ERROR: IMPORT LINKED TABLE: " & err.Description
- Call err.Raise(60000, "Critical error", "Critical error occured, see the log file for more informations")
- Resume Err_CreateLinkedTable_Fin
-
- Err_CreateLinkedTable_Fin:
- 'this will throw errors if a primary key already exists or the table is linked to an access database table
- 'will also error out if no pk is present
- On Error GoTo Err_LinkPK_Fin:
-
- Dim Fields As String
- Fields = InFile.readline()
- Dim Field As Variant
- Dim sql As String
- sql = "CREATE INDEX __uniqueindex ON " & td.name & " ("
-
- For Each Field In Split(Fields, ";+")
- sql = sql & "[" & Field & "]" & ","
- Next
- 'remove extraneous comma
- sql = Left$(sql, Len(sql) - 1)
-
- sql = sql & ") WITH PRIMARY"
- CurrentDb.execute sql
-
- logger "ImportLinkedTable", "DEBUG", "LinkedTable " & tblName & " improted from " & obj_path & tblName & ".LNKD"
- Err_LinkPK_Fin:
- On Error Resume Next
- InFile.Close
-
- End Sub
- ' Import Table Definition
- Public Sub ImportTableDef(ByVal tblName As String, ByVal directory As String)
- Dim filepath As String
- filepath = directory & tblName & ".sql"
- Dim Db As Object ' DAO.Database
- Dim fso As Object
- Dim InFile As Object
- Dim buf As String
- Dim p As Integer
- Dim p1 As Integer
- Dim strMsg As String
- Dim s As Variant
- Dim n As Integer
- Dim i As Integer
- Dim j As Integer
- Dim tempFileName As String
- tempFileName = VCS_File.TempFile()
- n = -1
- Set fso = CreateObject("Scripting.FileSystemObject")
- VCS_File.ConvertUtf8Ucs2 filepath, tempFileName
-
- ' open file for reading with Create=False, Unicode=True (USC-2 Little Endian format)
- 'Set InFile = fso.OpenTextFile(tempFileName, iomode:=ForReading, create:=False, Format:=TristateTrue)
- Set Db = CurrentDb
-
- KillTable tblName, Db
- buf = ReadFile(filepath, "x-ansi")
-
- ' The following block is needed because "on update" actions may cause problems
- For Each s In Split("UPDATE|DELETE", "|")
-
- p = InStr(buf, "ON " & s & " CASCADE")
- Do While p > 0
- n = n + 1
- ReDim Preserve k(n)
- k(n).table = tblName
- k(n).isUpdate = (s = "UPDATE")
-
- buf = Left$(buf, p - 1) & Mid$(buf, p + 18)
- p = InStrRev(buf, "REFERENCES", p)
- p1 = InStr(p, buf, "(")
- k(n).foreignFields = Split(VCS_String.SubString(p1, buf, "(", ")"), ",")
- k(n).foreignTable = Trim$(Mid$(buf, p + 10, p1 - p - 10))
- p = InStrRev(buf, "CONSTRAINT", p1)
- p1 = InStrRev(buf, "FOREIGN KEY", p1)
- If (p1 > 0) And (p > 0) And (p1 > p) Then
- ' multifield index
- k(n).refFields = Split(VCS_String.SubString(p1, buf, "(", ")"), ",")
- ElseIf p1 = 0 Then
- ' single field
- End If
- p = InStr(p, "ON " & s & " CASCADE", buf)
- Loop
- Next
-
- On Error Resume Next
-
- For i = 0 To n
- strMsg = k(i).table & " to " & k(i).foreignTable
- strMsg = strMsg & "( "
- For j = 0 To UBound(k(i).refFields)
- strMsg = strMsg & k(i).refFields(j) & ", "
- Next j
- strMsg = Left$(strMsg, Len(strMsg) - 2) & ") to ("
- For j = 0 To UBound(k(i).foreignFields)
- strMsg = strMsg & k(i).foreignFields(j) & ", "
- Next j
- strMsg = Left$(strMsg, Len(strMsg) - 2) & ") Check "
- If k(i).isUpdate Then
- strMsg = strMsg & " on update cascade " & vbCrLf
- Else
- strMsg = strMsg & " on delete cascade " & vbCrLf
- End If
- Next
-
- On Error GoTo 0
- Db.execute buf
- 'InFile.Close
-
- If Len(strMsg) > 0 Then
- MsgBox strMsg, vbOKOnly, "Correct manually"
- logger "ImportTableDef", "ERROR", strMsg & " - Correct manually"
- Else
- logger "ImportTableData", "DEBUG", "TableDef '" & tblName & "' imported from " & filepath
- End If
-
- End Sub
- ' Import the lookup table `tblName` from `source\tables`.
- Public Sub ImportTableData(ByVal tblName As String, ByVal obj_path As String)
- Dim Db As Object ' DAO.Database
- Dim rs As Object ' DAO.Recordset
- Dim fieldObj As Object ' DAO.Field
- Dim fso As Object
- Dim InFile As Object
- Dim c As Long, buf As String, Values() As String, value As Variant
- Dim path As String
-
- Set fso = CreateObject("Scripting.FileSystemObject")
-
- Dim tempFileName As String
- tempFileName = VCS_File.TempFile()
-
- path = obj_path & tblName & ".txt"
- VCS_File.ConvertUtf8Ucs2 path, tempFileName
-
- ' open file for reading with Create=False, Unicode=True (USC-2 Little Endian format)
- Set InFile = fso.OpenTextFile(tempFileName, iomode:=ForReading, create:=False, Format:=TristateTrue)
- Set Db = CurrentDb
- Db.execute "DELETE FROM [" & tblName & "]"
- Set rs = Db.OpenRecordset(tblName)
- buf = InFile.readline()
- Do Until InFile.AtEndOfStream
- buf = InFile.readline()
- If Len(Trim$(buf)) > 0 Then
- Values = Split(buf, vbTab)
- c = 0
- rs.AddNew
- For Each fieldObj In rs.Fields
- DoEvents
- value = Values(c)
- If Len(value) = 0 Then
- value = Null
- Else
- value = Replace(value, "\t", vbTab)
- value = Replace(value, "\n", vbCrLf)
- value = Replace(value, "\\", "\")
- End If
- '** correct a bug due to internationalization
- If fieldObj.Type = dbBoolean Then value = CBool(value)
- '**
- rs(fieldObj.name) = value
- c = c + 1
- Next
- rs.update
- End If
- Loop
- rs.Close
- InFile.Close
-
- logger "ImportTableData", "DEBUG", "Table data '" & tblName & "' imported from " & path
- fso.DeleteFile tempFileName
- End Sub
|