Język skryptów SowaSQL
Język skryptów SowaSQL jest wykorzystywany w wielu różnych miejscach systemu. Pozwala na algorytmiczne wyrażenie dowolnego procesu przetwarzania zawartości rekordu. Wynik procesu przetwarzania jest zawsze napisem (zorganizowanym w wiersze), który jest następnie interpretowany przez jeden z elementów systemu:
- program wyświetlania rekordu (klient SOWA-TCP lub WWW) [*.vpr]
- program sortowania kolekcji [*.spr, *.vpr]
- program prezentacji rekordu w zestawieniu [*.zpr]
- program aktualizacji rekordu (przed i po zakończeniu edycji) [*.apr]
- program aktualizacji rekordu po akcji edycyjnej pola (wejście, wyjście) [*.act]
- program operacji na bazie danych [*.ppr]
Język skryptów składnią przypomina typowe języki programowania. Jedynym typem danych dostępnym w tym języku są łańcuchy znaków. Napisy zapisywane są w apostrofach. Pojedyncze znaki można zapisać w postaci \kod i bez żadnych operatorów połączyć z resztą łańcucha w apostrofach. Przykład stałej łańcuchowej:
'Początek'\27'CG'\26'Test'
Podstawowe instrukcje języka skryptów
Struktura każdego programu ma postać:
procedure nazwa1 begin lista_instrukcji end ... begin lista_instrukcji end
Poszczególne elementy lista_instrukcji są separowane spacją lub zmianą wiersza; nie ma żadnego widocznego znaku separatora.
Instrukcja | Interpretacja |
---|---|
PRINT(wartość) | dopisuje wartość do listy wyników programu. |
TRACE(wartość) | służy do wyprowadzenia wartości dla celów testowych. Wartość pojawi się w pliku dziennika serwera albo w oknie programu klienta (zależnie od przeznaczenia skryptu) |
zmienna=wartość | Instrukcja podstawienia. Pierwsze wywołanie instrukcji podstawienia tworzy zmienną. Wszystkie zmienne w ramach jednego programu są globalne (tzn. zmienna ustawiona w programie głównym jest dostępna w procedurze i na odwrót). Nazwa zmiennej jest dowolnym ciągiem liter i znaków '_'. Może także zawierać cyfry, ale nie może się od cyfry rozpoczynać. Wielkość znaków nie ma znaczenia. |
{napis} | Komentarz. |
// napis | Komentarz do końca linii |
IF wartość | Instrukcja warunkowa. Jeżeli wartość jest pustym napisem, to wykonywana jest pierwsza lista_instrukcji, w przeciwnym przypadku - druga. Fragment ELSE lista_instrukcji jest opcjonalny. |
WHILE lista_instrukcji ENDDO | Instrukcja pętli. Lista_instrukcji jest wykonywana tak długo jak wartość jest pustym napisem. |
Jeśli zdefiniowano procedury (podprogramy), można je wywołać w programie głównym wywołując nazwę procedury w liście instrukcji.
Nawigacja w rekordzie logicznym SowaSQL
Pola w rekordzie logicznym SowaSQL mogą tworzyć strukturę hierarchiczną, maksymalnie 5-poziomową. O sposobie umieszczenia pola w hierarchii decydują dwa elementy:
- oznaczenie pola,
- indeks wystąpienia pola.
Oznaczenie pola jest ciągiem maksymalnie 5 znaków, z których każdy identyfikuje grupę pól na kolejnym poziomie hierarchii. Nazwy pól mogą składać się z dowolnych znaków, przy czym rozróżniane są małe i wielkie litery. Każdy kolejny znak nazwy pola określa przynależność pola do odpowiedniej ścieżki hierarchii. Przykładowo pola AA i AB w tak rozumianej hierarchii należą do wspólnej grupy A. Lista nazw pól jest ustalona dla danego katalogu. Listę określa plik katalog.ini definiujący strukturę katalogu.
Przykład 1.
Struktura pól reprezentujących listę autorów i tytuły może mieć postać:
A {grupa reprezentująca oznaczenie odpowiedzialności} AN {pole nazwisko (nazwiska) osoby} AI {pole imię (imiona) osoby} AF {podgrupa reprezentująca rolę osoby} T {grupa reprezentująca tytuły} TR {pole rodzaju tytułu} TT {pole tytułu}
Indeks wystąpienia pola jest ciągiem oddzielonych kropkami liczb, określających położenie pola w poszczególnych poziomach hierarchii. Przykładowo pola AB.1.1 i AB.1.2 są podporządkowane polu A.1, natomiast pole AB.2.1 polu A.2. Pola A.1 i A.2 leżą na jednym (najwyższym) poziomie.
Przykład 2.
Przykładowy rekord zawierający informacje o dwóch autorach może mieć postać:
AN .1.1=Kowalski AI .1.1=Jan AI .1.2=Andrzej AF .1.1=autor AN .2.1=Jasnorzewska AN .2.2=Pawlikowska AI .2.1=Krystyna AF .2.1=red.
Rekord SowaSQL jest widziany przez programistę raczej jako drzewo niż lista pól. Dlatego w językach skryptowych należy stosować polecenia umożliwiające poruszanie się po tak rozumianym drzewie. Wartość bieżącego pola reprezentowana jest zmienną standardową POLE. Jeżeli bieżącego pola nie ma w rekordzie, to wartością zmiennej POLE jest pusty łańcuch (''). Bieżący indeks wystąpienia reprezentowany jest zmienną standardową POZYCJA.
W chwili startu programu skryptu nazwa bieżącego pola i wystąpienie są puste. Do nawigacji w rekordzie służą instrukcje:
Instrukcja | Interpretacja |
---|---|
GRUPA(nazwa) | W wyniku tej operacji atrybuty bieżącego pola zostają umieszczone na stosie. Następnie do nazwy bieżącego pola zostaje dodany parametr nazwa, i zostaje wyszukane pierwsze pole z wystąpieniem pasującym do bieżącego (czyli pierwsze w poddrzewie). Wystąpienie tego pola staje się wystąpieniem bieżącym. W wyniku operacji nadawana jest wartość zmiennej standardowej OK. Jeśli rekord nie zawiera bieżącego pola, to zmienna OK staje się równa 'N', w przeciwnym przypadku jej wartością jest pusty łańcuch (''). |
SKIP | W wyniku tej operacji bieżącym staje się pole następne na tym samym poziomie. Jeśli nie ma takiego pola, to zmienna standardowa OK staje się równa 'N', w przeciwnym przypadku jej wartością jest pusty łańcuch. |
POZYCJA=wystąpienie | Instrukcja ustawia bieżące wystąpienie na podaną wartość (nie ma znaczenia czy pole istnieje czy nie). Wartością wystąpienie jest łańcuch zawierający pełny ciąg oddzielonych kropkami liczb dla pola w bieżącym miejscu hierarchii. Np. jeśl bieżącym polem jest AN, wówczas ustawienie pozycji na drugie wystąpienie pola N (na nazwisko Pawlikowska z przykładu 2) odbywa się przez przypisanie POZYCJA='.2.2' |
BACK | Odtwarza pole bieżące ze stosu (powrót do stanu sprzed instrukcji GRUPA lub OPEN) |
OPEN(rekord) | Przechodzi do wskazanego rekordu SowaSQL. Zazwyczaj skrypt uruchamiany jest już dla otwartego wcześniej rekordu - tym poleceniem można w obrębie skryptu czasowo przełączyć przetwarzanie na inny rekord. |
Przykład 3. Algorytm przechodzący przez wszystkie wypełnione pola z przykładu 2 wygląda następująco:
GRUPA('A') {pętla po kolejnych autorach} while OK {nazwisko (nazwiska)} GRUPA('N') while OK {...} SKIP enddo BACK {imię (imiona)} GRUPA('I') while OK {...} SKIP enddo BACK {funkcja autora} GRUPA('F') while OK {...} SKIP enddo BACK {przechodzimy do kolejnego autora} SKIP enddo BACK
Wyrażenia
Wyrażenia w języku skryptów pozwalają użyć prostych operacji na łańcuchach. Wyrażenia mogą mieć postać:
Wyrażenie | Opis |
---|---|
! wyrażenie | jeśli wartość wyrażenie jest pustym napisem to wynikiem jest 'N', w przeciwnym przypadku '' (pusty łańcuch) |
wyrażenie1 + wyrażenie2 | konkatenacja dwóch łańcuchów |
wyrażenie1 = wyrażenie2 | jeśli wartości wyrażenie1 i wyrażenie2 są identyczne, to wynikiem jest '' (pusty łańcuch), w przeciwnym przypadku 'N' |
wyrażenie1 # wyrażenie2 | jeśli wartości wyrażenie1 i wyrażenie2 są różne, to wynikiem jest '' (pusty łańcuch), w przeciwnym przypadku 'N' |
wyrażenie1 or wyrażenie2 | wynikiem jest '' (pusty łańcuch) tylko wtedy gdy wartość przynajmniej jednego z wyrażeń jest pustym napisem, w przeciwnym przypadku wynikiem jest 'N' |
wyrażenie1 > wyrażenie2 | jeśli wyrażenie1 jest alfabetycznie większe od wyrażenie2, to wynikiem jest '' (pusty łańcuch), w przeciwnym przypadku 'N'. Stosowany jest polski alfabet (z uwzględnieniem znaków diakrytycznych) |
Wyrażeniem jest także nazwa zmiennej (w tym także zmiennej standardowej).
Funkcje i zmienne standardowe
Zmienne standardowe, to zarezerwowane nazwy zmiennych mające specjalne przeznaczenie.
Nazwa zmiennej | Odczyt zmiennej | Zapis zmiennej |
---|---|---|
OK | Wynik poprzedniej operacji GRUPA() lub SKIP. Wartość 'N' oznacza brak grupy lub kolejnego wystąpienia w rekordzie. W przeciwnym razie wartość jest pusta ('') | Tylko do odczytu |
POLE | Pobiera wartość bieżącego pola. | Ustawia nową wartość bieżącego pola (tylko w programach APR, PPR i ACT) |
PIERWOTNE | Pobiera początkową wartość bieżącego pola (przed ew. modyfikacją). | Tylko do odczytu |
POZYCJA | Pobiera bieżący indeks wystąpienia. | Zob. "Nawigacja w rekordzie logicznym SowaSQL" |
REKORD | Numer bieżącego rekordu SOWA-2 | Tylko do odczytu |
HEADER | Parametr przekazywany przy wywołaniu skryptu | Zmienia wartość zmiennej. |
LP | Zmienna globalna, zachowująca swoją wartość przy kolejnych wywołaniach skryptu (wycofywana) | |
EDYTOWALNE | Znacznik edytowalności bieżącego pola (dotyczy tylko skryptów ACT i APR). | Ustawienie wartości EDYTOWALNE='N' zablokuje możliwość edycji bieżącego pola na formularzu |
Funkcje operują zawsze na łańcuch znaków i ich wynikiem jest także łańcuch znaków.
Nazwa i parametry funkcji | Opis | Przykłady / uwagi |
---|---|---|
head(s1, s2) | Zwraca fragment łańcucha s1 poprzedzający s2. Jeśli s1 nie zawiera s2, zwracany jest pusty łańcuch. | |
tail(s1, s2) | Zwraca fragment łańcucha s1 następujący po s2. Jeśli s1 nie zawiera s2, zwracany jest cały łańcuch s1. | |
sep(s1, s2, s3) | Łączy łańcuchy s1 i s3 separatorem s2, o ile s1 i s3 są niepustymi łańcuchami. Jeśli jeden z łańcuchów s1 lub s3 jest pusty, zwracany jest tylko drugi, niepusty łańcuch. | |
reverse(s) | Przestawia pierwszy wyraz na koniec. Jako miejsce podziału w pierwszej kolejności przyjmowany jest pierwszy napotkany przecinek. Jeśli nie ma przecinka, miejscem podziału jest pierwsza spacjia. Warunek specjalny: jeśli s kończy się sekwencją [wartość] - wówczas jest ona zachowywana na miejscu, przestawienie wyrazu dotyczy części łańcucha poprzedzającej tą sekwencję. | |
up(s) | Zamienia wszystkie litery s na wielkie. | |
lo(s) | Zamienia wszystkie litery s na małe. | |
matches(s, pattern) | Sprawdza czy łańcuch s pasuje do wzorca pattern i jeśli tak, zwracany jest pusty łańcuch. Jeśli nie, zwraca wartość 'N'. W składni wzorca występują dwa znaki specjalne: ? - zastępuje dowolny pojedynczy znak | |
position(substr, s) | Sprawdza czy łańcuch s zawiera łańcuch substr. Jeśli tak, zwracana jest pozycja substr w łańcuchu s (indeksy znaków liczone od 1). Jeśli nie, zwracany jest pusty łańcuch. | |
succ(n) | Dodaje 1 do wartości liczbowej wyrażonej w łańcuchu n (zwraca n+1). Jeśli n nie jest liczbą, zwracane jest '0'. | |
add(n1, n2) | Dodaje dwie wartości liczbowe wyrażone w łańcuchach n1 i n2 (zwraca n1+n2). Jeśli n1 lub n2 nie jest liczbą, traktowany jest jako '0'. | |
mul(n1, n2) | Mnoży dwie wartości liczbowe wyrażone w łańcuchach n1 i n2 (zwraca n1*n2). Jeśli n1 lub n2 nie jest liczbą, zwracana jest wartość '0'. | |
div(n1, n2, digits) | Dzieli dwie wartości liczbowe wyrażone w łańcuchach n1 i n2 (operacja n1/n2). W wartości liczbowej wyrażonej w łańcuchu digits określić należy liczbę zwracanych miejsc po przecinku. Jeśli dowolna z wartości nie jest liczbą, albo n2 jest zerem, zwracana jest wartość '-' (minus). Jeśli digits jest liczbą większą od zera, separatorem części ułamkowej jest przecinek. | |
gt(n1, n2) | Porównuje dwie wartości liczbowe wyrażone w łańcuchach n1 i n2. Jeśli n1 lub n2 nie jest liczbą, traktowany jest jako '0'. Jeśli n1 jest większy od n2, zwracany jest pusty łańcuch. W przeciwnym razie zwracana jest 'N'. | |
ge(n1, n2) | Porównuje dwie wartości liczbowe wyrażone w łańcuchach n1 i n2. Jeśli n1 lub n2 nie jest liczbą, traktowany jest jako '0'. Jeśli n1 jest większy lub równy n2, zwracany jest pusty łańcuch. W przeciwnym razie zwracana jest 'N'. | |
replace(line, s1, s2) | Zastępuje w line wszystkie wystąpienia s1 wartością s2. | |
trim(s) | Obustronnie usuwa spacje z wartości s. | |
sort(list, sep) | Sortuje alfabetycznie listę wyrażoną w łańcuchu list, przy założeniu że elementy listy wyznaczone są separatorem sep. Stosowany jest alfabet polski (z uwzględnieniem znaków diakrytycznych') | Np. sort('Książka|Film|Muzyka|','|') = 'Film|Książka|Muzyka|') |
agregate(line, sep) | Agreguje listę kolejnych identyfikatorów w lini do postaci "od - do" (uprzednio je sortując). Identyfikatory rozdzielane są separatorem sep. | Np. agregate('1|2|3|4|5|', '|') = '1-5|' agregate('1|2|3|5|, '|') = '1-3|5|' |
ord(s) | Zwraca kod ASCII pierwszego znaku łańcucha s. | |
chr(n) | Zwraca znak o kodzie ASCII wyrażonym w łańcuchu n. | |
random(n) | Zwraca liczbę losową w przedziale od 0 do wartości liczbowej wyrażonej w łańcuchu n. | |
omit(s) | Zwraca łańcuch z pominięciem znaczników podpól MARC-21. Oznaczenie znacznika podpola zależy od konfiguracji, zwykle jest to ^c gdzie c to znak podpola. | |
subfield(line, c) | Zwraca wartość podpola c w wierszu line, zawierającym pole MARC-21 z oznaczeniami podpól. Jeśli line nie zawiera podpola o kodzie c, zwracany jest pusty łańcuch. | Np. subfield('^aDziady ^cMickiwicz', 'a') = 'Dziady ' |
marker(line) | Zwraca oznaczenie pierwszego podpola w line, zawierającym pole MARC-21 z oznaczeniami podpól. | Np. marker('^aDziady ^cMickiwicz') = 'a' |
mak(line) | Zamienia symbole liter obcych alfabetów MAK na zwykłe | |
uri(s) | Koduje znaki specjalne w łańcuch s, do postaci zgodnej z formatem URI (adresy zasobów w HTTP) | |
utf8(s) | Koduje łańcuch s do postaci w UTF-8. | |
pr(sep, line) | Jeśli łańcuch line jest niepusty, zwraca line poprzedzony wartością sep. | |
ta(line, sep) | Jeśli łańcuch line jest niepusty, zwraca line+sep. | |
form(s, pattern) | Formatuje łańcuch s wg wzorca pattern. Przetwarzane są kolejne znaki łańcucha s wg odpowiadającym ich pozycji znakom z wzorcu pattern. Wzorzec może zawierać znaki 'A' - przepisanie znaku z łańcucha s, oraz '\' (backslash) - pominięcie znaku z łańcucha s. Inne znaki ze wzorca są bezpośrednio przepisywane do wyniku. | Np. form('2012-10-20', '\\\\\AA') = '10' form('123', 'A-A-A') = '1-2-3' |
rform(s, pattern) | Formatuje łańcuch s wg wzorca pattern, przy czym w odróżnieniu od funkcji form - łańcuch s przed rozpoczęciem formatowania jest odwracany (przetwarzanie s odbywa się od końca). | Np. rform('2012-10-20', '\\\AA') = '10' |
date(n) | Zwraca bieżącą datę powiększoną o n dni w formacie RRRRMMDD. Jeśli n jest pustym łańcuchem zwracana jest bieżąca data. | Np. w dniu 2012-10-20: date(2) = '20121022' |
time(n) | Zwraca bieżący czas powiększony o n sekund w formacie GGMMSS. Jeśli n jest pustym łańcuchem zwracany jest bieżący czas. | Np. o godzinie 12:23:02: time(70) = '122412' |
days(d1,d2) | Zwraca liczbę dni między datą d1 i d2. Daty powinny być wyrażone w postaci RRRRMMDD, a d2 być datą późniejszą od d1. | |
cfg(name) | Zwraca parametr katalogu o nazwie name | |
external(line) | Wywołuje niestandardową metodę zdefiniowaną w zewnętrznym module serwera aplikacji lub programie klienta (zob. dalej). | |
first(idf, prefix) | Wartość pierwszego wpisu w indeksie idf+prefix. | |
next(idf, prefix, key) | Wartość następnego wpisu w indeksie idf+prefix, za key (key musi istnieć i jest kontrolowane z uwzględnieniem małych i wielkich liter) | |
find(idf, key) | Znajduje wartość najbliższego wpisu w indeksie idf, począwszy od key (ignorowana jest wielkość liter) | |
last(idf, prefix) | Wartość ostatniego wpisu w indeksie idf+prefix | |
select(record, hard, pattern) | Zwraca listę numerów rekordów, separowanych '|', dla których klucz indeksu jest zgodny z hard i spełnia pattern. | |
references(id) | Zwraca listę numerów rekordów, separowanych '|', które zawierają referencję do id. |
Implementacja rekordu MARC-21 w rekordzie logicznym SowaSQL
Dla rekordów MARC-21 przyjęto następujący model reprezentacji:
Element rekordu MARC21 | Reprezentacja w SowaSQL | Uwagi |
---|---|---|
Leader | L.1 | |
Wskaźnik nr 1 pola xxx | xxxA.1.1.*.1 | * oznacza numer kolejnego wystąpienia pola MARC21 |
Wskaźnik nr 2 pola xxx | xxxB.1.1.*.1 | |
Treść pola xxx | xxxZ.1.1.*.1 |
Wskaźniki oraz treść 001Z, 005Z przechowywane są w polach indeksowanych, natomiast pozostałe elementy w polach nieindeksowanych. W związku z tym system SowaSQL dla każdego rekordu MARC-21 wyznacza dodatkowe pola, które reprezentowane są jako indeksowane. Zawartość tych pól decyduje o budowie poszczególnych indeksów umożliwiających wyszukiwanie danych. Sposób wyliczenia tych pól określa skrypt wykonywany każdorazowo przy zamknięciu rekordu.
W typowym katalogu MARC-21 przechowywane są rekordy bibliograficzne (oznaczone U), rekordy wzorcowe haseł przedmiotowych (oznaczone A) oraz rekordy wzorcowe haseł formalnych (oznaczone C).