Wyrażenia pól i funkcje skalarne
Wyrażenie pola oblicza jedną wartość rekordu wynikowego. Występuje na liście SELECT, w warunku RULE oraz jako argument agregatu okna rekordowego. Nie należy go mylić z wyrażeniem strumieniowym klauzuli FROM, które buduje i taktuje cały strumień.
Budowanie wyrażenia
Operandami są literały liczbowe i tekstowe, symbol $ wewnątrz generatora, odwołania do pól oraz wyniki funkcji. Pole można wskazać nazwą, kwalifikowaną nazwą strumienia albo płaskim indeksem, na przykład temperature, src.temperature i src[0]. Dla liczbowej deklaracji a T[N] goła nazwa a oznacza cały wpis tablicowy i nie jest operandem skalarnym; należy podać a[0]…a[N-1]. STRING[N] jest jednym polem tekstowym.
Podstawowe operatory arytmetyczne to +, -, *, / i ^. Nawiasy zmieniają grupowanie. Operatory * i / wiążą mocniej niż + i -, a potęgowanie ^ wiąże najmocniej i jest prawostronnie łączne:
SELECT v*w^2, v^w^2, (v*w)^2 STREAM powers FROM source
Powyższe pola znaczą odpowiednio v*(w^2), v^(w^2) i (v*w)^2. Literał ujemny jest jednym atomem gramatyki: -2^2 oznacza (-2)^2, natomiast -v^2 oznacza -(v^2).
Dla typów całkowitych i wymiernych nieujemna potęga całkowita ma semantykę powtarzanego mnożenia, łącznie z promocją typu i przepełnieniem. Pozostałe przypadki korzystają z obliczenia zmiennoprzecinkowego; wynik nieskończony albo NaN staje się NULL. Operandy tekstowe są niedozwolone.
Wartość NULL jest propagowana przez zwykłą arytmetykę. Dzielenie przez zero daje NULL dla każdego typu liczbowego i nie zatrzymuje dalszego przetwarzania strumienia. Reguły porównań i trójwartościowej logiki warunku RULE opisuje rozdział Warunek logiczny.
⚠️ Ostrzeżenie Po przeplocie
A#Bnie wolno odwoływać się do jego składowych przezA[0],A.pole,A[_]aniA.*. Przeplot ma jeden wspólny schemat; należy użyć nazwy strumienia wynikowego albo odzyskać składową operatorem&lub%. Szczegóły opisuje rozdział Aliasowanie.
Dostępne funkcje skalarne
Jedyną listą nazw i arności wspólną dla kompilatora i ewaluatora jest tabela rqlFunctions.hpp. Nazwy są dopasowywane bez względu na wielkość liter, a w planie zapisywana jest postać kanoniczna. Nieznana funkcja albo błędna arność zatrzymuje kompilację; błąd nie jest odkładany do wykonania.
| Grupa | Funkcje |
|---|---|
| Matematyczne | Sqrt, Ceil, Floor, Abs, round, trunc, sin, cos, exp, tan, log, log2 |
| Obsługa wartości | isnull, null2zero, IsZero, IsNonZero, Length |
| Konwersje | to_integer, to_float, to_double, to_string |
Wszystkie funkcje przyjmują jeden argument wyrażeniowy. Jedynym wyjątkiem jest opcjonalna szerokość pola wynikowego w to_string(wyrażenie : szerokość).
Predykaty i wartości brakujące
isnull(x)zwraca 1 dlaNULLi 0 dla wartości obecnej;null2zero(x)zamieniaNULLna całkowite zero, ale wartość obecną przepuszcza bez zmiany jej typu;IsZero(x)iIsNonZero(x)zwracają całkowite 1 albo 0 dla argumentu liczbowego.
null2zero jest konwersją stratną: po jej wykonaniu nie da się odróżnić pierwotnego braku od rzeczywistego zera. Nie zastępuje bitmapy NULL przechowywanej w pliku .meta.
Długość napisu
Length(x) działa wyłącznie na napisie. Liczy długość rzeczywistej wartości do pierwszego bajtu zerowego, a nie zadeklarowaną szerokość STRING[N]. Dla pola STRING[8] zawierającego alpha wynikiem jest 5. Argument liczbowy jest błędem wykonania.
Konwersje
to_integer, to_float i to_double konwertują wartość liczbową albo tekstową do wskazanego typu. NULL przechodzi bez zmiany. to_integer obcina część ułamkową w stronę zera, nie podłoguje: to_integer(-8/3) daje -2. Wartość zmiennoprzecinkowa, której INTEGER nie pomieści - także NaN i nieskończoność - daje NULL, tak samo jak przepełnienie arytmetyki. Ta sama reguła obejmuje funkcje matematyczne nad polem całkowitym, których wynik wraca do typu argumentu: Sqrt(-4) i log(0) nad polem INTEGER dają NULL. Zakres jest sprawdzany po obcięciu części ułamkowej: dla argumentu typu DOUBLE wartość 2147483647.5 daje 2147483647, natomiast 2147483648.0 daje NULL. Reguła nie kończy się na typach całkowitych: NaN i nieskończoność nie mają przybliżenia wymiernego, więc wartość zmiennoprzecinkowa zapisywana do pola RATIONAL daje NULL tak samo jak w INTEGER, a nie zero.
to_string tworzy pole tekstowe. Bez drugiego członu jego szerokość wynosi 32 bajty; postać to_string(x : N) deklaruje N bajtów. Separatorem jest dwukropek, ponieważ przecinek rozdziela pola listy SELECT:
SELECT to_string(value : 10), Length(label), null2zero(optional) \
STREAM converted FROM source
Szerokość wyniku tekstowego jest ustalana po rozwiązaniu referencji do pól, dlatego czyste przepisanie STRING[N] i konkatenacja tekstowa zachowują poprawny deskryptor. Typ całego wyrażenia, także liczbowego, wyznacza compiler::inferFieldShapes() po rozwiązaniu odwołań do pól; reguły opisuje rozdział Równanie typów w górę.
Zadeklarowana szerokość jest własnością pola, a nie skutkiem konkretnego przebiegu kompilacji. Obowiązuje także wtedy, gdy cały argument jest stały: to_string(42 : 16) daje STRING[16], a nie STRING[2]. Upraszczanie wyrażeń zwija argument pod wywołaniem, ale samego to_string nie usuwa - inaczej deklaracja znikałaby razem z programem przy ponownej kompilacji planu, czyli po zapytaniu ad hoc (xqry -a), które kompiluje żywy plan drugi raz.
sin, cos i exp
Wszystkie trzy funkcje przyjmują argument liczbowy i zwracają DOUBLE, niezależnie od typu argumentu. sin i cos interpretują kąt w radianach. Na przykład dla pola k typu INTEGER wyrażenia sin(k), cos(k) i exp(k) dają pola typu DOUBLE; wynik nie traci części ułamkowej. NULL na wejściu daje NULL, a wynik niefinitywny (np. exp(1000)) również daje NULL bez zatrzymania strumienia.
Wyjątkiem jest argument typu RATIONAL: kompilator go odrzuca i wymaga jawnego to_double - tak samo jak dla Sqrt, patrz rozdział niżej.
Zmiana typu sin i cos względem starszego silnika może zmienić deskryptor .desc i układ rekordu: INTEGER oraz FLOAT zajmują 4 bajty, a DOUBLE 8 bajtów. Istniejący artefakt o starym schemacie wymaga ponownego utworzenia albo osobnego strumienia wynikowego.
Funkcje niewymierne nad wartością RATIONAL
Sqrt, sin, cos, exp, tan, log i log2 nie przyjmują argumentu typu RATIONAL - kompilator odrzuca taki zapis kanałem Check result: i podaje obejście. Dotyczy to w praktyce reduktorów strumieniowych nad polami BYTE, INTEGER, UINT i RATIONAL, bo ich wynik ma typ RATIONAL. Reduktory nad FLOAT i DOUBLE zachowują typ wejścia:
SELECT * STREAM m FROM AVG(src)
SELECT Sqrt(m[0]) STREAM o FROM m // odrzucone przy kompilacji
SELECT Sqrt(to_double(m[0])) STREAM o FROM m // poprawnie
Obowiązuje jedna reguła: funkcja o niewymiernej przeciwdziedzinie nad wartością wymierną wymaga jawnego to_double. Ta sama reguła obejmuje warunek reguły (RULE ... WHEN), który kompilator sprawdza osobnym przebiegiem.
Dla Sqrt, tan, log i log2 powodem bramki jest powrót z obliczenia przez double do RATIONAL: wynik zostawał przybliżony ułamkiem o dużym mianowniku (dla argumentu wymiernego 2/1 pierwiastek dawał 19601/13860, a logarytm 2731/3940). W starszej wersji dwa kolejne mnożenia takiego przybliżenia mogły przepełnić 32-bitowy licznik lub mianownik bez sygnału; Sqrt(x)*Sqrt(x)*Sqrt(x) zwracało -4,247 zamiast +2,828. Obecnie arytmetyka wartości pól typu INTEGER i RATIONAL wykrywa przepełnienie i zapisuje NULL, ale nie znosi to wymogu jawnego to_double dla tych funkcji.
Dla sin, cos i exp powód jest inny: te trzy kończą na DOUBLE i nigdy nie wracają do RATIONAL, więc policzyłyby się poprawnie. Ich odrzucenie jest decyzją o kontrakcie języka, podjętą po to, żeby nie trzeba było pamiętać listy wyjątków - jedna reguła zamiast siedmiu osobnych zachowań. Ceną jest to_double w każdym zapytaniu liczącym np. RMS nad reduktorem.
Ograniczenie nie obejmuje pozostałych funkcji ani innych typów argumentu. Zaokrąglenia Floor, Ceil, round i trunc nad RATIONAL są bezpieczne, bo ich wynik jest całkowity, czyli ma mianownik 1, a Abs liczy się wprost na wartości i mianownika nie rusza w ogóle.
Bramka dotyczy wyłącznie pary z RATIONAL i nie zmienia typu wyniku żadnej funkcji. tan, log i log2 nad INTEGER nadal dają INTEGER, czyli obcinają część ułamkową - to strata jawna i zamierzona, nie przepełnienie. Ewentualne doprowadzenie ich do DOUBLE, tak jak sin, cos i exp, zmieniłoby typ pola w .desc, więc jest osobnym zadaniem.
NOTE: Funkcje i propagację typów sprawdzają testy integracyjne
fncall_runtime_case,string_field_passthrough,issue121_isnull,issue128_numeric_to_stringiissue128_string_to_numericoraz testy jednostkoweut_compiler,ut_expevaliut_facctxtsrc.