Es gibt nur folgende Funktionen
- RedInitDatabase(...)
Öffnet oder erstellt die Datenbanken
- RedCloseDatabase(...)
Schließt die Datenbanken
- RedDatabaseUpdate(...)
Schreib auf beide Datenbanken
- RedDatabaseQuery(...)
Liest von der primären Datenbank
Update v1.03
Neu Funktionen
- RedDatabaseError()
Letzter Fehler auslesen
- RedDatabaseSync(...)
Synconisierung der Datenbanken nach einen Crash
Hinweis:
Ein Query wird nur auf der primären Datenbank ausgeführt.
Sonderfall:
Ein "delete from" muss wohl über ein "DatabaseQuery" ausgeführt werden. Warum weiss ich nicht.
Hier für auch die Funktion "RedDatabaseUpdate" verwenden. Intern wird bei "delete from" statt "DatabaseUpdate" ein "DatabaseQuery" ausgeführt.
Empfehlung:
Die Funktion RedDatabaseSync(...) nicht automatisch ausführen. Es sollte erst von der erhalteten Datenbank nach einen Systemcrash eine Sicherungskopie erstellt werden. Dann erst mit RedInitDatabase(..., createdatabase) die fehlende Datenbank erstellen lassen, die Tabellen anlegen und mit RedDatabaseSync(...) den abgleich durchführen.
Todo:
Umstellung von "Begin, Commit, Rollback" auf "Savepoint ..., Release, Rollback to ..."
Die Funktion "Savepoint" funktioniert irgendwie nicht. Weiss jemand rat?
Code: Alles auswählen
;-TOP
; ***************************************************************************************
; Comment : Redundante SQLite Datenbank
; Author : mk-soft, Germany
; Second Author :
; File : *.pb
; Version : 1.03
; Create : 16.03.2014
; Update : 05.04.2014
EnableExplicit
UseSQLiteDatabase()
Define red_error.s
Procedure RedInitDatabase(db, path1.s, path2.s, filename.s, createdatabase = 0)
Protected db1, db2, filename1.s, filename2.s , filepart.s, extensionpart.s , file, result
; Path korrigieren
If Right(path1, 1) <> "\"
path1 + "\"
EndIf
If Right(path2, 1) <> "\"
path2 + "\"
EndIf
; Datenbanknamen ertsellen
filepart = GetFilePart(filename, #PB_FileSystem_NoExtension)
extensionpart = GetExtensionPart(filename)
filename1 = path1 + filepart + "_1." + extensionpart
filename2 = path2 + filepart + "_2." + extensionpart
If createdatabase
If FileSize(filename1) <= 0
file = CreateFile(#PB_Any, filename1)
If file = 0
ProcedureReturn 0
Else
CloseFile(file)
EndIf
EndIf
If FileSize(filename2) <= 0
file = CreateFile(#PB_Any, filename2)
If file = 0
ProcedureReturn 0
Else
CloseFile(file)
EndIf
EndIf
EndIf
db1 = db
db2 = db + 1
result = 0
; Datenbank 1 öffnen
If OpenDatabase(db1, filename1, "", "", #PB_Database_SQLite)
result + 1
EndIf
; Datenbank 2 öffnen
If OpenDatabase(db2, filename2, "", "", #PB_Database_SQLite)
result + 1
EndIf
ProcedureReturn result
EndProcedure
; ***************************************************************************************
Procedure RedCloseDatabase(db)
Protected db1, db2, result
db1 = db
db2 = db + 1
If IsDatabase(db1)
If CloseDatabase(db1)
result + 1
EndIf
EndIf
If IsDatabase(db2)
If CloseDatabase(db2)
result + 1
EndIf
EndIf
ProcedureReturn result
EndProcedure
; ***************************************************************************************
Procedure RedDatabaseUpdate(db, sql.s, as_query = 0)
Protected db1, db2
Protected begin1, begin2, update1, update2, count1, count2 , rollback1, rollback2, commit1, commit2, result1, result2, delete_from
Shared red_error.s
db1 = db
db2 = db + 1
If IsDatabase(db1) = 0
red_error = "ERROR DATABASE CONNECTION 1"
ProcedureReturn -1
EndIf
If IsDatabase(db2) = 0
red_error = "ERROR DATABASE CONNECTION 2"
ProcedureReturn -2
EndIf
red_error = "OK"
delete_from = FindString(sql, "delete from", 1, #PB_String_NoCase)
Repeat
; Begin 1
begin1 = DatabaseUpdate(db1, "BEGIN")
If begin1 = 0
red_error = "ERROR DATABASE 1: " + DatabaseError()
Break
EndIf
; Begin 2
begin2 = DatabaseUpdate(db2, "BEGIN")
If begin2 = 0
red_error = "ERROR DATABASE 2: " + DatabaseError()
Break
EndIf
; Update 1
If as_query Or delete_from
update1 = DatabaseQuery(db1, sql)
If update1 = 0
red_error = "ERROR DATABASE UPDATE 1: " + DatabaseError()
rollback1 = #True
rollback2 = #True
Break
Else
While NextDatabaseRow(db1)
;
Wend
FinishDatabaseQuery(db1)
count1 = 1
EndIf
Else
update1 = DatabaseUpdate(db1, sql)
If update1 = 0
red_error = "ERROR DATABASE UPDATE 1: " + DatabaseError()
rollback1 = #True
rollback2 = #True
Break
Else
count1 = AffectedDatabaseRows(db1)
EndIf
EndIf
; Update 2
If as_query Or delete_from
update2 = DatabaseQuery(db2, sql)
If update2 = 0
red_error = "ERROR DATABASE UPDATE 2: " + DatabaseError()
rollback1 = #True
rollback2 = #True
Break
Else
While NextDatabaseRow(db2)
;
Wend
FinishDatabaseQuery(db2)
count2 = 1
EndIf
Else
update1 = DatabaseUpdate(db2, sql)
If update1 = 0
red_error = "ERROR DATABASE UPDATE 2: " + DatabaseError()
rollback1 = #True
rollback2 = #True
Break
Else
count2 = AffectedDatabaseRows(db2)
EndIf
EndIf
; Vergleiche Affected Database Rows
If count1 <> count2
rollback1 = #True
rollback2 = #True
red_error = "ERROR DATABASE UPDATE : Redundant data set differently"
Break
EndIf
Until #True
; Commit 1
If Not rollback1
commit1 = DatabaseUpdate(db1, "COMMIT")
If commit1 = 0
red_error = "ERROR DATABASE RELEASE 1: " + DatabaseError()
rollback1 = #True
rollback2 = #True
Else
commit1 = AffectedDatabaseRows(db1)
EndIf
EndIf
; Commit 2
If Not rollback2
commit2 = DatabaseUpdate(db2, "COMMIT")
If commit2 = 0
red_error = "ERROR DATABASE RELEASE 2: " + DatabaseError()
rollback1 = #True
rollback2 = #True
Else
commit2 = AffectedDatabaseRows(db2)
EndIf
EndIf
; Rollback 1
If rollback1
result1 = DatabaseUpdate(db1, "ROLLBACK")
If result1 = 0
red_error = "ERROR DATABASE ROLLBACK 1: " + DatabaseError()
Else
result1 = AffectedDatabaseRows(db1)
EndIf
EndIf
; Rollback 2
If rollback2
result2 = DatabaseUpdate(db2, "ROLLBACK")
If result2 = 0
red_error = "ERROR DATABASE ROLLBACK 2: " + DatabaseError()
Else
result2 = AffectedDatabaseRows(db2)
EndIf
EndIf
If rollback1 Or rollback2
ProcedureReturn -1
Else
ProcedureReturn count1
EndIf
EndProcedure
; ***************************************************************************************
Procedure RedDatabaseQuery(db, sql.s, List result.s())
Protected db0, db1, db2
Protected query, count, result, colum, index, row.s
Shared red_error.s
db1 = db
db2 = db + 1
ClearList(result())
If IsDatabase(db1)
db0 = db1
ElseIf IsDatabase(db2)
db0 = db2
Else
red_error = "ERROR DATABASE CONNECTION"
ProcedureReturn 0
EndIf
red_error = "OK"
Repeat
; Query
query = DatabaseQuery(db0, sql)
If query = 0
red_error = "ERROR DATABASE: " + DatabaseError()
result = -1
Break
Else
colum = DatabaseColumns(db0) - 1
While NextDatabaseRow(db0)
row = ""
For index = 0 To colum
row + #DQUOTE$ + GetDatabaseString(db0, index) + #DQUOTE$
If index < colum
row + ";"
EndIf
Next
AddElement(result())
result() = row
Wend
result = ListSize(result())
EndIf
Until #True
ResetList(result())
ProcedureReturn result
EndProcedure
; ***************************************************************************************
Procedure.s RedDatabaseError()
Shared red_error
ProcedureReturn red_error
EndProcedure
; ***************************************************************************************
Procedure RedDatabaseSync(db, table.s, primary_key.s, modified_date.s = "")
Protected dbx, db1, db2
Protected sql1.s, sql2.s, query1, query2
Protected primary_column, date_column, date1.s, date2.s, result, column, index
Protected Dim columns.s(0)
Protected Dim values.s(0)
Shared red_error.s
db1 = db
db2 = db + 1
If IsDatabase(db1) = 0
red_error = "ERROR DATABASE CONNECTION 1"
ProcedureReturn -1
EndIf
If IsDatabase(db2) = 0
red_error = "ERROR DATABASE CONNECTION 2"
ProcedureReturn -2
EndIf
red_error = "OK"
; Fehlende Datensätze anlegen
For dbx = 1 To 2
If dbx = 1
; Vergleich primären Datenbank zur sekundären Datenbank
db1 = db
db2 = db +1
Else
; Vergleich sekundären Datenbank zur primären Datenbank
db1 = db + 1
db2 = db
EndIf
Repeat
; Step 1
sql1 = "SELECT * FROM " + table + ";"
query1 = DatabaseQuery(db1, sql1)
If query1 = 0
red_error = "ERROR DATABASE SYNC " + db1 + ": " + DatabaseError()
result = -1
Break
Else
column = DatabaseColumns(db1) - 1
Dim columns(column)
Dim values(column)
For index = 0 To column
columns(index) = DatabaseColumnName(db1, index)
Next
primary_column = DatabaseColumnIndex(db1, primary_key)
If primary_column < 0
red_error = "ERROR DATABASE SYNC " + db1 + ": Unknown primary key"
result = -1
FinishDatabaseQuery(db1)
Break
EndIf
While NextDatabaseRow(db1)
For index = 0 To column
values(index) = "'" + GetDatabaseString(db1, index) + "'"
Next
sql2 = "SELECT * FROM " + table + " WHERE " + columns(primary_column) + " = " + values(primary_column) + ";"
query2 = DatabaseQuery(db2, sql2)
If query2 = 0
red_error = "ERROR DATABASE SYNC " + db2 + ": " + DatabaseError()
result = -1
Break 3
EndIf
If NextDatabaseRow(db2)
FinishDatabaseQuery(db2)
Else
FinishDatabaseQuery(db2)
sql2 = "INSERT INTO " + table + " ("
For index = 0 To column
sql2 + columns(index)
If index < column
sql2 + ","
Else
sql2 + ")"
EndIf
Next
sql2 + " VALUES ("
For index = 0 To column
sql2 + values(index)
If index < column
sql2 + ","
Else
sql2 + ")"
EndIf
Next
sql2 + ";"
query2 = DatabaseUpdate(db2, sql2)
If query2 = 0
red_error = "ERROR DATABASE SYNC " + db1 + ": " + DatabaseError()
result = -1
Break 3
Else
result + AffectedDatabaseRows(db2)
EndIf
EndIf
Wend
EndIf
Until #True
Next
If modified_date = ""
ProcedureReturn result
EndIf
; Zeitstämpel überprüfen
For dbx = 1 To 2
If dbx = 1
; Vergleich primären Datenbank zur sekundären Datenbank
db1 = db
db2 = db +1
Else
; Vergleich sekundären Datenbank zur primären Datenbank
db1 = db + 1
db2 = db
EndIf
Repeat
; Step 1
sql1 = "SELECT * FROM " + table + ";"
query1 = DatabaseQuery(db1, sql1)
If query1 = 0
red_error = "ERROR DATABASE SYNC " + db1 + ": " + DatabaseError()
result = -1
Break
Else
column = DatabaseColumns(db1) - 1
Dim columns(column)
Dim values(column)
For index = 0 To column
columns(index) = DatabaseColumnName(db1, index)
Next
primary_column = DatabaseColumnIndex(db1, primary_key)
If primary_column < 0
red_error = "ERROR DATABASE SYNC " + db1 + ": Unknown primary key"
result = -1
FinishDatabaseQuery(db1)
Break
EndIf
date_column = DatabaseColumnIndex(db1, modified_date)
If date_column < 0
red_error = "ERROR DATABASE SYNC " + db1 + ": Unknown modify date column"
result = -1
FinishDatabaseQuery(db1)
Break
EndIf
While NextDatabaseRow(db1)
For index = 0 To column
values(index) = "'" + GetDatabaseString(db1, index) + "'"
Next
sql2 = "SELECT * FROM " + table + " WHERE " + columns(primary_column) + " = " + values(primary_column) + ";"
query2 = DatabaseQuery(db2, sql2)
If query2 = 0
red_error = "ERROR DATABASE SYNC " + db2 + ": " + DatabaseError()
result = -1
Break 3
EndIf
If NextDatabaseRow(db2) = 0
FinishDatabaseQuery(db2)
Else
date1 = values(date_column)
date2 = "'" + GetDatabaseString(db2, date_column) + "'"
FinishDatabaseQuery(db2)
If date1 <= date2
Continue
EndIf
sql2 = "UPDATE " + table + " SET "
For index = 0 To column
If index <> primary_column
sql2 + columns(index) + " = " + values(index)
If index < column
sql2 + ","
EndIf
EndIf
Next
sql2 + " WHERE " + columns(primary_column) + " = " + values(primary_column)
query2 = DatabaseUpdate(db2, sql2)
If query2 = 0
red_error = "ERROR DATABASE SYNC " + db2 + ": " + DatabaseError()
result = -1
Break 3
Else
result + AffectedDatabaseRows(db2)
EndIf
EndIf
Wend
EndIf
Until #True
Next
ProcedureReturn result
EndProcedure
; ***************************************************************************************
;- Test
DisableExplicit
CompilerIf #PB_Compiler_IsMainFile
OpenConsole()
Define db, sql.s, result, card.s , index, value.s,
NewList rows.s()
path.s = GetPathPart(ProgramFilename())
db = RedInitDatabase(0, path, path, "TestDB.sqlite", 1)
sql = "CREATE TABLE IF NOT EXISTS card (card_id VARCHAR(80) PRIMARY KEY, value REAL, date DATETIME);"
result = RedDatabaseUpdate(0, sql, error)
PrintN(RedDatabaseError())
result = RedDatabaseSync(0, "card", "card_id", "date")
Debug result
If result < 0
PrintN(RedDatabaseError())
EndIf
For index = 1000 To 1020
card = "CARD-" + Str(index)
sql = "insert into card (card_id, value, date) values ('" + card + "', 0.0, datetime('now'));"
result = RedDatabaseUpdate(0, sql, error)
;PrintN(error\text)
PrintN(RedDatabaseError())
Next
For index = 1000 To 1020
card = "CARD-" + Str(index)
value = StrF(Random(5000, 100))
sql = "update card set value = " + value + ", date = datetime('now') where card_id = '" + card + "';"
result = RedDatabaseUpdate(0, sql)
PrintN(RedDatabaseError())
Next
sql = "select * from card;"
result = RedDatabaseQuery(0, sql, rows())
If result < 0
PrintN(RedDatabaseError())
EndIf
ForEach rows()
PrintN(rows())
Next
RedCloseDatabase(0)
Input()
CompilerEndIf
FF
