Arief Sujatmiko on Blogspot
blog ini tempat saya share tentang pemikiran dan kejadian-kejadian yang saya temui, update terbaru follow my twitter @ariefsujatmiko.
Kamis, 22 Januari 2026
Searching Algorithms That I Often Use
Selasa, 16 Desember 2025
Creating Expert System for Database Application Development
The design of Gampang Builder is very simple, it get input from the user about the application design and generate software project for it, not a complete software but only:
- Main form and main menu of the application.
- Navigation system to access facilities in the application and limiting access by application user.
- Data service for create, edit and delete for entities and relations.
- Editing form for the entites and relations.
- Printable report for the entities.
When we create an application first we have to define the backgroud of the problem to solve and the target to be achieved by the application.
Then we choose the technology for the to be generated application.
To make our code more reusable I split the code into few modules.In each module we can define the class name for the data service and access right than can be used to limit user access because not every user will have the same access level.After we define the modules we can define the entities by the data table and relations between entities in the applications.
In the data tabel we can define field name, data type, its characteristic, and relation with other table such as look up or master-detail.If we need the printed design for documentation, Gampang Builder can print it.
The last thing to do is generate the application but because this application is too old and many changes to the library change the package file reader, I will repair the code and update when it completed. I created a few application with this program, one of the application is a medical laboratory database application and is still running today from 2016. I dont have time yet to improve this program but maybe someday.
That's all in this article, I hope it's usefull.
Creating Simple Web Server with Lazarus
Web server is a program or sub program that provide data to client program not limited to web browser using HTTP or HTTPS (HTTP over SSL) protocol. HTTP is a simple, fast and robust protocol for client-server application. The HTTP protocol works by having the client send a request to the server, then the server processes the request and sends back a response to the client.
Example of HTTP Client's Request:
GET / HTTP/1.1 Host: www.example.com
The first line of a HTTP request consist of a command, a url and the protocol version. Followed by header lines and terminated by empty line. Each header line consist of header name and value separated by colon and one space. After the header data is the content data that contain n bytes where n is the value of Content-Length header.
Example of HTTP Server's Response:
HTTP/1.1 200 OK Date: Mon, 23 May 2005 22:38:34 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 155 Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux) ETag: "3f80f-1b6-3e1cb03b" Accept-Ranges: bytes Connection: close <html> <head> <title>An Example Page</title> </head> <body> <p>Hello World, this is a very simple HTML document.</p> </body> </html>
The first line of a HTTP response consist of protocol version and result code followed by error message. Followed by header lines and terminated by empty line. Each header line consist of header name and value separated by colon and one space. After the header data is the content data that contain n bytes where n is the value of Content-Length header.
Free Pascal, Delphi and Lazarus share the same programming language the Object Pascal with little differentiation. So many Delphi component can be used in Lazarus and vice versa because it only requires minor changes in the source code to support both programming. If you don't want to use visual component you can use operating system API like winsock (Windows Socket) for Windows or unisock (Unix Socket) for Linux. I will use synapse for Free Pascal library in this example.
To help parsing input data I create TStringSplitter :
- type
- { TStringSplitter }
- TStringSplitter = class(TObject)
- private
- FLine: String;
- FCol: Integer;
- procedure SetCol(AValue: Integer);
- procedure SetLine(AValue: String);
- public
- constructor Create;
- function Fetch(const ATerminator: String=''): String;
- property Line: String read FLine write SetLine;
- property Col: Integer read FCol write SetCol;
- end;
- function PosAfter(const SubText, Text: String; StartAt: Integer): Integer;
- var
- I, L1, L2: Integer;
- begin
- Result := -1;
- L1 := Length(SubText);
- L2 := Length(Text);
- if ((L1 > L2) or (StartAt < 1)) then exit;
- for I := StartAt to L2-L1+1 do
- if (CompareMem(@SubText[1], @Text[I], L1)) then
- begin
- Result := I;
- exit;
- end;
- end;
- procedure TStringSplitter.SetCol(AValue: Integer);
- begin
- FCol := AValue;
- end;
- procedure TStringSplitter.SetLine(AValue: String);
- begin
- FLine := AValue;
- FCol := 1;
- end;
- constructor TStringSplitter.Create;
- begin
- FLine := '';
- FCol := 1;
- end;
- function TStringSplitter.Fetch(const ATerminator: String): String;
- var
- I, J: Integer;
- begin
- Result := '';
- I := FCol;
- if (I > Length(FLine)) then exit;
- if (ATerminator <> '') then
- begin
- J := PosAfter(ATerminator, FLine, I);
- if (J > 0) then
- begin
- Result := Copy(FLine, I, J-I);
- FCol := J + Length(ATerminator);
- exit;
- end;
- end;
- Result := Copy(FLine, FCol, MaxInt);
- FCol := Length(FLine) + 1;
- end;
When the server is started it initialize the socket to listen to the specified port. Then it repeatly check for incoming connection and start HTTP handler worker thread until the server is terminated.
- procedure TDaemon1.WorkServerListener(Thread: TWorkerThread);
- var
- sock: TTCPBlockSocket;
- h: TSocket;
- begin
- try
- sock := TTCPBlockSocket.Create;
- try
- sock.Bind('0.0.0.0',IntToStr(Port));
- if (sock.LastError <> 0) then
- raise ENetworkError.Create('Cannot bind to port '+IntToStr(Port));
- sock.Listen;
- if (sock.LastError <> 0) then
- raise ENetworkError.Create('Cannot listen to port '+IntToStr(Port));
- while (not Thread.Terminated) do
- begin
- if (sock.CanRead(0)) then
- begin
- h := sock.Accept;
- {$HINTS OFF}
- if (h <> INVALID_SOCKET) then
- WorkerThreads.AddNew(@WorkRequestHandler,Pointer(h));
- {$HINTS ON}
- end
- else
- Sleep(1);
- end;
- finally
- sock.Free; // close and free socket object
- end;
- except
- on E: Exception do
- // DebugLog('HTTP Server terminated by error '+E.ClassName+': '+
- // E.Message);
- end;
- end;
The HTTP handler worker thread read HTTP request from incoming connection and send back HTTP Respond.
- procedure TDaemon1.WorkRequestHandler(Thread: TWorkerThread);
- var
- sock: TTCPBlockSocket;
- S1: TStringSplitter;
- s, cmd, url, http_ver, nm, vl, error_msg, respond_text, respond_data: AnsiString;
- I, result_code: Integer;
- close_socket, can_read: Boolean;
- headers, gets, respond_headers: TStrings;
- begin
- result_code := 500;
- close_socket := True;
- sock := TTCPBlockSocket.Create;
- S1 := TStringSplitter.Create;
- headers := TStringList.Create;
- gets := TStringList.Create;
- respond_headers := TStringList.Create;
- try
- {$HINTS OFF}
- sock.Socket := TSocket(Thread.Param);
- {$HINTS ON}
- repeat
- can_read := sock.CanRead(0);
- if (sock.LastError <> 0) then exit;
- if (can_read) then
- begin
- I := Pos('?',url);
- if (I >= 1) then
- begin
- S1.Line := Copy(url, I+1, MaxInt);
- repeat
- nm := S1.Fetch('=');
- if (nm <> '') then
- begin
- vl := S1.Fetch('&');
- gets.Values[nm] := vl;
- end;
- until (nm = '');
- Delete(url,I,MaxInt);
- end;
- S1.Line := sock.RecvString(ReadTimeout);
- if (sock.LastError <> 0) then exit;
- cmd := S1.Fetch(' ');
- url := S1.Fetch(' ');
- http_ver := S1.Fetch;
- if ((cmd = 'GET') or (cmd = 'POST')) then
- begin
- close_socket := (http_ver = 'HTTP/0.9') or (http_ver = 'HTTP/1.0');
- S1.Line := sock.RecvString(ReadTimeout);
- if (sock.LastError <> 0) then exit;
- while (S1.Line <> '') do
- begin
- nm := S1.Fetch(': ');
- vl := S1.Fetch;
- headers.Values[nm] := vl;
- S1.Line := sock.RecvString(ReadTimeout);
- if (sock.LastError <> 0) then exit;
- end;
- //
- // .. do some processing here
- //
- respond_data :=
- '<head><title>Welcome</title></head><body><h1>Welcome to my Web Server</h1><p>You are in here: '+HttpEncode(url)+'</body>';
- respond_headers.Values['Content-Type'] := 'html/text';
- respond_headers.Values['Content-Length'] := IntToStr(Length(respond_data));
- result_code := 200;
- end
- else begin
- result_code := 501;
- close_socket := True;
- end;
- if((result_code < 200) or (result_code > 499)) then close_socket := True;
- case result_code of
- 200: error_msg := 'OK';
- 400: error_msg := 'Bad Reqeust';
- 500: error_msg := 'Internal Server Error';
- 501: error_msg := 'Not Implemented';
- end;
- respond_text :=
- http_ver + ' ' + IntToStr(result_code) + ' ' + error_msg + #13#10;
- for I := 0 to respond_headers.Count do
- respond_text := respond_text + respond_headers.Names[I] + ': ' +
- respond_headers.ValueFromIndex[I] + #13#10;
- respond_text := respond_text + #13#10 + respond_data;
- sock.SendBuffer(@respond_text[1], Length(respond_text));
- headers.Clear;
- respond_headers.Clear;
- respond_text := '';
- end
- else
- Sleep(1);
- until (Thread.Terminated or close_socket);
- finally
- respond_headers.Free;
- gets.Free;
- headers.Free;
- S1.Free;
- sock.Free;
- end;
- end;
You can send and receive data using easy to parse data like JSON, XML, BSON or binary data for data service, it is very flexible.
That's all for this article and I hope it's useful.
Kamis, 04 Desember 2025
Creating Thread from Procedure to simplify thread programming in Free Pascal and Delphi
Thread in Free Pascal and Delphi is a process that run simutanously with main program. This is very useful when we need to process data in background while user doing his own work. And is needed to maximize processing in multicore processor because a process including main process can only use 1 core and a quad core processor require a minimum of 4 threads to use all 4 cores.
Basically to create a thread we need to create the thread class and override the execute procedure with the thread code. To minimize work I create WorkerThreadList class, a very simple class to run a procedure of object as a thread.
First the class definition:
- type
- { TSemaphore }
- TSemaphore = class(TObject)
- private
- FSem: Integer;
- public
- constructor Create;
- destructor Destroy; override;
- procedure Lock;
- procedure Unlock;
- end;
- { WorkerThreads Routines }
- TWorkerThread = class;
- TWorkerThreadList = class;
- TWorkEvent = procedure(WT: TWorkerThread) of object;
- { TWorkerThread }
- TWorkerThread = class(TThread)
- private
- FOwner: TWorkerThreadList;
- FOnWork: TWorkEvent;
- FParam: Pointer;
- protected
- procedure Execute; override;
- public
- constructor Create(AOwner: TWorkerThreadList; AOnWork: TWorkEvent;
- AParam: Pointer);
- destructor Destroy; override;
- property OnWork: TWorkEvent read FOnWork write FOnWork;
- property Param: Pointer read FParam;
- property Terminated;
- end;
- { TWorkerThreadList }
- TWorkerThreadList = class(TObject)
- private
- FWorkers: TObjectList;
- FSem: TSemaphore;
- function GetItems(Index: Integer): TWorkerThread;
- public
- constructor Create;
- destructor Destroy; override;
- function AddNew(AOnWork: TWorkEvent; AParam: Pointer=nil): TWorkerThread;
- procedure TerminateWait(Thread: TWorkerThread);
- procedure UnregisterWorkerThread(AThread: TWorkerThread);
- procedure TerminateWorkerThreads(Wait: Boolean);
- property Items[Index: Integer]: TWorkerThread read GetItems; default;
- end;
Then I create the class definition like this:
- { TWorkerThreadList }
- function TWorkerThreadList.GetItems(Index: Integer): TWorkerThread;
- begin
- Result := TWorkerThread(FWorkers[Index]);
- end;
- constructor TWorkerThreadList.Create;
- begin
- inherited;
- FWorkers := TObjectList.Create(False);
- FSem := TSemaphore.Create;
- end;
- destructor TWorkerThreadList.Destroy;
- begin
- TerminateWorkerThreads(True);
- FWorkers.Free;
- FSem.Free;
- inherited Destroy;
- end;
- function TWorkerThreadList.AddNew(AOnWork: TWorkEvent;
- AParam: Pointer): TWorkerThread;
- var
- N: TWorkerThread;
- begin
- N := TWorkerThread.Create(Self, AOnWork, AParam);
- try
- FSem.Lock;
- try
- FWorkers.Add(N);
- finally
- FSem.Unlock;
- end;
- Result := N;
- N.Start;
- except
- N.Free;
- raise;
- end;
- end;
- procedure TWorkerThreadList.TerminateWait(Thread: TWorkerThread);
- begin
- if (Thread = nil) then exit;
- FSem.Lock;
- try
- if (FWorkers.IndexOf(Thread) < 0) then
- exit
- else begin
- if (not Thread.Terminated) then Thread.Terminate;
- if (Thread.Suspended) then Thread.Start;
- end;
- finally
- FSem.Unlock;
- end;
- repeat
- FSem.Lock;
- try
- if (FWorkers.IndexOf(Thread) < 0) then exit;
- finally
- FSem.Unlock;
- end;
- Sleep(1);
- until (False);
- end;
- procedure TWorkerThreadList.UnregisterWorkerThread(AThread: TWorkerThread);
- begin
- if ((AThread = nil) or (AThread.FOwner <> Self)) then
- exit;
- FSem.Lock;
- try
- FWorkers.Remove(AThread);
- AThread.FOwner := nil;
- finally
- FSem.Unlock;
- end;
- end;
- procedure TWorkerThreadList.TerminateWorkerThreads(Wait: Boolean);
- var
- I, J: Integer;
- begin
- repeat
- FSem.Lock;
- try
- J := FWorkers.Count;
- for I := 0 to J-1 do
- begin
- with TWorkerThread(FWorkers[I]) do
- begin
- if (not Terminated) then Terminate;
- if (Suspended) then Start;
- end;
- end;
- finally
- FSem.Unlock;
- end;
- Sleep(1);
- until ((J = 0) or not Wait);
- end;
- { TWorkerThread }
- procedure TWorkerThread.Execute;
- begin
- try
- if (Assigned(FOnWork)) then FOnWork(Self);
- except
- end;
- if (FOwner <> nil) then FOwner.UnregisterWorkerThread(Self);
- end;
- constructor TWorkerThread.Create(AOwner: TWorkerThreadList; AOnWork: TWorkEvent;
- AParam: Pointer);
- begin
- inherited Create(True);
- FreeOnTerminate := True;
- Priority := tpIdle;
- FOwner := AOwner;
- FOnWork := AOnWork;
- FParam := AParam;
- end;
- destructor TWorkerThread.Destroy;
- begin
- if (FOwner <> nil) then FOwner.UnregisterWorkerThread(Self);
- inherited Destroy;
- end;
- { TSemaphore }
- constructor TSemaphore.Create;
- begin
- FSem := 0;
- end;
- destructor TSemaphore.Destroy;
- begin
- Unlock;
- inherited Destroy;
- end;
- procedure TSemaphore.Lock;
- begin
- while (InterLockedExchange(FSem, 1) = 1) do Sleep(1);
- end;
- procedure TSemaphore.Unlock;
- begin
- InterLockedExchange(FSem, 0);
- end;
Using this class I can run a procedure of object with this parameter using TWorkerThreadList object:
TWorkEvent = procedure(WT: TWorkerThread) of object;
How to use the TWorkerThreadList class:
- Define WorkerThreads variable as global variable or in the data module:
WorkerThreads: TWorkerThreadList; - Add object create and free for the WorkerThreads object in the data module on create and on destroy:
- procedure TDaemon1.DataModuleCreate(Sender: TObject);
- begin
- WorkerThreads := TWorkerThreadList.Create;
- end;
- procedure TDaemon1.DataModuleDestroy(Sender: TObject);
- begin
- WorkerThreads.Free;
- end;
- Create the thread procedure:
- procedure TDaemon1.WorkServerListener(Thread: TWorkerThread);
- begin
- try
- { do something }
- except
- { do handle exceptions }
- end;
- end;
- Create thread for the procedure:
- procedure TDaemon1.DataModuleStart(Sender: TCustomDaemon; var OK: Boolean
- );
- begin
- WorkerThreads.AddNew(@WorkServerListener, nil);
- OK := True;
- end;
WorkerThreadList object can be created and destroyed as needed. A WorkerThreadList object can run alot of procedures.
Working with thread can be tricky, You have to be carefull with race condition, some objects that is not thread safe you need to put semaphore so that only one thread can access it at a time. Memory leak is not the only bugs.
Thats all for the WorkerThreadList object, and I hope this article can be useful.
Kamis, 25 Januari 2024
Creating Linux Daemon or Windows Service with Lazarus
Daemon Application in Linux or Service Application in Windows is an application that running in the background, usually automatically started when the operating system started even if user is not logged in. In Windows Service Application you cannot access Windows GUI library.
To create Daemon with Lazarus, first you need to create new project and select Daemon (service) application. After you click the OK button the IDE will create a new lazarus project with a daemon mapper unit dan a daemon class unit. Then save the project to the specified project name and folder.
Then you need to give the daemon class name. In this example the daemon class is named FirebirdRelayDaemon and the unit file saved as uFirebirdRelayDaemon.pas.The daemon class will never be registered to the operating system or started unless you register it in the Daemon Mapper. To register the daemon class you need to open the daemonmapperunit1.pas.
Then click the object inspector and then click the ellipsis button in the DaemonDefs property to open the daemon definitions editor. Then click Add button in the daemon definitions editor to add new daemon definition.
After you click Add button the object inspector will display the new daemon definition. You need to edit DaemonClassName, Display and Name properties.
- DaemonClassName: TFirebirdRelayDaemon.
- DisplayName: Firebird Relay Server.
- Name: firebirdrelayd.
After the daemon class is registered, you can place the code for your daemon in the daemon class. You can place objects initialization in the OnCreate event and objects finalization in the OnDestroy event. The code to start the daemon thread is placed in the OnStart event and code to terminate daemon thread is placed in the OnStop event.
This is the example of the daemon class declaration:
- type
- { TFirebirdRelayDaemon }
- TFirebirdRelayDaemon = class(TDaemon)
- procedure DataModuleCreate(Sender: TObject);
- procedure DataModuleDestroy(Sender: TObject);
- procedure DataModuleStart(Sender: TCustomDaemon; var OK: Boolean);
- procedure DataModuleStop(Sender: TCustomDaemon; var OK: Boolean);
- private
- { private declarations }
- FirebirdRelayServer: TFirebirdRelayServer;
- public
- { public declarations }
- end;
This is the example of the daemon class definition:
- { TFirebirdRelayDaemon }
- procedure TFirebirdRelayDaemon.DataModuleCreate(Sender: TObject);
- begin
- FirebirdRelayServer := TFirebirdRelayServer.Create;
- end;
- procedure TFirebirdRelayDaemon.DataModuleDestroy(Sender: TObject);
- begin
- FirebirdRelayServer.Free;
- end;
- procedure TFirebirdRelayDaemon.DataModuleStart(Sender: TCustomDaemon;
- var OK: Boolean);
- begin
- FirebirdRelayServer.LoadFromIniFile(ExtractFilePath(ParamStr(0))+'konfig.ini',
- 'FirebirdRelayServer');
- FirebirdRelayServer.StartThread;
- OK := True;
- end;
- procedure TFirebirdRelayDaemon.DataModuleStop(Sender: TCustomDaemon;
- var OK: Boolean);
- begin
- FirebirdRelayServer.TerminateThread;
- OK := True;
- end;
Output of the compiled daemon:
The daemon is installed in the windows services list:
You can start the service from command line using "net start firebirdrelayd" and stop the service using "net stop firebirdrelayd".
Thankyou for reading this article and I hope this article can be useful.
Compress and Decompress Text or File using ZLib in Delphi or Free Pascal Lazarus
When I was in my college I work as part time programmer in a software house. I got a task to create a software updater. The software updater work by copying new files to the already installed application in the client's computers. To make update file easier to distribute I need to compress the new files and combine them into a file.
In Delphi or Lazarus we can use TFileStream class to access files and using TCompressionStream in the zstream unit to compress data from memory buffer to output stream that can be any TStream object including TFileStream or TMemoryStream. To decompress later you can use TDecompressionStream class.
If ZLib compression is not enough you can use BZip2 library for stronger compression.
Bellow is an example of using ZLib TCompressionStream and TDecompressionStream class:
- program TestCompressAndDecompress;
- {$MODE DELPHI}
- uses SysUtils, Classes, zstream;
- var
- F: TMemoryStream;
- Comp: TCompressionStream;
- Decomp: TDecompressionStream;
- X, Y: AnsiString;
- begin
- F := TMemoryStream.Create;
- try
- Comp := TCompressionStream.Create(clMax, F);
- try
- X := 'Compress and Decompress array of bytes or a file using ZLib in Delphi or Free Pascal Lazarus';
- Comp.Write(X[1], Length(X));
- finally
- Comp.Free;
- end;
- WriteLn('Original Text: "',X,'"');
- WriteLn('Original Size: ',Length(X),' bytes.');
- WriteLn('Compressed Size: ',F.Size,' bytes.');
- F.Position := 0;
- Decomp := TDecompressionStream.Create(F);
- try
- SetLength(Y, Length(X));
- Decomp.Read(Y[1], Length(Y));
- finally
- Decomp.Free;
- end;
- WriteLn('Decompressed Text: "',Y,'"');
- finally
- F.Free;
- end;
- end.
Selasa, 14 November 2023
Creating A Thread Safe Database Connection Pool for Delphi and Free Pascal based Lazarus
One day I got a software project to create an application that need to be accessed from many computer with one data server. I decided to create a three-tier application, a client application for the client computer and a middle application that connect directly to database server that is a Firebird Database Server for this project. After a few weeks the project is finished and I realized that the response time of every client request is very slow caused by database connection time. Because every time client request arrived the middle application create new thread to handle the request and create new database connection and doing database query and then send the respond data back to the requester client and finally close database connection and free database connection, transaction and queries objects before the thread is destroyed.
Then I begin researching to reduce the database connection time in the request handler thread. I become pesimistic after I read in delphi manual book that every thread must create and use its own database connection. But I'm still not giving up because other programming like PHP and MySQL can do that. Then I try to isolate every parts of the database connection and database query in part by part, and finally I'm able to reuse database connection, transcation and query object with many threads by isolating creation and desctruction of database connection object and transaction object and also commit and rollback process, everything else is no problem with multithread.
- procedure TSimpleQuery.Commit;
- begin
- if ((FQuery.Connection = nil) or not FQuery.Connection.Connected or
- FQuery.Connection.AutoCommit) then exit;
- EnterCriticalSection(FConnectionManager.GetCriticalSection^);
- try
- try
- FQuery.Connection.Commit;
- except
- LogExceptionMessage;
- raise;
- end;
- finally
- LeaveCriticalSection(FConnectionManager.GetCriticalSection^);
- end;
- ConnectionManager.SetAllowFailover(FQuery, True);
- end;
- procedure TSimpleQuery.Rollback;
- begin
- if ((FQuery.Connection = nil) or not FQuery.Connection.Connected or
- FQuery.Connection.AutoCommit) then exit;
- EnterCriticalSection(FConnectionManager.GetCriticalSection^);
- try
- try
- FQuery.Connection.Rollback;
- except
- LogExceptionMessage;
- raise;
- end;
- finally
- LeaveCriticalSection(FConnectionManager.GetCriticalSection^);
- end;
- ConnectionManager.SetAllowFailover(FQuery, True);
- end;
- ISimpleQuery = interface
- ['{1C1949E4-C472-45BA-8638-5A14545592F8}']
- function GetConnection: TZConnection;
- function GetConnectionManager: IConnectionManager;
- function GetParentQuery: ISimpleQuery;
- function GetQueryObject: TZReadOnlyQuery;
- function GetPrepared: Boolean;
- function GetRecordCount: Integer;
- function GetRowsAffected: Integer;
- function GetActive: Boolean;
- procedure SetActive(AValue: Boolean);
- function GetName: String;
- procedure SetName(const AValue: String);
- function GetFieldDefs: TFieldDefs;
- function GetFields: TFields;
- function GetSQL: TStrings;
- function GetBOF: Boolean;
- function GetEOF: Boolean;
- function GetParams: TParams;
- function GetParamCheck: Boolean;
- procedure SetParamCheck(Value: Boolean);
- function GetConnected: Boolean;
- function GetAutoCommit: Boolean;
- procedure SetAutoCommit(const Value: Boolean);
- function GetRecordAccess: TRecordAccess;
- function GetPost: ITransportVar;
- function GetWhere: ITransportVar;
- procedure Open;
- procedure OpenQuery(const ASQL: String);
- procedure ExecSQL;
- procedure ExecuteQuery(const ASQL: String);
- procedure Close;
- procedure First;
- procedure Next;
- procedure Prior;
- procedure Last;
- procedure Commit;
- procedure Rollback;
- procedure Prepare;
- procedure Unprepare;
- function CreateQuery: ISimpleQuery;
- function FieldByName(const AFieldName: String): TField;
- function FindField(const AFieldName: String): TField;
- function ParamByName(const AParamName: String): TParam;
- procedure Select(const SelectedCols: String);
- procedure Group(const GroupBy: String);
- procedure Order(const OrderBy: String);
- procedure Get(const Table: String);
- procedure Insert(const Table: String);
- procedure Update(const Table: String);
- procedure Delete(const Table: String);
- function GetSQLWhere(AWhere: ITransportVar): String;
- procedure AllowPost(const Columns: String);
- property ConnectionManager: IConnectionManager read GetConnectionManager;
- property ParentQuery: ISimpleQuery read GetParentQuery;
- property Active: Boolean read GetActive write SetActive;
- property Connection: TZConnection read GetConnection;
- property QueryObject: TZReadOnlyQuery read GetQueryObject;
- property FieldDefs: TFieldDefs read GetFieldDefs;
- property Fields: TFields read GetFields;
- property QueryFields[const AFieldName: String]: TField read FieldByName; default;
- property SQL: TStrings read GetSQL;
- property BOF: Boolean read GetBOF;
- property EOF: Boolean read GetEOF;
- property RowsAffected: Integer read GetRowsAffected;
- property RecordCount: Integer read GetRecordCount;
- property Prepared: Boolean read GetPrepared;
- property Params: TParams read GetParams;
- property ParamCheck: Boolean read GetParamCheck write SetParamCheck;
- property Connected: Boolean read GetConnected;
- property AutoCommit: Boolean read GetAutoCommit write SetAutoCommit;
- property Name: String read GetName write SetName;
- property RecordAccess: TRecordAccess read GetRecordAccess;
- property Post: ITransportVar read GetPost;
- property Where: ITransportVar read GetWhere;
- end;
I'm using reference counter in the Interface type to make coding faster by creating query interface object and after the interface object is unused then it's automatically freed and the database connection object, transaction object, the wrapped query object is deallocated and waiting for next use.
I develop two version one with ZeosDBO component and another one with SQLDb component and both working and very stable.
After I upgraded my application with this database connection pool the client request handling is faster than before.
I'm sorry about my bad english this is the first time i write article in english because maybe alot programmer outside of my country require this knowledge.
Searching Algorithms That I Often Use
As a Computer Science student I have learn a lot of algorithm that not only usable in programming buat also in my daily life. For example, I...
-
Daemon Application in Linux or Service Application in Windows is an application that running in the background, usually automatically starte...
-
One day I got a software project to create an application that need to be accessed from many computer with one data server. I decided to cre...
-
When I was in my college I work as part time programmer in a software house. I got a task to create a software updater. The software updater...



















