Komunikacja z bazą danych

Previous  Next



W systemie ANT Studio znaleźć można gotowy moduł SQLArchive służący do komunikacji z bazą danych. Jego przeznaczeniem jest odczyt i zapis trendów, jest specjalizowany do wykonywania jednego zadania. W systemie ANT Studio położono nacisk na łatwość posługiwania się językiem SQL z poziomu skryptu.
Moduł skryptowy, który ma korzystać z SQLowej bazy danych powinien zostać wyposażony w kanał wejściowy typu „sql_result”. Do kanału tego należy zmapować kanał "result" modułu ODBC lub SQLLite.
Do komunikacji z bazą danych wykorzystywane są dwie procedury: antSQLQuery i antSQLCheck zdefiniowane w pakiecie antUtility, który należy dołączyć na początku skryptu poleceniem: package require antUtility

Poniżej przedstawiono krótki skrypt realizujący komunikację z bazą danych wraz z objaśnieniem poszczególnych elementów.
  

package require antUtility
proc startup { } {
    antSetChannelControl sql_result_in save_and_notify
    antSetChannelControl input_in save_and_notify   
}
proc shutdown { } {
}
proc notifyData { channel_name } {
    if { "sql_result_in" == $channel_name } {
        return
    }
    if { "input_in" == $channel_name } {
        set value [ lindex [ antGetChannelValues "input_in" ] 0 ]
        antClearChannelValues "input_in"
        set t [ antTimeToString [ antGetCurrentTime ] ]
        antSQLQuery sql_result_in answer "UPDATE tabela SET wartosc = $value, czas = \'$t\' WHERE nazwa = \'ZeSkryptu\'"
        antSQLCheck answer update
    }
}
 



W procedurze startup tworzone są kolejki obu kanałów wejściowych. Istotne elementy znajdują się w procedurze notifyData:
·      do obsługi zapytań SQL wykorzystywana jest procedura antSQLQuery więc wszelkie dane przesyłane na kanał „sql_result_in” należy ignorować;
·      pobierana jest najświeższa dana z kanału „input_in”, oraz czas systemowy, który konwertowany jest do postaci zrozumiałej dla języka SQL;
·      wywołanie procedury antSQLQuery, wykonującej zapytanie SQL. Jej parametry to kolejno:
o     nazwa kanału prowadzącego do bazy danych (w tym przypadku „sql_result_in”);
o     nazwa zmiennej, która będzie przechowywała odpowiedź (tutaj będzie to zmienna „answer”);
o     zapytanie SQL;
·      po wykonaniu zapytania sprawdzany jest typ odpowiedzi zapisanej w zmiennej. Przy tego rodzaju zapytaniu spodziewamy się odpowiedzi typu „update”. Procedura antSQLCheck sprawdza to i jeśli wystąpią jakieś niezgodności zgłosi błąd.

Powyższy przykład realizuje funkcje zapisu do bazy danych. Poniżej przedstawiono przyklad odczytujący wartości z bazy i wysyłający je na kanały wyjściowe modułu.
  

package require antUtility
proc startup { } {
    antSetChannelControl sql_result_in save_and_notify
    antInstallHook "1 s"
}
proc shutdown { } {
}
proc notifyData { channel_name } {
}
proc timerHook { hook_name } {
    antSQLQuery sql_result_in answer "SELECT nazwa, czas, wartosc FROM tabela WHERE nazwa = \'ZeSkryptu\'"
    antSQLCheck answer result
    antSetChannelValues "output_out" [ lindex $answer(row0) 2 ]
}
 



W skrypcie tym użyte zostało zapytanie SELECT , na które odpowiedź jest typu „result”, co zostało uwzględnione w procedurze antSQLCheck. Ostatnia linijka procedury timerHook realizuje odczyt danych ze zmiennej „answer” i wysyłanie ich na kanał wyjściowy.

Ważnym elementem jest opis struktury zmiennej „answer”. Jest ona tablicą wypełnianą w zależności od potrzeb. Odpowiedzi w języku zapytań SQL mogą należeć do jednego z trzech typów:
·      Rezultat (result) – takich odpowiedzi udziela baza jeśli zadano pytanie zwracające dane (SELECT) i wszystko poszło dobrze (tablica istnieje, posiada odpowiednie kolumny, itd);
·      Zmiana (update) – takich odpowiedzi udziela baza jeśli zapytanie modyfikowało w jakiś sposób bazę danych (INSERT, UPDATE) i nie wystąpił błąd;
·      Błąd – tego rodzaju odpowiedź może pojawić się przy każdego typu zapytaniu, jeśli tylko wystąpi jakaś nieprawidłowość (np. SELECT dotyczy nieistniejącej tabeli, INSERT nie zawiera odpowiedniej liczby kolumn, itd)

Tak więc rodzaj odpowiedzi bazy danych jest najistotniejszą sprawą przy jej opisie. Jest on przechowywany w polu „status”. Może mieć wartość: „error”, „update” lub „result”.
Jeżeli „status” ma wartość „error”, to dodatkowe pole „error” przechowuje opis błędu, przesłany przez bazę danych.
Jeżeli „status” ma wartość „update”, to dodatkowe pole „update_count” zawiera licznik zmian (np. wierszy wstawionych do tabeli, wierszy zmienionych itd).
W przypadku odpowiedzi typu „result”, pól jest więcej:
·      „row_count” zawiera ilość wierszy w odpowiedzi;
·      „coulmn_count” zawiera ilość kolumn;
·      „column_i_name” zawiera nazwę i-tej kolumny w odpowiedzi;
·      „column_i_table_name” zawiera oryginalną nazwę i-tej kolumny w tabeli (inna jeśli użyto klauzuli AS w zapytaniu SQL);
·      „column_i_type” zawiera typ i-tej kolumny w odpowiedzi;
·      „rowi” zawiera dane i-tego wiersza odpowiedzi  w postaci listy TCL;

Tak więc komenda [ lindex $answer(row0) 2 ] z przykładowego skryptu pobiera trzeci element pierwszego wiersza.
Przykładowy skrypt, który odczytuje wszystkie możliwe informacje o tabeli może wyglądać następująco:
  

package require antUtility
proc startup { } {
    antSetChannelControl sql_result_in save_and_notify
    antInstallHook "1 s"
}
proc shutdown { } {
}
proc notifyData { channel_name } {
}
proc timerHook { hook_name } {
    antSQLQuery sql_result_in answer "SELECT * FROM tabela"
    antSQLCheck answer result
    antLog "Wynik zawiera $answer(column_count) kolumn i $answer(row_count) wierszy"
    for { set i 0 } { $i < $answer(column_count) } { incr i } {
        antLog "Kolumna $i: nazwa \"$answer(column_$i\_name)\", nazwa w tabeli \"$answer(column_$i\_table_name)\",typ \"$answer(column_$i\_type)\""
    }
    for { set i 0 } { $i < $answer(row_count) } { incr i } {
        antLog "Wiersz $i: \"$answer(row$i)\""
    }
}
 


Kilka uwag do powyższego przykładu:
·      Nazwy kolumn w tabeli to właściwość nie wspierana przez SQLLite, którego tutaj użyto. W większości baz danych pojawią się tutaj oryginalne nazwy kolumn.
·      Dane z kolumny „czas” wyglądają nietypowo, gdyż są w formacie czasu używanym w skryptach TCL systemu ANT Studio (notacja: „sekundy.ułamki_sekund). Do przekonwertowania ich na czytelny format SQL można użyć funkcji antTimeToString.