Posts Tagged sql

Kolorowanie składni Oracle SQL w Latex

Ostatnimi czasy musiałem umieścić w dokumencie utworzonym przy użyciu Latex przykłady zapytań dla bazy danych Oracle. W związku z czym sięgnąłem do pakietu listings, który pozwala na odpowiednie wyróżnienie słów kluczowych SQL.

Dokładny sposób włączenia tego pakietu i jego podstawowe parametry można znaleźć we tym wpisie: Kolorowanie składni Javy w systemie LaTeX przygotowanym przez Marcina Stachniuka. Dla Oracle deklaracja może wyglądać tak:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
\documentclass[a4paper,12pt]{article}

\usepackage[T1]{fontenc}
\usepackage[utf8x]{inputenc}

\lstset{language=SQL,
    basicstyle=\small,
    showstringspaces=false,
    tabsize=4,
    inputencoding=utf8x,
    extendedchars=\true,
}

\begin{document}

\begin{lstlisting}
    SELECT MIN(DISTINCT kolumna2)
    FROM table
    WHERE NVL(kolumna, 0) = 0
\end{lstlisting}

\end{document
}

Taki zapis spowoduje wygenerowanie zapytania SQL, gdzie słowa kluczowe oraz niektóre funkcje zostaną wypisane czcionką pogrubioną, pozostałem elementy natomiast zwykłą. Także nie wszystkie elementy Oracle SQL zostają poprawnie zakwalifikowane i część z nich wyświetla się w „zwykły” sposób.

Drugim problem są nazwy funkcji, ja chciałbym aby zostały one wyświetlone pochyłą czcionką a nie pogrubioną.

Najprostszym rozwiązaniem oczywiście byłoby dodanie odpowiednich słów kluczowych zarówno z deklaracji języka, oraz utworzenie odpowiedniej klasy dla funkcji, które byłby wypisywane kursywą. Nie można jednak zmienić przyporządkowania już przypisanych funkcji, także mogę np. wypisać funkcję NVL kursywą, ale już z funkcją MIN mi się to nie uda.

Pozostaje więc zdefiniowanie nowego języka, który będzie zachowywał się tak, jak tego oczekuję. Nie jest to trudne, tym bardziej, że dysonujemy już przecież definicją dla języka SQL, wystarczy tylko odpowiednio ją zmodyfikować.

Najpierw należy odszukujemy definicję języka SQL, w moim przypadku znajduje się ona w tym pliku /usr/share/texmf-texlive/tex/latex/listings/lstlang1.sty:

1393
1394
1395
1396
1397
1398
1399
1400
1401
%%
%% SQL definition (c) 1998 Christian Haul
%%                (c) 2002 Neil Conway
%%                (c) 2002 Robert Frank
%%                (c) 2003 Dirk Jesko
%%
\lst@definelanguage{SQL}%
    {morekeywords={ABSOLUTE,ACTION,ADD,ALLOCATE,ALTER,ARE,AS,ASSERTION,%
       AT,BETWEEN,BIT_LENGTH,BOTH,BY,CASCADE,CASCADED,CASE,CAST,%

Pozostaje teraz zdefiniować własny dialekt, skopiować definicję dialektu SQL oraz ją zmodyfikować. Poniżej moja propozycja:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
\lstdefinelanguage[Oracle]{SQL}%
  {morekeywords={ABSOLUTE,ACTION,ADD,ALLOCATE,ALTER,ARE,AS,ASSERTION,%
      AT,BETWEEN,BIT_LENGTH,BOTH,BY,CASCADE,CASCADED,CASE,CAST,%
      CATALOG,CHAR_LENGTH,CHARACTER_LENGTH,CLUSTER,%
      COLLATION,COLUMN,CONNECT,CONNECTION,CONSTRAINT,%
      CONSTRAINTS,CONVERT,CORRESPONDING,CREATE,CROSS,CURRENT_DATE,%
      CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_USER,DAY,DEALLOCATE,%
      DEC,DEFERRABLE,DEFERED,DESCRIBE,DESCRIPTOR,DIAGNOSTICS,%
      DISCONNECT,DOMAIN,DROP,ELSE,END,EXEC,EXCEPT,EXCEPTION,EXECUTE,%
      EXTERNAL,EXTRACT,FALSE,FIRST,FOREIGN,FROM,FULL,GET,GLOBAL,%
      GRAPHIC,HAVING,HOUR,IDENTITY,IMMEDIATE,INDEX,INITIALLY,INNER,%
      INPUT,INSENSITIVE,INSERT,INTO,INTERSECT,INTERVAL,%
      ISOLATION,JOIN,KEY,LAST,LEADING,LEFT,LEVEL,LIMIT,LOCAL,%
      MATCH,MINUTE,MONTH,NAMES,NATIONAL,NATURAL,NCHAR,NEXT,NO,NOT,NULL,%
      OCTET_LENGTH,ON,ONLY,ORDER,ORDERED,OUTER,OUTPUT,OVERLAPS,%
      PAD,PARTIAL,POSITION,PREPARE,PRESERVE,PRIMARY,PRIOR,READ,%
      RELATIVE,RESTRICT,REVOKE,RIGHT,ROWS,SCROLL,SECOND,SELECT,SESSION,%
      SESSION_USER,SIZE,SPACE,SQLSTATE,SUBSTRING,SYSTEM_USER,%
      TABLE,TEMPORARY,THEN,TIMEZONE_HOUR,%
      TIMEZONE_MINUTE,TRAILING,TRANSACTION,TRANSLATION,%
      TRUE,UNIQUE,UNKNOWN,USAGE,USING,VALUE,VALUES,%
      VARGRAPHIC,VARYING,WHEN,WHERE,WRITE,YEAR,ZONE,%
      AND,ASC,CHECK,COMMIT,DESC,DISTINCT,GROUP,IN,% FF
      LIKE,NUMBER,ROLLBACK,VARCHAR2,% FF
      UNION,UPDATE,% RF
      ALL,ANY,CUBE,CUBE,DEFAULT,DELETE,EXISTS,GRANT,OR,RECURSIVE,% DJ
      ROLE,ROLLUP,SET,SOME,TRIGGER,VIEW},% DJ
   morendkeywords={BIT,BLOB,CHAR,CHARACTER,CLOB,DATE,DECIMAL,FLOAT,% DJ
      INT,INTEGER,NUMERIC,SMALLINT,TIME,TIMESTAMP,VARCHAR},% moved here
   sensitive=false,% DJ
   morecomment=[l]--,%
   morecomment=[s]{/*}{*/},%
   morestring=[d]',%
   morestring=[d]",%

  %% dodatkowe słowa kluczowe dla Oracla oraz definicja funkcji

    morekeywords={SAMPLE,SEED,ROWNUM,SYSDATE,IS,MINUS,WITH,START,%
      PARTITION,OVER,UNBOUNDED,PRECEDING,CURRENT,ROW,FOLLOWING,%
      MODIFY,RENAME,TO,REFERENCES,DISABLE,ENABLE,NOVALIDATE,EXCEPTIONS,%
      TRUNCATE,MERGE,MATCHED,SAVEPOINT,FORCE,OPTION,SEQUENCE,CYCLE,%
      INCREMENT,MINVALUE,MAXVALUE},
    classoffset=1,
    morekeywords={dbms_random,LOWER,UPPER,INITCAP,LPAD,RPAD,LTRIM,%
      RTRIM,TRIM,SUBSTR,SUM,MIN,MAX,INSTR,LENGTH,ROUND,TRUNC,POWER,%
      SQRT,SIGN,ABS,MOD,NULLIF,SIN,COS,TAN,SINH,COSH,TANH,REPLACE,%
      TRANSLATE,TO_CHAR,TO_NUMBER,DECODE,NVL,NVL2,COALESCE,AVG,%
      COLLATE,COUNT,STDDEV,VARIANCE,MEDIAN,MONTHS_BETWEEN,ADD_MONTHS,%
      LAST_DAY,NLSSORT,TO_DATE,GROUPING,RANK,DENSE_RANK,ROW_NUMBER,%
      FIRST_VALUE,LAST_VALUE,LEAD,LAG},
    keywordstyle=\textit,
  }[keywords,comments,strings]%

Oczywiście, każdy będzie musiał dostosować ten zapis pod siebie w zależności od funkcji które są używane, być może o niektórych słowach kluczowych SQL także zapomniałem. Należy pamiętać, że jeżeli jakieś słowo jest wypisywane pogrubionymi literami (a np. powinno być funkcją i chcemy aby zostało zapisane kursywą), to należy sprawdzić czy jest ono zdefiniowane w jakieś wcześniejszej linii. W takim przypadku należy je stamtąd usunąć.

Użycie takiego dialektu wygląda tak:

6
7
8
9
10
11
12
\lstset{language=[Oracle]SQL,
    basicstyle=\small,
    showstringspaces=false,
    tabsize=4,
    inputencoding=utf8x,
    extendedchars=\true,
}

Źródła

Tags: , ,

Logowanie zadawanych zapytań do bazy danych przy użyciu log4jdbc

Duża (jeżeli nie większość) część aplikacji korzysta z bazy danych. Zapisuje oraz pobiera z niej informacje praktycznie na okrągło i co ważniejsze, nie jest w stanie poprawnie funkcjonować bez niej. W przypadku Javy połączenia realizowane z bazą danych są opisane poprzez standard JDBC (Java DataBase Connectivity). Praktycznie wszystkie sterowniki do baz danych relacyjnych opierają się na tym standardzie i realizują opisane w nim funkcje. Ponieważ aplikacje tak silnie zależą od baz danych, często pojawiają się różne problemy związane z ich użyciem. W celu ich analizy przydatne może być możliwość sprawdzenia, jak wyglądają poszczególne zapytania do bazy danych wysłane przez aplikację, łącznie z informacje jakie dane są w nich przesyłane.

Jedną z możliwości sprawdzenia (dokładnego) jak wyglądają poszczególne zapytania do bazy danych, jest użycie specjalnego sterownika pośredniczącego pomiędzy naszą aplikacją a serownikiem bazodanowym. Pośrednik ten umożliwia zalogowanie odpowiedniego zapytania zanim zostanie ono przekazane do sterownika odpowiedzialnego za połączenie się z bazą danych i wykonywania na niej operacji. Takim sterownikiem pośredniczącym jest log4jdbc.

Instalacja biblioteki w aplikacji

Pierwszym krokiem powinno być pobranie odpowiedniej wersji biblioteki ze strony log4jdbc. Należy zwrócić uwagę, że inne wersje są zalecane w zależności od tego, jaka wersja Javy zostanie użyta do uruchomienia aplikacji. W moim przypadku najwłaściwszy był plik log4jdbc4-1.2beta1.jar.

Powyższy plik JAR należy dołączyć do ścieżki aplikacji. Ja w swoim przypadku korzystałem z aplikacji uruchomionej na serwerze aplikacyjnym JBoss, więc skopiowałem ten plik do katalogu [cc]common/lib[/cci]. Pozostał jeszcze tylko restart serwera i biblioteka jest gotowa do użycia.

Inicjalizacja połączenia

Istnieją dwa sposoby inicjalizacja biblioteki: w kodzie aplikacji poprzez wywołanie odpowiedniej klasy oraz poprzez odpowiednie utworzenie łańcucha połączenia z bazą danych w standardzie JDBC.

Inicjalizacja log4jdbc w kodzie aplikacji

Oto przykładowy kod źródłowy, jaki należy użyć w aplikacji :

1
2
3
4
5
6
7
// get connection from datasource
Connection conn = dataSource.getConnection();

// wrap the connection with log4jdbc
conn = new net.sf.log4jdbc.ConnectionSpy(conn);

// now use Connection as normal (but it will be audited by log4jdbc)

Czyli najpierw tworzymy lub pozyskujemy połączenie do bazy danych, a następnie przekazujemy je do instancji klasy ConnectionSpy, która od tej pory będzie pośredniczyć w wywoływaniu odpowiedniego kodu i logowaniu informacji przez bibliotekę log4jdbc.

Inicjalizacja połączenia przy użyciu JDBC

Drugim sposobem jest zdefiniowanie połączenia do bazy danych przy użyciu łańcucha JDBC. Robi się to dodając informację o użyciu biblioteki log4jdbc w łańcuchu definiującym połączenie. Jeżeli podstawowe połączenie z bazą danych wygląda następująco (połączenie z bazą danych MySQL):

jdbc:mysql://localhost:3306/baza_danych

To należy dodać do łańcucha ten ciąg znaków log4jdbc, tak aby wyglądał on następująco:

jdbc:log4jdbc:mysql://localhost:3306/baza_danych

Jako klasę sterownika należy użyć klasy: net.sf.log4jdbc.DriverSpy. Przykładowa konfiguracja źródła danych dla JBossa wygląda następująco:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>bazaDanychDS</jndi-name>
    <connection-url>jdbc:log4jdbc:mysql://localhost:3306/baza_danych</connection-url>
    <driver-class>net.sf.log4jdbc.DriverSpy</driver-class>
    <!-- definicja użytkownika, hasła, innych parametrów potrzebnych do połączenia z bazą danych -->
  </local-tx-datasource>
</datasources>

Biblioteka log4jdbc automatycznie postara się zaincjalizować odpowiednią klasę sterownika dla używanej bazy danych. Pełna list domyślnie używanych klas (w zależności od wybranej bazy danych) znajduje się na stronie domowej tego projektu, poniżej znajduje się wybór kilku najczęściej używanych:

Baza danych Klasa sterownika
Oracle oracle.jdbc.driver.OracleDriver
MySQL com.mysql.jdbc.Driver
PostgresSQL org.postgresql.Driver
HSQLDB org.hsqldb.jdbcDriver

Jeżeli istnieje potrzeba użycia innego sterownika do bazy danych, można te informacje przekazać do biblioteki konfigurując odpowiednią zmienną systemową log4jdbc.drivers:

-Dlog4jdbc.drivers=<driverclass>[,</driverclass><driverclass>...]

Teraz pozostaje uruchomić aplikację i w logach powinny znaleźć się informacje o zadawanych zapytaniach, o wartościach poszczególnych parametrów w zapytaniu (szczególnie pomocne w podczas używania klasy PreparedStatement).

Konfiguracja loggerów

Biblioteka log4jdbc definiuje pięć różnych loggerów odpowiedzialnych za zapisywanie różnych informacji dotyczących komunikacji z bazą danych. Każdy z nich może mieć ustawiony jeden z poziomów logowania: DEBUG, INFO, ERROR, FATAL.

Jeżeli wszystkie będą ustawione na poziom FATAL, to faktycznie log4jdbc zostanie wyłączony. Wtedy też nowo tworzone połączenie nie będą otaczane klasą ConnectionSpy tylko będzie to bezpośrednie połączenie z bazą danych. Taki sposób działania jest przydatny, ponieważ można nawet w środowisku produkcyjnym mieć zdefiniowane połączenie z bazą danych przy użyciu log4jdbc, ale włączać je za pomocą definicji odpowiednich zmiennych, bez potrzeby modyfikowania łańcucha definiującego połączenie.

Znaczenie pozostałych poziomów logowania:

  • ERROR – wypisanie stack trace w logu w przypadku wystąpienia wyjątku SQLException
  • INFO – dodanie wyświetlania poleceń SQL
  • DEBUG – dodanie nazwy klasy oraz numeru linii w której zostało wykonane dane polecenie SQL (powoduje także większy narzut czasowy na zapisanie takie informacji w logu)

Dostępne loggery:

Logger Opis
jdbc.sqlonly logowanie samych poleceń SQL, w przypadku korzystania z klasy PreparedStatement także zostaną podane wartości poszczególnych parametrów
jdbc.sqltiming informacje o czasie wykonywania poszczególnych zapytań SQL
jdbc.audit logowanie wszystkich wywołań JDBC poza wynikami (używane do wyszukiwania problemów z JDBC)
jdbc.resultset logowanie wszystkich wywołań do klasy ResultSet
jdbc.connection logowanie informacji o otwieraniu i zamykaniu połączeń, pomocne w przypadku wystąpienia problemów z wyciekami połączeń

Przykładowa fragment linii poleceń wywołującej JVM w którym są te loggery włączane:

-Djdbc.sqlonly=DEBUG -Djdbc.sqltiming=DEBUG -Djdbc.audit=INFO -Djdbc.connection=INFO

Źródła

Tags: , , , , , , , ,