Skrypt SQL w ADOQuery z wieloma instrukcjami - C++ Builder i Delphi

W tym post-cie, zademonstruję funkcję za pomocą, której można wykonywać skrypty SQL składające się z dowolnej liczby instrukcji.

Funkcję tę można używać w Delphi od wersji 7 do najnowszej (w chwili pisania post-u) czyli Delphi XE 2, oraz w C++ Builder od wersji 6.0 do najnowszej (w chwili pisania post-u) czyli C++ Builder XE2. W zasadzie we wcześniejszych wersjach też, ale testowałem tylko od Delphi 7.0 i od C++ Builder 6.0.

Domyślnie komponenty ADOQuery, ADOCommand nie umożliwiają wykonywania wielu instrukcji SQL rozdzielonych średnikami, można wykonywać tylko jedną instrukcję SQL. Moja funkcja to umożliwia, jest napisana w Delphi 7 i wstawiona do biblioteki DLL, aby w innych językach programowania było można jej też używać.

Po omówieniu funkcji i biblioteki DLL, zademonstruję jak ją używać w programie napisanym w Delphi 7.0 i programie napisanym w C++ Builder 2007 we współpracy z systemem baz danych SQL Server 2008R2 Express. Natomiast w kolejnym post-cie (tego tematu), zademonstruję tę samą bibliotekę DLL, ale kompatybilną ze znakami UNICODE i systemami baz danych obsługującymi znaki UNICODE, wraz z przykładem użycia w Delphi 2010 i C++ Builder 2010.

Biblioteka DLL z funkcją, może być używana z wszystkimi dostępnymi systemami baz danych, które są obsługiwane przez komponent ADOQuery i dla który można skonstruować ConnectionString.

Więcej o konstruowaniu ConnectionString, można poczytać w linku poniżej:
Patrz tutaj (język ang.).


Do tego post-a załączam również kompletne pliki źródłowe biblioteki DLL, pliki źródłowe z przykładem użycia tej biblioteki DLL (z funkcją do wykonywania skryptów SQL) w Delphi 7.0 i C++ Builder 2007.

Ważne jest to, że aby używać tej funkcji zaszytej w bibliotece DLL, nie musicie nic o niej widzieć, poza znajomością jej nagłówka, co więcej wszystkie potrzebne komponenty, w tym TADOQuery i TADOConnection są wewnątrz niej tworzone dynamicznie.

Zaczynamy od kodu biblioteki DLL (wewnątrz funkcja do wykonywania skryptów SQL), następnie omówię jej najważniejsze aspekty, a na koniec przedstawię sposób jej używania w Delphi i C++ Builder.




Co do samej biblioteki DLL, tylko jedna uwaga pominąłem moduł ShareMem, który jest potrzebny gdy używamy biblioteki DLL w wielu językach programowania, a nagłówek funkcji ma w sobie parametry string.
Jak widzicie, ja nie użyłem tego modułu, a biblioteka DLL jest używana w wielu językach programowania.
Jak to możliwe? - po prostu w nagłówku funkcji użyłem zmiennych PChar, zamiast string-ów. Dzięki temu rozwiązałem bardzo poważny problem. Gdy ładujemy bibliotekę DLL dynamicznie i dynamicznie ją zwalniamy, czyli używamy jej w działającym programie w zależności od potrzeby, wtedy moduł ShareMem, nie może być dołączony do biblioteki DLL, ponieważ po zwolnieniu biblioteki DLL wyskakuje wyjątek (JEST TO OGÓLNIE PRZYJĘTY NIENAPRAWIONY BUG, przynajmniej ja tak myślę i tak wyczytałem w wielu źródłach). Zatem moje rozwiązanie z PChar i wieloma językami programowania jest jedyne możliwe (przynajmniej ja tak myślę).

Omówmy teraz samą funkcję, w zasadzie sam nagłówek funkcji oraz kod w ciele funkcji, który odpowiada za dynamiczne używanie komponentów ADO w bibliotekach DLL i wątkach. Resztę ciała funkcji zostawiam Wam do analizy.

Nagłówek:

function Wykonaj_Skrypt_SQL_W_ADOQuery_DELPHI_C(
connection_string_Delphi: PChar;
skryptSQL_Delphi: PChar;
pokazuj_instrukcje_SQL: Bool):Bool; stdcall;


Pierwszy parametr to wspomniany wcześniej ConnectionString,
drugi to skrypt SQL,
trzeci to parametr odpowiedzialny za decyzję, czy podczas wykonywania skryptu mają pojawiać się po kolei w ShowMessage wykonywane instrukcje.
Funkcja zwraca wartość true jeśli skrypt SQL wykonał się w całości, w zasadzie jeśli transakcja wykonała sie w całości, lub false jeśli skrypt SQL nie wykonał się w całości, transakcja zostanie cofnięta.

Ważne jest to, aby pamiętać, że nie wszystkie systemy baz danych odsługują transakcje lub obsługują transakcje częściowo.
Przykład: MySQL, nie obsługuje transakcji DDL, nie ma problemu z DML.
Czyli wykonując skrypt SQL za pomocą mojej funkcji z biblioteki DLL, gdy najpierw utworzymy tabelę, potem do niej wstawimy wierz i kolejny wierz, ale ten drugi wierz wstawimy z takim samym kluczem głównym, moja funkcja wykona ROLLBACK, cofnie transakcje, bo nie można wstawić dwa razy wiersza z tym samym kluczem głównym, ale utworzona tabela pozostanie, gdyż MySQL nie wspiera transakcji DDL, wiersz pierwszy zostanie cofnięty przez ROLLBACK, czyli de facto będziemy mieli pusta tabelę. Problem ten nie wystąpi np. w SQL Server - przy opisanym schemacie postępowania nie będziemy mieli nawet tabeli.

Gdy Wasz skrypt SQL obojętnie jakiego systemu baz danych dotyczy nie zawiera instrukcji odpowiedzialnych za wykonywanie transakcji moja funkcja już o to zadba.
Gdy Wasz skrypt SQL obojętnie jakiego systemu baz danych dotyczy zawiera instrukcje odpowiedzialne za wykonywanie transakcji zostaną one uwzględnione i cały skrypt SQL zostanie opatrzony jedną transakcją nadrzędną.

Poniższy kod jest wymagany, gdy używamy komponentów ADO wewnątrz bibliotek DLL lub wewnątrz wątków (Wątki nie są przedmiotem tego posta).

//KOD wymagany, dla używania komponentów ADO, w bibliotekach DLL i w wątkach.
try
OleInitialize(nil);
komponentADOQuery^ := TADOQuery.Create(nil);
komponentADOQuery^.Connection := connection^;
komponentADOQuery^.Connection.LoginPrompt := False; //opcja
komponentADOQuery^.Connection.ConnectionString:= connection_string^;
//KONIEC KODU WYMAGANEGO

...

//KOD wymagany, dla używania komponentów ADO, w bibliotekach DLL i w wątkach.
komponentADOQuery^.Connection.Close;
//KONIEC KODU WYMAGANEGO

...

//KOD wymagany, dla używania komponentów ADO, w bibliotekach DLL i w wątkach.
finally
try
connection^.Free;
komponentADOQuery^.Free;
except
;
end;
try
OleUninitialize;
except
;
end;
end;
//KONIEC KODU WYMAGANEGO


No i nadszedł czas na użycie opisanego powyżej kodu.

Przykład DELPHI 7.0:




Przykład C++ Builder 2007:




Podaję również przykład ConnectionString dla połączenia z SQL Server 2008R2 Express z instancją bazy danych SQLExpress i autoryzacją ustawioną na autoryzację Windows.
Ten ConnectionString korzysta z baz danych temodb, czyli wszystkie skrypty SQL wykonane przy pomocy mojego programiku, np. tworzące tabele zostaną automatycznie "unieważnione" po restarcie serwera SQL Server 2008R2 Express. Tabela tempdb jest czyszczona przy każdym uruchamianiu serwera baz danych SQL Server.

Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=tempdb;Data Source=.\SQLExpress


Pliki do pobrania:
1. Źródła biblioteki DLL
-pobierz
2. Źródła przykładowego programu w Delphi 7.0 korzystającego z biblioteki DLL
-pobierz
3. Źródła przykładowego programu w C++ Builder 2007 korzystającego z biblioteki DLL
-pobierz
4. Tylko plik wykonywalny przykładowego programu w Delphi 7.0 korzystającego z biblioteki DLL
-pobierz
5. Tylko plik wykonywalny przykładowego programu w C++ Builder 2007 korzystającego z biblioteki DLL
-pobierz
6. Źródła przykładowego programu w Delphi 2007 korzystającego z biblioteki DLL i źródła biblioteki DLL w Delphi 2007
-pobierz



Pomagali: Arkadiusz Dymek, Edek Pienkowski, Zenek Bóbr, Marek Borowski, Piotr Sietnik, Michoo - więcej -> Skrypt SQL w ADOQuery z wieloma instrukcjami