SQLite Server für kleine Anwendungen (Erster Ansatz)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
mk-soft
Beiträge: 3701
Registriert: 24.11.2004 13:12
Wohnort: Germany

SQLite Server für kleine Anwendungen (Erster Ansatz)

Beitrag von mk-soft »

Hier nun nach Anfrage der erste Ansatz SQLite Datenbank im Netzwerk zu verwenden. Hier sind nach etwa 2 Stunden die ersten Schritte dieses umzusetzen...
Als Basis für den Datenaustausch über Netzwerk verwende ich das Modul NetworkData und muss hier geladen werden
Um beim Client die GUI aus Threads zu aktualisieren benötigen wir noch das Modul ThreadToGUI
Kurze Erläuterung:
Mit SendString wird direkt das SQL-Command zum Server gesendet. Mit DataID (DatabaseID) können verschiedene Datenbanken angesprochen werden.
Der Server antwortet bei einen Query der Datenbank mit den gesamten Ergebnis in einer LinkedList im Textformat und einen String, Integer mit der Anzahl der Abfrage.
Bei ein Update antwortet der Server mit einen String und Integer.

Ist nur mal schnell zusammengebastelt. Ich denke aber ausbaufähig.

SQLServer.pb

Code: Alles auswählen

;-TOP
; NetworkData SQLite Server v0.1

Enumeration ;Window
  #Main
EndEnumeration

Enumeration ; Menu
  #Menu
EndEnumeration

Enumeration ; MenuItems
  #MenuExit
EndEnumeration

Enumeration ; Gadgets
  #List
EndEnumeration

Enumeration ; Statusbar
  #Status
EndEnumeration

; Global Variable
Global exit

IncludeFile "Modul_NetworkData.pb"

; ***************************************************************************************

UseModule NetworkData

#DatabaseMax = 100
Global DatabaseFolder.s = GetHomeDirectory()
Global DatabaseFile.s = "Database_"
UseSQLiteDatabase()

; ---------------------------------------------------------------------------------------

Macro AddTextElement(MyList, Text)
  AddElement(MyList) : MyList = Text
EndMacro

; ---------------------------------------------------------------------------------------

Procedure SQL_InitDatabase(DatabaseID)
  Protected fileDB.s, filePB, result
  If DatabaseID > #DatabaseMax
    ProcedureReturn 0
  EndIf
  fileDB = DatabaseFolder + DatabaseFile + DatabaseID + ".db"
  If FileSize(fileDB) < 0
    filePB = CreateFile(#PB_Any, fileDB)
    If filePB
      CloseFile(filePB)
    EndIf
  EndIf
  result = OpenDatabase(DatabaseID, fileDB, "", "")
  ProcedureReturn result
EndProcedure

; ---------------------------------------------------------------------------------------

Procedure SQL_CloseDatabase(DatabaseID)
  If IsDatabase(DatabaseID)
    CloseDatabase(DatabaseID)
  EndIf
EndProcedure

; ---------------------------------------------------------------------------------------

Procedure SQL_Query(*pData.udtDataSet)
  Protected r1, index, columns, count, text.s
  Protected NewList Result.s()
  
  Repeat
    With *pData
      If Not IsDatabase(\DataID)
        If Not SQL_InitDatabase(\DataID)
          SendString(\ConnectionID, \DataID, "Error: Open Database")
          SendInteger(\ConnectionID, \DataID, -1)
          Break
        EndIf
      EndIf
      r1 = DatabaseQuery(\DataID, \String)
      If r1
        columns = DatabaseColumns(\DataID) - 1
        text = ""
        For index = 0 To columns
          text + DatabaseColumnName(\DataID, index) + #TAB$
        Next
        text = RTrim(text, #TAB$)
        AddTextElement(Result(), text)
        While NextDatabaseRow(\DataID)
          count + 1
          text = ""
          For index = 0 To columns
            text + GetDatabaseString(\DataID, index) + #TAB$
          Next
          AddTextElement(Result(), text)
        Wend
        FinishDatabaseQuery(\DataID)
        SendList(\ConnectionID, \DataID, Result())
        SendString(\ConnectionID, \DataID, "Count: " + count)
        SendInteger(\ConnectionID, \DataID, count)
        Break
      Else
        SendString(\ConnectionID, \DataID, DatabaseError())
        SendInteger(\ConnectionID, \DataID, - 2)
        Break
      EndIf
    EndWith
  ForEver
  
EndProcedure

; ---------------------------------------------------------------------------------------

Procedure SQL_Update(*pdata.udtDataSet)
  Protected r1, rows
  
  Repeat
    With *pData
      If Not IsDatabase(\DataID)
        If Not SQL_InitDatabase(\DataID)
          SendString(\ConnectionID, \DataID, "Error: Open Database")
          SendInteger(\ConnectionID, \DataID, -1)
          Break
        EndIf
      EndIf
      r1 = DatabaseUpdate(\DataID, \String)
      If r1
        rows = AffectedDatabaseRows(\DataID)
        SendString(\ConnectionID, \DataID, "Affected Rows: " + rows)
        SendInteger(\ConnectionID, \DataID, rows)
        Break
      Else
        SendString(\ConnectionID, \DataID, DatabaseError())
        SendInteger(\ConnectionID, \DataID, - 2)
        Break
      EndIf  
    EndWith
  ForEver
  
EndProcedure

; ---------------------------------------------------------------------------------------

Procedure SQL_IsQuery(sql.s)
  Protected r1, lSql.s
  
  lSql = LCase(sql)
  r1 = FindString(lSql, "select")
  If r1 > 0 And r1 < 20
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
  
EndProcedure

; ---------------------------------------------------------------------------------------
; ---------------------------------------------------------------------------------------

UnuseModule NetworkData

; ***************************************************************************************

; NewData Callback
Procedure NewData(SEvent, ConnectionID, *NewData.NetworkData::udtDataset)
  
  UseModule NetworkData
  
  Static online
  Protected ip.s, result.s, index
  
  If SEvent = #PB_NetworkEvent_Connect
    online + 1
    ip = IPString(GetClientIP(ConnectionID))
    Logging("Callback: Client connected: IP " + ip)
    ProcedureReturn 0
    
  ElseIf SEvent = #PB_NetworkEvent_Disconnect
    If online > 0
      online - 1
      If online = 0
        For index = 0 To #DatabaseMax
          SQL_CloseDatabase(index)
        Next
      EndIf
    EndIf
    Logging("Callback: Client disconnected ID " + Str(ConnectionID))
    ProcedureReturn 0
    
  ElseIf SEvent = #PB_NetworkEvent_Data
    With *NewData
      ip = IPString(GetClientIP(ConnectionID))
      Logging("Callback: New data from ID " + Str(ConnectionID) + " (" + ip + "): DataID " + Str(\DataID))
      Select \Type
        Case #NetInteger
          
        Case #NetString
          If SQL_IsQuery(\String)
            SQL_Query(*NewData)
          Else
            SQL_Update(*NewData)
          EndIf
          
        Case #NetData
          
        Case #NetList
          
        Case #NetFile
          
      EndSelect
      
      ProcedureReturn 0
    EndWith
    
  EndIf
  
  UnuseModule NetworkData

EndProcedure

Procedure UpdateWindow()
  
  Protected x, y, dx, dy, menu, status
  
  menu = MenuHeight()
  If IsStatusBar(#Status)
    status = StatusBarHeight(#Status)
  Else
    status = 0
  EndIf
  x = 0
  y = 0
  dx = WindowWidth(#Main)
  dy = WindowHeight(#Main) - menu - status
  ResizeGadget(#List, x, y, dx, dy)
  
EndProcedure

; Main
Procedure Main()
  
  Protected event, style, dx, dy
  
  style = #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget
  dx = 640
  dy = 480
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, dx, dy, "SQLite Server v0.1", style)
    
    ; Enable Fullscreen
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      Protected NewCollectionBehaviour
      NewCollectionBehaviour = CocoaMessage(0, WindowID(#Main), "collectionBehavior") | $80
      CocoaMessage(0, WindowID(#Main), "setCollectionBehavior:", NewCollectionBehaviour)
    CompilerEndIf
    
    ; Menu
    CreateMenu(#Menu, WindowID(#Main))
    MenuTitle("Common")
    MenuItem(#MenuExit, "E&xit")
    ; Gadgets
    ListViewGadget(#List, 0, 0, dx, dy)
    
    ; Statusbar
    CreateStatusBar(#Status, WindowID(#Main))
    AddStatusBarField(#PB_Ignore)
    
    UpdateWindow()
    
    NetworkData::BindLogging(#PB_Event_FirstCustomValue, #List)
    ServerID = NetworkData::InitServer(6037, @NewData())
    
    NetworkData::SetDataFolder(GetHomeDirectory())
    
    ; Main Loop
    Repeat
      event = WaitWindowEvent(10)
      Select event
        Case #PB_Event_Menu
          Select EventMenu()
              CompilerIf #PB_Compiler_OS = #PB_OS_MacOS   
              Case #PB_Menu_About
                
              Case #PB_Menu_Preferences
                
              Case #PB_Menu_Quit
                NetworkData::CloseServer(ServerID)
                exit = #True
                
              CompilerEndIf
              
            Case #MenuExit
              NetworkData::CloseServer(ServerID)
              exit = #True
              
          EndSelect
          
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #List
              
          EndSelect
          
        Case #PB_Event_SizeWindow
          Select EventWindow()
            Case #Main
              UpdateWindow()
              
          EndSelect
          
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Main
              NetworkData::CloseServer(ServerID)
              exit = #True
              
          EndSelect
          
      EndSelect
      
    Until exit
    
  EndIf
  
EndProcedure : Main()

End
Zuletzt geändert von mk-soft am 31.12.2016 02:57, insgesamt 1-mal geändert.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
mk-soft
Beiträge: 3701
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: SQLite Server für kleine Anwendungen (Erster Ansatz)

Beitrag von mk-soft »

SQLClient.pb

Code: Alles auswählen

; SQLClient v0.1

Enumeration FormWindow
  #Main
EndEnumeration

Enumeration FormGadget
  #Button_Connect
  #Button_Disconnect
  #Button_Send
  #Button_Clear
  #Editor_Command
  #ListIcon_Result
  #Editor_Status
  #String_Server
  #Spin_DatabaseID
  #Text_DB
  #Text_Host
EndEnumeration

Procedure OpenMain(x = 0, y = 0, width = 940, height = 620)
  OpenWindow(#Main, x, y, width, height, "SQL-Client v0.1", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  CreateStatusBar(0, WindowID(#Main))
  AddStatusBarField(#PB_Ignore)
  StatusBarText(0, 0, "Status")
  ButtonGadget(#Button_Connect, 0, 0, 110, 30, "Connect")
  ButtonGadget(#Button_Disconnect, 110, 0, 110, 30, "Disconnect")
  ButtonGadget(#Button_Send, 220, 0, 110, 30, "Send")
  ButtonGadget(#Button_Clear, 330, 0, 110, 30, "Clear")
  EditorGadget(#Editor_Command, 0, 30, 940, 140)
  ListIconGadget(#ListIcon_Result, 0, 170, 940, 320, "Column 1", 100)
  EditorGadget(#Editor_Status, 0, 490, 940, 106)
  StringGadget(#String_Server, 750, 0, 190, 30, "localhost")
  SpinGadget(#Spin_DatabaseID, 560, 0, 80, 30, 0, 100, #PB_Spin_Numeric)
  TextGadget(#Text_DB, 460, 5, 100, 25, "Database ID")
  TextGadget(#Text_Host, 690, 5, 60, 25, "Host")
EndProcedure

IncludeFile "Modul_NetworkData.pb"
IncludeFile "Modul_ThreadToGUI.pb"

UseModule NetworkData
UseModule ThreadToGUI

#DatabaseID = 0

Global exit

Procedure NewData(SEvent, ConnectionID, *NewData.NetworkData::udtDataset)
  Protected columns, index, text.s
  
  If SEvent = #PB_NetworkEvent_Disconnect
    Logging("Callback: Server disconnected: ID " + Str(ConnectionID))
    exit = 1
    ProcedureReturn 0
  EndIf
  
  With *NewData
    Logging("Callback: New data from ConnectionID " + Str(ConnectionID) + ": DataID " + Str(\DataID))
    Select \Type
      Case #NetInteger
        Logging("Callback: Result = " + Str(\Integer))
        
      Case #NetString
        DoSetGadgetText(#Editor_Status, "")
        DoSetGadgetText(#Editor_Status, \String)
        
      Case #NetData
        
      Case #NetList
        DoClearGadgetItems(#ListIcon_Result)
        DoClearGadgetColumns(#ListIcon_Result)
        If FirstElement(\Text())
          columns = CountString(\Text(), #TAB$)
          For index = 0 To columns
            DoAddGadgetColumn(#ListIcon_Result, index, StringField(\Text(), index + 1, #TAB$), 120)
          Next
          While NextElement(\Text())
            text = ReplaceString(\Text(), #TAB$, #LF$)
            DoAddGadgetItem(#ListIcon_Result, -1, text)
          Wend
          ClearList(\Text())
        EndIf
    EndSelect
    
  EndWith
  
  ProcedureReturn 0
  
EndProcedure

Procedure Main()
  Protected event, ConnectionID, DatabaseID
  
  
  OpenMain()
  If IsWindow(#Main) 
    SetGadgetState(#Spin_DatabaseID, 0)
    BindEventGUI(#PB_Event_FirstCustomValue)
    ; Main Loop
    Repeat
      event = WaitWindowEvent(10)
      Select event
        Case #PB_Event_Menu
          Select EventMenu()
              CompilerIf #PB_Compiler_OS = #PB_OS_MacOS   
              Case #PB_Menu_About
                
              Case #PB_Menu_Preferences
                
              Case #PB_Menu_Quit
                NetworkData::CloseClient(ConnectionID)
                exit = #True
                
              CompilerEndIf
          EndSelect
          
          
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #Button_Connect
              ConnectionID = InitClient(GetGadgetText(#String_Server), 6037, @NewData())
              If ConnectionID
                StatusBarText(0, 0, "Connected")
              Else
                StatusBarText(0, 0, "Error Connection")
              EndIf
            Case #Button_Disconnect
              If ConnectionID
                CloseClient(ConnectionID)
                ConnectionID = 0
                StatusBarText(0, 0, "Disconnected")
              EndIf
              
            Case #Button_Send
              If ConnectionID
                DatabaseID = GetGadgetState(#Spin_DatabaseID)
                SetGadgetText(#Editor_Status, "")
                SendString(ConnectionID, DatabaseID, GetGadgetText(#Editor_Command))
              EndIf
              
            Case #Button_Clear
              SetGadgetText(#Editor_Command, "")
              
          EndSelect
          
        Case #PB_Event_SizeWindow
          Select EventWindow()
            Case #Main
              ;UpdateWindow()
          EndSelect
          
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Main
              NetworkData::CloseClient(ConnectionID)
              exit = #True
          EndSelect
          
      EndSelect
      
    Until exit
    
  EndIf
  
EndProcedure : Main()
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
uweb
Beiträge: 461
Registriert: 13.07.2005 08:39

Re: SQLite Server für kleine Anwendungen (Erster Ansatz)

Beitrag von uweb »

Hallo mk-soft,

nach nur 2 Stunden so etwas zu zaubern wäre für mich unglaublich wenn ich es nicht "live" mitbekommen hätte.


Vielen Dank !

Auch für die verwendeten Module!


Als permanenter Anfänger habe ich natürlich einen anderen Blick auf manche Dinge.
Ich weiß aber auch wo ich stehe. Deswegen soll das keine Kritik sein, sondern nur Vorschläge wie Du den kleinen Leuten wie mir den Zugang erleichern kannst. Die Großen kommen sicher auch sehr gut damit klar.
Es geht also nur um den Unterschied zwischen super-gut und perfekt.


- Doku
Klar wenn man fit ist liest man alles was man wissen will aus dem Quelltext.
Mich müsste man am Händchen nehmen und mir erklären was wo passiert damit ich im Code durchsteige obwohl er offensichtlich gut strukturiert ist. Es liegt nicht am Code sondern daran, dass ich nicht den Überblick habe.

- Threadsafe
"Out of the Box" starte ich den Server und ich bekomme von PB aus Modul_NetworkData.pb die Aufforderung Threadsafe zu aktivieren. Wenn ich das tue passiert aber beim nächsten mal das Selbe. Ok, das konnte ich gerade noch selbst lösen indem ich den Check vor dem IncludeFile eingefügt habe.

Code: Alles auswählen

CompilerIf #PB_Compiler_Thread = 0
  CompilerError "Use Compileroption Threadsafe!"
CompilerEndIf
- Modul_ThreadToGUI.pb hatte noch den Namen ThreadToGUI.pb
Auch das war easy. Einfach umbenannt und damit das Namensschema übernommen.

Nun läuft es reibungslos.
Benutzeravatar
mk-soft
Beiträge: 3701
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: SQLite Server für kleine Anwendungen (Erster Ansatz)

Beitrag von mk-soft »

Danke :wink:

Ist ja der erste Ansatz und noch einiges zu tun.

1. Das Modul NetworkData mit AES-Stream erweitern.
2. Dann das Modul als SQLNetworkData die Funktion umschreiben und erweitern, da die Protokolle von NetworkData schon auch mit sehr vielen und grossen Datenmengen funktionieren.

Aber jetzt erstmal:

Allen ein guten Rutsch ins neue Jahr

:lurk:
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Antworten