Sniffing Interbase Traffic - Felix John COLIBRI. |
- abstract: Comparison example of Interbase Express, BDE and direct API calls in term of network traffic
- key words: Interbase - Ibx - BDE - isc - Interbase API - TCP-IP Sniffer
- software used: Windows XP, Delphi 6, tcp_ip_sniffer
- hardware used: Pentium 1.400Mhz, 256 M ram, 140 G hard disk
- scope: Delphi 1 to 2005, Windows, Linux
- level: Intermediate
- plan:
1 - Introduction Interbase already comes complete with several logging and monitoring tools. However those tools mainly work at the application level. For instance, the
Interbase Express monitor simply displays what Jeff Overcash decided to present: at chosen points, before sending orders to the Interbase Client, the
Interbase Express unit writes some text to the Ibx monitor. The IbxSqlMonitor is a great tool, but its main purpose is to give the Delphi programmer some feedback of the progress of his requests. If you wish to
analyze the traffic in terms of bytes, a lower level monitoring tool like a network sniffer comes handy. In this paper we are going to use the sniffer that we built in the
TCP IP Sniffer paper and will compare, as an example, the traffic generated by using different Interbase components: Ibx, the Bde
and direct API calls to the Interbase Client DLL. Before starting, I want to stress that we simply want to test the performance of the different access components. Our goal is NOT to snoop the meaning of the
data transferred (the salary and stock options of your boss). The user can always encrypt his data, and the packet content will be opaque. We are interested by the volume of information, and will look at the content only to
see what Interbase overhead is included in the packets (schema information and the like). This Interbase content analysis is precisely the benefit of sniffing over simple timing: we can identify the cause of the problem, and
possibly find a remedy.
2 - Creating the Interbase Application 2.1 - The test bed To have a flexible test bed, we are going to build the database and the table
which we want to analyze with a small Interbase Express program. If you are familiar with Ibx programming, you can skip this part. You can also use an existing Database instead of creating a dedicated one like we are doing here.
2.2 - Create the Database We create the Database using the usual CreateDatabase instruction: |
create a new application and call il "p_create_sniffed_database" | | place a tIbDatabase component on your tForm |
| drop a tButton, create its OnClick event, and create the database:
const k_database_disk_path= '\\notebook\c\programs\fr\colibri_utilities\programs\interbase_sniffer\_data\';
k_database_path= 'notebook:c:/programs/fr/colibri_utilities/programs/interbase_sniffer/_data/';
k_database_name= 'ib_sniffer.GDB';
procedure TForm1.create_database_Click(Sender: TObject);
begin IbDatabase1.Close;
f_create_path(g_database_disk_path);
with IbDatabase1, Params do
begin Connected:= False;
DatabaseName:= g_database_path+ k_database_name;
SqlDialect:= 3; Clear;
Add('USER '+ QuotedStr('SYSDBA'));
Add('PASSWORD '+ QuotedStr('masterkey'));
Add('PAGE_SIZE 4096'); CreateDatabase;
end; // IbDatabase1 end; // create_database_Click |
| | compile, run and click Button1 | Please note that:
- we have placed the base on a remote PC. Sniffing TCP IP does NOT work on local bases: the TCP IP stack is not used, and therefore sniffing does not work
- to make sure that the path exists, we called f_create_path which calls
ForceDirectories to build the desired path. The parameter is a Windows parameter
- to create the Database, we used an Interbase connection string, which is not exactly the same as the Windows path
- we chose Interbase Dialect 3, which offers DateTime and huge integers, but you may prefer to stick with Dialect 1. The page size is the standard
one that we use all the time, but changing this value might obviously have some influence on the benchmarking
- we placed all the strings in constants, as you can see by looking at the
.ZIP, the constants are all in an include file which is called by all our projects, to avoid any mis spelling
- we also added in the .ZIP a DROP DATABASE button to be able to restart the whole trial again.
2.3 - Create the Table The table is created with the classic CREATE TABLE request:
CREATE TABLE my_table (field_1 type_1, field_2 type_2, ..., field_n type_n) | In our case:
| drop another tIbDatabase, a tIbTransaction and link those together. Double click on tIbDatabase to open the connection editor, and fill in the
DatabaseName, User, Password, remove the Login checkbox and hit "Test" to check the connection: |
| drop an tIbSql component, link it to IbDatabase1 | |
place a tButton, create its OnClick event, and create a "reasonable" table:
const k_sql_create_table=
'CREATE TABLE '+ k_table_name
+ ' ('
+ ' s_id INTEGER, '
+ ' s_company CHAR(40), '
+ ' s_address CHAR(50), '
+ ' s_contact CHAR(20), '
+ ' s_amount_due DOUBLE PRECISION'
+ ' ) ';
procedure execute_sql(p_sql_query: String);
// -- this procedure will sent any "write" sql request to the remote database
begin
with Form1, write_ib_sql do
begin Close;
with Sql do begin
Clear; Add(p_sql_query);
end; // with Sql Try
write_IbDatabase.Open;
write_IbDatabase.DefaultTransaction.Active:= True;
if write_IbDatabase.DefaultTransaction.InTransaction
then write_IbDatabase.DefaultTransaction.Commit;
write_IbDatabase.DefaultTransaction.StartTransaction;
ExecQuery;
write_IbDatabase.DefaultTransaction.Commit;
display(' ok'); except
on e: Exception do
begin
write_IbDatabase.DefaultTransaction.RollBack;
display(' *** exec '+ e.Message);
end; end;
end; // with IbSql1 end; // execute_sql
procedure TForm1.create_table_Click(Sender: TObject);
begin write_IbDatabase.Close;
write_IbDatabase.DatabaseName:= g_database_path+ k_database_name;
execute_sql(k_sql_create_table); end; // create_Click |
| | compile, run and click Button1 |
Note that:
- we have placed the creation request in a litteral constant, as explained above
- the creation itself has been split in two:
- a general procedure execute_sql, which sends any "write" request to the
Interbase Server (CREATE INDEX, DROP CONSTRAINT, ALTER COLUMN : anything but SELECT )
- the procedure create_table_Click to send the CREATE TABLE request
The generic write request procedure will be used later for populating the table
- in the .ZIP we added a "drop_table_" button to allow several runs.
2.4 - Populate the Table The addition of rows in a table is performed with the INSERT INTO request:
INSERT INTO my_table (field_1, field_2, ... , field_n)
VALUES (value_1, value_2, ... , value_n) | To have a reasonable amount of data available, we automated the generation using random numbers:
- we extracted the client names and adresses from the CUSTOMER table from DBDEMO. Those were saved in a .TXT file
- we load this file in a tStringList
- for each row in our table, we generate a random number from 0 to
tStringList.Count- 1, and this will become our new customer.
The INSERT INTO request itself is built by simply inserting the radom
generated values, and the whole process driven by a classic FOR loop.
Let's first extract the values from the DBDEMO table (or any table of your choice):
- drop another tButton on the tForm, call it "extract_bde" and type the extraction text:
const k_company_list_name= 'company.txt';
k_address_list_name= 'address.txt';
k_contact_list_name= 'contact.txt';
procedure TForm1.extract_bde_Click(Sender: TObject);
var l_c_company_name_list, l_c_address_list, l_c_contact_list: tStringList;
begin l_c_company_name_list:= tStringList.Create;
l_c_address_list:= tStringList.Create;
l_c_contact_list:= tStringList.Create;
With extract_table do begin
DataSource1.DataSet:= extract_table; Open;
First;
While Not Eof do
begin
l_c_company_name_list.Add(FieldByName('Company').AsString);
l_c_address_list.Add(FieldByName('Addr1').AsString);
l_c_contact_list.Add(FieldByName('Contact').AsString);
Next; end; // While Not Eof
end; // With IbQuery1
l_c_company_name_list.SaveToFile(g_database_disk_path+ k_company_list_name);
l_c_company_name_list.Free;
l_c_address_list.SaveToFile(g_database_disk_path+ k_address_list_name);
l_c_address_list.Free;
l_c_contact_list.SaveToFile(g_database_disk_path+ k_contact_list_name);
l_c_contact_list.Free; end; // extract_bde_Click |
And here is the insertion part: | drop another tButton on the tForm, call it "insert", and type the insertion text:
function f_double_to_sql_string(p_double: Double): String;
var l_save_decimal_separator: Char; begin
l_save_decimal_separator:= DecimalSeparator; DecimalSeparator:= '.';
Result:= FloatToStr(p_double);
DecimalSeparator:= l_save_decimal_separator;
Result:= f_replace_character(Result, ',', '.');
end; // l_save_decimal_separator
procedure TForm1.insert_Click(Sender: TObject);
procedure insert_generic(p_id: Integer; p_company_name, p_address, p_contact: String;
p_amount_due: Double);
var l_values: String;
l_sql_request: String; begin
l_values:=
IntToStr(p_id)+ ', '
+ QuotedStr(p_company_name)+ ', '
+ QuotedStr(p_address)+ ', '
+ QuotedStr(p_contact)+ ', '
+ f_double_to_sql_string(p_amount_due);
l_sql_request:= k_sql_insert_partial
+ ' ('+ l_values+ ')';
execute_sql(l_sql_request); end; // insert_generic
var l_c_company_name_list, l_c_address_list, l_c_contact_list: tStringList;
l_line_index: Integer;
l_sample_count: Integer;
l_company_name_random, l_address_random, l_contact_random: String;
l_amount_due_random: Double; begin // insert_Click
write_IbDatabase.Close;
write_IbDatabase.DatabaseName:= g_database_path+ k_database_name;
l_c_company_name_list:= tStringList.Create;
l_c_company_name_list.LoadFromFile(g_database_disk_path+ k_company_list_name);
l_sample_count:= l_c_company_name_list.Count;
l_c_address_list:= tStringList.Create;
l_c_address_list.LoadFromFile(g_database_disk_path+ k_address_list_name);
l_c_contact_list:= tStringList.Create;
l_c_contact_list.LoadFromFile(g_database_disk_path+ k_contact_list_name);
for l_line_index:= 0 to StrToInt(insert_count_edit_.Text)- 1 do
begin
l_company_name_random:= l_c_company_name_list[Random(l_sample_count)];
l_address_random:= l_c_address_list[Random(l_sample_count)];
l_contact_random:= l_c_contact_list[Random(l_sample_count)];
l_amount_due_random:= 23000+ Random(150000)/ 10;
insert_generic(1000+ l_line_index, l_company_name_random, l_address_random,
l_contact_random, l_amount_due_random);
end; // for l_line_index end; // insert_Click |
Notice that we used QuotedStr to avoid any single vs double quote, which are not the same in Interbase Dialect 1 and Dialect 3 |
| compile, execute and click | | here is our application in full swing:
|
Our base is ready, and we can easily change the content of our table (add columns, different types, increase the row count...). You can also add some
bells and whistles, like: - an edit to allow the user to set the line count
- a button to empty the table
3 - Sniffing Interbase Express
We are now ready for the main action. We will launch a SELECT query, and measure the traffic of this instruction. So we will: 3.1 - The Sniffing part
The sniffing part is done by importing the c_packet_sniffer into our project. A you may remember, this CLASS can start the sniffing, and collect statistics about the packet count and sizes.
The use of the c_packet_sniffer would still require the tedious link of all collected data to visual controls of our application. We already did this display in the p_packet_sniffer application, but we do not wish to build the
SELECT in that main form. Regrettably Delphi offers no way to isolate a group of controls from a Form. We can try to do some "Save As" gimmickery, or
use copy and paste, but that's about it. Of course we can create a brand new component, but this would be an overkill. So we will use a CLASS which will create the visual controls "by hand", like in olden OWL time, and use this
CLASS, and use this c_display_packet CLASS in our project. Here is a snapshot of our previous capture Form:
What we want is the lower part (tNoteBook, tListView) and some count labels. We created a CLASS:
We build a small project with a tPanel (the owner and parent of our packet
display) and a "star" and "stop" button, and the .ZIP is available. The c_display_packet CLASS could easily be turned into a component, but our main goal being to compare Interbase component we will not do it here.
3.2 - The Interbase Express SELECT Let us now write the Interbase Express applications |
create a new application and call il "p_sniff_ibx_select" | | drop a tIbDatabase, a tIbTransaction and link those together.
Double click on tIbDatabase to open the connection editor, and fill in the DatabaseName, User, Password, remove the Login checkbox and hit "Test" to check the connection |
| place a tPanel on the Form to display the capture statistics | |
place a tButton, create its OnClick event, call it "connect" and write the capture and connection code:
procedure TForm1.connect_Click(Sender: TObject);
var l_log_name: String; begin
if sniff_.Checked
then begin
g_c_display_packets:= c_display_packets.create_display_packets('display_packets',
sniff_panel);
if log_.Checked
then begin
f_create_path(k_local_log_path);
l_log_name:= k_local_log_path+ f_now_to_string+ '_log_ibx.bin'
end
else l_log_name:= '';
g_c_display_packets.start_capture(0, True, l_log_name, k_list_packets_true);
end; IbDatabase1.Open;
end; // connect_Click | | |
drop an tIbQuery component, link it to IbDatabase1. | | place a tButton, create its OnClick event, call it "select" let it open
the table: const k_table_name= 'sniffer_table';
k_sql_select= 'SELECT * FROM '+ k_table_name;
procedure TForm1.select_Click(Sender: TObject);
begin DataSource1.Dataset:= Nil;
with IbQuery1 do begin
Close; with Sql do
begin Clear;
Add(k_sql_select); Open;
end; // with Sql
end; // with IbQuery1 end; // select_Click |
| | compile, run and click: Here is an example of opening the table:
And when we check "stats", we get the byte counts (we only show the capture window): You may have noticed that the packet count is not exactly the same at the
count in the previous figure: by the time we had copied the figure to the clipboard and had inserted it in this paper, the network had sent some additional packets. We kept the discrepancy to show you that getting an
accurate picture might require some quick foot work. |
3.3 - Monitoring Ibx To see which instruction have been sent, we can add an IbSqlMonitor:
| drop a tIbSqlMonitor on Form1. Toggle its Enabled property to True. Create ist OnSql property and let it display each log message:
var g_event_count: Integer;
procedure TForm1.IBSQLMonitor1SQL(EventText: String; EventTime: TDateTime);
begin
display('= ===== '+ IntToStr(g_event_count));
Inc(g_event_count);
display('= '+ EventText);
end; // IBSQLMonitor1SQL | | |
here is the monitor display: This figure show that we can display the Ibx monitoring event together with the packet count. For instance: = 23 ===== 1
IBTransaction1: [Start transaction] |
This simultaneaous monitoring and packet sniffing is interesting because: - it shows that the monitoring is performed by the Interbase Express layer,
at chosen points, and the information is synthetized to be as useful as possible to the programmer. The goal is not to measure traffic.
- it also proves how easy it is to intergrate the sniffing class into a
project, and display different informations. When we use a separate sniffing tool, we have to note on a piece of paper the timing of the user events and the packet count to try to get an accurate picture. In addition, the
possibility to save the captured packets on disk allows us to perform different kind of studies on the data flow off-line, whereas on-line packet analysis would force us to restart the trials again and again.
4 - 1- Sniffing the BDE We can compare the traffic by using a BDE application:
We also used direct Interbase Api calls, but will not present the detailed application here, since this would drag us into the isc_xxx programming presentation.
5 - The Results
To have a point of comparison, we computed the rock bottom traffic amount one could expect. This simply is the binary size of record. In our case: - s_id INTEGER = 4 bytes
- s_company CHAR(40) = 40 bytes
- s_address CHAR(50) = 50 bytes
- s_contact CHAR(20) = 20 bytes
- s_amount_due DOUBLE PRECISION = 8 bytes
So each record is 122 bytes long.
We filled the table with 100, 1.000 and 10.000 records, and compared the
results for Interbase Express and the BDE: records | database | raw | ibx | bde |
100 | 588 K | 12 K | 111 (57 K) | 58 (24 K) | 1.000 | 692 K | 122 K |
268 (199 K) | 219 (166 K) | 10.000 | 1.712 K | 1.220 K | 1.846 (1.618 K) | 1.770 (1.584 K) |
In this figure: - the "database" figure is the size read in a Windows Explorer
- "raw" is the amount of the "bare minimum" data size
- "ibx" is the result of the capture
- the first number is the packet count (in and out)
- the second is the packet byte count. This figure is the packet content count (without the header). If you wish to compare this amount to the
database size, you must add 54 bytes per packet (14 for Ethernet, 20 for IP and 20 for TCP) which adds a 4% overhead (neglecting the handshake packets, the error packets, packets of smaller size etc).
- "bde" contains the results for the BDE capture
A couple of points a worth mentioning: - when I first performed the 10.000 trial, I kept having 900 K capture logs,
whereas the bare minimum is 1.220K. I then stopped displaying the data and added Application.ProcessMessage in the WHILE loop: the result is as displayed above.
The reason is that when the application processing is too important, the capture misses some packets. Those are mainly at the end of the capture (up to 7000 all packets were recorded, and the end was missing. This was
discovered by checking the s_id values in the recorded packets). This also demonstrates that using packet capture remains a tricky business. - a dump of a single record transfer showed that the true Interbase record
sent over the wire was 152 bytes long. This was easily measured by computing the distance between two consecutive records:
51 i 00 E0 18 E5 4D C2 00 90 : <========= 00-90-F5-0F-0C-1F 00-E0-18-E5-4D-C2
F5 0F 0C 1F 08 00 ______ : <
45 00 03 A4 C3 55 40 00 : <IP 192.168.0.3 192.168.0.2
80 06 B2 A8 C0 A8 00 03 : <
C0 A8 00 02 ____________ : <
0B EA 06 D4 C1 2E 8D 00 : < TCP 3050 1748 sz=892
A4 23 B4 8D 50 18 FD CF : < psh,ack,
12 FE 00 00 ____________ : < ack= 8DB423A4, seq= 008D2EC1
33 33 33 33 73 60 DC 40 : < 3333s`Ü@<
00 00 A0 3A 00 00 00 42 : < .. :...B<
-
00 00 00 00 00 00 00 01 : < ........<
4E 04 00 00 00 00 47 6F : < N.....Go<
6C 64 20 43 6F 61 73 74 : < ld Coast<
20 53 75 70 70 6C 79 20 : < Supply <
20 20 20 20 20 20 20 20 : < <
20 20 20 20 20 20 20 20 : < <
20 20 20 20 20 20 00 00 : < ..<
37 2D 37 33 37 36 33 20 : < 7-73763 <
4E 61 6E 61 6B 61 77 61 : < Nanakawa<
20 52 6F 61 64 20 20 20 : < Road <
20 20 20 20 20 20 20 20 : < <
20 20 20 20 20 20 20 20 : < <
20 20 20 20 20 20 20 20 : < <
20 20 00 00 4D 61 72 69 : < ..Mari<
61 20 45 76 65 6E 74 6F : < a Evento<
73 68 20 20 20 20 20 20 : < sh <
00 00 00 00 00 00 00 00 : < ........<
00 00 00 00 00 16 DB 40 : < ......Û@<
00 00 A0 3A 00 00 00 42 : < .. :...B<
-
00 00 00 00 00 00 00 01 : < ........<
4F 04 00 00 00 00 55 6E : < O.....Un<
69 73 63 6F 20 20 20 20 : < isco <
|
In any case, the trial demonstrate that Ibx carries a 30 K overhead. We naturally looked at the content of the Ibx packets on a 1 record table. There
were lots of nearly-empty packets. Obviously there was some startup computation performed by Ibx. At the end, we remembered that we had added the IbMonitor to our application.
Changing IbMonitor's Enabled property to False, and even deleting the component form the Form did not change anything. However toggling all
IbDatabase TraceFlags to False did the trick: Interbase Express is about 1 K leaner (1.583 K) then the BDE.
So, at the end of the day, the conclusion is the following:
- Ibx and the BDE have about the same traffic in SELECT statements. This is not too surprising, since all calls are converted to nearly the same isc_xxx Api calls.
- the use of IbSqlMonitor adds a fixed 30 K overhead in traffic
6 - Improvements 6.1 - Benchmarks
- all benchmarks can be challenged. We could spend days trying out all kinds of benchmarking schemes and strategies, there is always going to be some user claiming, usually rightly so, that this benchmark does not correspond
to the way he is using Interbase. We present here an example, and it will be easy to tweak the code here or there to suit your needs.
In our case - we would have to make sure that our trials are not biased by previous
experiments. In particular, we should be suspicious of the caches present at many levels (the PC, Interbase, Delphi).
- time measurements should also be performed without any sniffing in the loop, to avoid any spurious interactions
In this area, Ibx and the Bde had about the same performance, about a couple of seconds for 100.000 records (on a local 100 MB network). The longest was the ending of the connection (about 7 seconds).
6.2 - Level of Interbase Sniffing At the client level, we have 3 tracing possibilities: - at the first level, the IbSqlMonitor displays informations about the
request which are going to be sent to the Interbase Client
- at the second level, that we named the "proxy" level, you can trap the requests as they are sent to the Interbase Client. For instance, when I
was working under Linux I wrote in Delphi a DLL (under Linux it is not named a DLL but it is the same concept). This DLL simply received the request from Ibx, displayed the isc_xxx command AND all its parameters, and forwarded
the request to the real Interbase Client. The difference with IbSqlMonitor is that I had all control over what I wanted to display
- the third level is the network level, which we presented in this paper. We
can use general sniffing tools, but have to interpret the raw packet content.
6.3 - Other possibilities Now that we have the tool, we could study other settings:
- what is the the impact of the different Interbase Client level or Interbase Server level tunings (cache size, packet size etc)
- compare the different Interbase versions (Super Server vs Classic, Firebird 1.5 vs Interbase 7.5)
- analyze the effect of using other Delphi components: dbExpress and the
much acclaimed more efficient drivers, or the Borland Data Provider on top of Windows Forms
- add timing measurements
- what is the impact of using VARCHAR instead of CHAR (but there we would also add timing measurements, and compare first insert and modifications with longer or shorter values)
- similar question for parametrized queries vs direct SELECT
- forcing some schema loading on our client (by opening tables)
- using nested tables vs separate master and detail
- what is the cost of closing and reopening the connection ?
- an easy one now: using stored procedures and triggers or calling everything over on the Client side. Using a stored procedures for sequence fetching or
grabbing the value directly in RDB$DATABASE.
- transactions. Ah, transactions, that's my favorite one. Just throwing a curve at you: what would be the difference between "SnapShot" and "Read
Committed" ?. And what about letting Delphi handling all the transactions (in a single default transaction) versus trying to do it all ourselve, possibly with multiple transactions ?
The list goes on and on. This is an ideal field of investigation for a Master Student looking for an easy thesis subject. With nice graphics all over the place, which always look good at presentation time. And everything can be done
in Delphi: the sniffer sources have been published in Delphi in our previous paper, and the creation of the data to measure is part of this paper. tTeeChart to the rescue to draw the lovely graphics.
On a more practical level, I started this sniffing because I did not understand the time taken by some requests. This certainly would be my advice: only dwelve in this area if your are forced to do so because of performance considerations.
7 - Download the Source Code We placed all the sources for the projects in the following .ZIP files:
Those files contain:
- the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
- any .TXT for parameters
- all units (.PAS) for units
The .ZIPs
- are self-contained: you will not need any other product (unless expressly mentioned).
- can be used from any folder (the pathes are RELATIVE)
- will not modify your PC in any way beyond the path where you placed the .ZIP
(no registry changes, no path creation etc).
To use the .ZIP: - create or select any folder of your choice
- unzip the downloaded file
- using Delphi, compile and execute
To remove the .ZIP simply delete the folder.
As usual: - please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken
links or had some problem downloading the file. Resulting corrections will be helpful for other readers
- we welcome any comment, criticism, enhancement, other sources or reference
suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
- or more simply, enter your (anonymous or with your e-mail if you want an
answer) comments below and clic the "send" button
- and if you liked this article, talk about this site to your fellow
developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate: the more traffic and Google references we get, the more articles we will write.
8 - Conclusion We presented how to use a TPC IP Sniffer to compare Interbase network traffic.
9 - References
- Ten Things You Can do to Make Interbase Scream
Bill KARWIN - Borland Conference 1998 One of the numerous paper about Interbase optimization
- The Firebird Book
Helen BORIE - Apress - ISBN 1 59059 279 4 The most complete book about Interbase programming, at the Sql, and C level. Also very good explanations about the architecture.
- TCP IP Sniffer the companion paper where we presented the Sniffer capture and display applications
10 - Other Papers with Source and Links
Database |
database reverse engineering |
Extraction of the Database Schema by analyzing the content of the application's .DFMs | sql parser |
Parsing SQL requests in Delphi, starting from an EBNF grammar for SELECT, INSERT and UPDATE | ado net tutorial |
a complete Ado Net architectural presentation, and projects for creating the Database, creating Tables, adding, deleting and updating rows, displaying the data in controls and DataGrids, using in memory DataSets, handling Views, updating the Tables with a DataGrid
| turbo delphi interbase tutorial |
develop database applications with Turbo Delphi and Interbase. Complete ADO Net architecture, and full projects to create the database, the Tables, fill the rows, display and update the values with DataGrids. Uses the BDP |
bdp ado net blobs | BDP and Blobs : reading and writing Blob fields using the BDP with Turbo Delphi |
interbase stored procedure grammar |
Interbase Stored Procedure Grammar : The BNF Grammar of the Interbase Stored Procedure. This grammar can be used to build stored procedure utilities, like pretty printers, renaming tools, Sql Engine conversion or ports |
using interbase system tables |
Using InterBase System Tables : The Interbase / FireBird System Tables: description of the main Tables, with their relationship and presents examples of how to extract information from the schema |
eco tutorial |
Writing a simple ECO application: the UML model, the in memory objects and the GUI presentation. We also will show how to evaluate OCL expressions using the EcoHandles, and persist the data on disc |
delphi dbx4 programming |
the new dbExpress 4 framework for RAD Studio 2007 : the configuration files, how to connect, read and write data, using tracing and pooling delegates and metadata handling |
blackfishsql |
using the new BlackfishSql standalone database engine of RAD Studio 2007 (Win32 and .Net) : create the database, create / fill / read Tables, use Pascal User Defined Functions and Stored Procedures |
rave pdf intraweb |
how to produce PDF reports using Rave, and have an Intraweb site generate and display .PDF pages, with multi-user access |
embarcadero er studio |
Embarcadero ER Studio tutorial: how to use the Entity Relationship tool to create a new model, reverse engineer a database, create sub-models, generate reports, import metadata, switch to Dimensional Model | | |
Web |
sql to html | converting SQL ascii request to HTML format
| simple web server |
a simple HTTP web Server and the corresponding HTTP web Browser, using our Client Server Socket library |
simple cgi web server |
a simple CGI Web Server which handles HTML <FORM> requests, mainly for debugging CGI Server extension purposes |
cgi database browser | a CGI extension in order to display and modify a Table using a Web Browser |
whois | a Whois Client who requests information about owners of IP adresses. Works in batch mode. |
web downloader |
an HTTP tool enabling to save on a local folder an HTML page with its associated images (.GIF, .JPEG, .PNG or other) for archieving or later off-line reading |
web spider | a Web Spider allowing to download all pages from a site, with custom or GUI filtering and selection. |
asp net log file |
a logging CLASS allowing to monitor the Asp.Net events, mainly used for undesrtanding, debugging and journaling Asp.Net Web applications |
asp net viewstate viewer |
an ASP.NET utility displaying the content of the viewtate field which carries the request state between Internet Explorer and the IIS / CASSINI Servers |
rss reader |
the RSS Reader lets you download and view the content of an .RSS feed (the entry point into somebody's blog) in a tMemo or a tTreeView. Comes complete with an .HTML downloader and an .XML parser |
news message tree |
how to build a tree of the NNTP News Messages. The downloaded messages are displayed in tListBox by message thread (topic), and for each thread the messages are presented in a tTreeVi"ew |
threaded indy news reader |
a NewsReader which presents the articles sorted by thread and in a logical hierarchical way. This is the basic Indy newsreader demo plus the tree organization of messages |
delphi asp net portal programming |
presentation, architecture and programming of the Delphi Asp Net Portal. This is a Delphi version of the Microsoft ASP.NET Starter Kit Web Portal showcase. With detailed schemas and step by step presentation, the Sql scripts and binaries of the Database
| delphi web designer |
a tiny Delphi "RAD Web Designer", which explains how the Delphi IDE can be used to generate .HTML pages using the Palette / Object Inspector / Form metaphor to layout the page content |
intraweb architecture |
the architecture of the Intraweb web site building tool. Explains how Delphi "rad html generator" work, and presents the CLASS organization (UML Class diagrams) |
ajax tutorial |
AJAX Tutorial : writing an AJAX web application. How AJAX works, using a JavaScript DOM parser, the Indy Web Server, requesting .XML data packets - Integrated development project |
asp net master pages |
Asp.Net 2.0 Master Pages : the new Asp.Net 2.0 allow us to define the page structure in a hierarchical way using Master Pages and Content Pages, in a way similar to tForm inheritance |
delphi asp net 20 databases |
Asp.Net 2.0 and Ado.Net 2.0 : displaying and writing InterBase and Blackfish Sql data using Dbx4, Ado.Net Db and AdoDbxClient. Handling of ListBox and GridView with DataSource components
| asp net 20 users roles profiles |
Asp.Net 2.0 Security: Users, Roles and Profiles : Asp.Net 2.0 offers a vaslty improved support for handling security: new Login Controls, and services for managing Users, grouping Users in Roles, and storing User preferences in Profiles
| bayesian spam filter |
Bayesian Spam Filter : presentation and implementation of a spam elimination tool which uses Bayesian Filtering techniques | | |
TCP/IP |
tcp ip sniffer | project to capture and display the packets travelling on the Ethernet network of your PC. |
sniffing interbase traffic |
capture and analysis of Interbase packets. Creation of a database and test table, and comparison of the BDE vs Interbase Express Delphi components |
socket programming | the simplest Client Server example of TCP / IP communication using Windows Sockets with Delphi |
delphi socket architecture |
the organization of the ScktComp unit, with UML diagrams and a simple Client Server file transfer example using tClientSocket and tServerSocket | | |
Object Oriented Programming Components |
delphi virtual constructor |
VIRTUAL CONSTRUCTORS together with CLASS references and dynamic Packages allow the separation between a main project and modules compiled and linked in later. The starting point for Application Frameworks and Plugins
| delphi generics tutorial |
Delphi Generics Tutorial : using Generics (parameterized types) in Delphi : the type parameter and the type argument, application of generics, constraints on INTERFACEs or CONSTRUCTORs | |
| UML Patterns |
the lexi editor |
delphi source code of the Gof Editor: Composite, Decorator, Iterator, Strategy, Visitor, Command, with UML diagrams |
factory and bridge patterns |
presentation and Delphi sources for the Abstract Factory and Bridge patterns, used in the Lexi Document Editor case study from the GOF book |
gof design patterns |
delphi source code of the 23 Gof (GAMMA and other) patterns: Composite, Decorator, Iterator, Strategy, Visitor, Command | | |
| Graphic |
delphi 3d designer |
build a 3d volume list, display it in perspective and move the camera, the screen or the volumes with the mouse. |
writing a flash player |
build your own ShockWave Flash movie Player, with pause, custom back and forward steps, snapshots, resizing. Designed for analyzing .SWF demos. | | |
Utilities |
the coliget search engine |
a Full Text Search unit allowing to find the files in a directory satisfying a complex string request (UML AND Delphi OR Patters) |
treeview html help viewer |
Treeview .HTML Help Viewer : the use of a Treeview along with a WebBrowser to display .HTML files alows both structuring and ordering of the help topics. This tool was used to browse the Delphi PRISM Wiki help. | |
| Delphi utilities |
delphi net bdsproj |
structure and analysis of the .BDSPROJ file with the help of a small Delphi .XML parser | dccil bat generator
| generation of the .BAT for the Delphi DCCIL command line compiler using the .BDSPROJ | dfm parser |
a Delphi Project analyzing the .DFM file and building a memory representation. This can be used for transformations of the form components |
dfm binary to text | a Delphi Project converting all .DFM file from a path from binary to ascii format |
component to code |
generate the component creation and initialization code by analyzing the .DFM. Handy to avoid installing components on the Palette when examining new libraries |
exe dll pe explorer |
presents and analyzes the content of .EXE and .DLL files. The starting point for extracting resources, spying .DLL function calls or injecting additional functionalities |
dll and process viewer |
analyze and display the list of running processes, with their associated DLLs and Memory mapped files (Process Walker) | | |
Controls |
find memo | a tMemo with "find first", "find next", "sort", "save" capabilities | | |
|
11 - The author
Felix John COLIBRI works at the Pascal Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly
active in the area of custom software development (new projects, maintenance, audits, BDE migration, Delphi
Xe_n migrations, refactoring), Delphi Consulting and Delph
training. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, FireBird, Tcp/IP, Web Services, OOP / UML, Design Patterns, Unit Testing training sessions. |