D. Prameswara
D. Prameswara Tukang ketik yang sedang belajar pemrograman #linux #android #vue #node.js

Membuat aplikasi pembackup database Postgresql dengan Lazarus/Freepascal

Pada kesempatan kali ini kita akan membuat sebuah aplikasi sederhana yang berfungsi untuk membackup database-dalam hal ini Postgresql- tentu saja dengan menggunakan Lazarus dan Freepascal. Aplikasi ini sangat sederhana yang berfokus pada bagaimana memanggil sebuah aplikasi eksternal dengan parameter-parameter tertentu.
Secara detail, hal-hal yang akan kita pelajari dari pembuatan aplikasi ini adalah bagaimana :
  • memanggil aplikasi eksternal
  • menentukan parameter-parameternya
  • mensetting environment variable
  • menulis pada input stream
  • membaca pesan/verbose output proses
Ok, langsung saja, seperti biasa silahkan membuat project baru di Lazarus anda, dan buatlah 1 buah form dengan tampilan kira-kira seperti di bawah ini.
Membuat aplikasi pembackup database Postgresql dengan Lazarus/Freepascal, TProcess, pg_dump
Alur aplikasi ini kita buat sederhana saja. Hanya ada 1 tombol dan 1 memo. User akan menekan tombol tersebut yang kemudian memanggil tool/aplikasi backup database. Hasil verbose proses kemudian akan ditampilkan ke dalam memo. Jika proses backup berhasil, maka akan memunculkan sebuah dialog OK begitupun dengan gagal, akan memunculkan dialog Error.
Buatlah event on click pada tombol tersebut, dengan cara double klik tombol tersebut pada tampilan desain form. Hasilnya seperti di bawah ini.
procedure TForm1.Button1Click(Sender: TObject);
begin

end;
Sebelum coding lebih lanjut, untuk memanggil aplikasi eksternal, kita dapat menggunakan beberapa cara (silahkan googling jika ingin tahu). Cara yang akan kita gunakan kali ini adalah menggunakan kelas TProsess. Oleh karena itu, pada bagian atas, silahkan tambahkan unit proses, seperti contoh di bawah ini.
interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  process;

Sekarang kembali ke dalam procedure TForm1.Button1Click di bagian bawah. Tekan tombol Ctrl h untuk kembali ke posisi cursor sebelumnya. Deklarasikan sebuah variable dengan tipe TProcess yang akan digunakan untuk menjalankan aplikasi eksternal, misalkan diberi nama lProc.
NOTE: saya terbiasa memberikan prefix l di depan variabel lokal dengan tujuan agar mudah dikenali. Dan Anda tidak harus mengikuti kebiasaan ini.
procedure TForm1.Button1Click(Sender: TObject);
var
  lProc: TProcess;
begin
  lProc :=TProcess.Create(nil);  
lProc :=TProcess.Create(nil); Artinya kita meng-instansiasi kelas TProcess menjadi object lProc, dimana object tersebut tidak ada yang memiliki (nil).
Kenapa Nil dan bukan Self atau yang lain, jawabannya karena object tersebut akan kita destroy menual, sehingga Nil, Self atau yang lain tidak akan menjadi soal.
Kemudian tambahkan option proses poUsePipes agar TProcess membaca verbose proses. Jika anda tidak ingin membaca output proses, maka option ini bisa anda hilangkan. 
  lProc :=TProcess.Create(Self);
  lProc.Options:=[poUsePipes];
Sekarang bagian yang paling penting. Tool/aplikasi backup database Postgres yang biasa digunakan adalah pg_dump. Format perintahnya kira-kira seperti di bawah ini.
pg_dump -U [nama_user] -h [host] -p [port] -F [format backup] -v -f [nama file] [nama_database]
-v : verbose, ini harus untuk memunculkan log proses backup
Contoh
pg_dump -U postgres -h localhost -p 5432 -F custom -v -b -f file_backup.backup db_sekolah
Property TProcess pertama yang harus anda tentukan adalah nama executable nya, dalam hal ini adalah pg_dump
  lProc.Executable:='pg_dump';

Bisa dilakukan dengan cara seperti di atas, akan tetapi hal ini tidak disarankan. Nama executable harus beserta absolute path nya. Misalkan jika di Linux, file pg_dump biasanya berada pada /usr/bin/pg_dump.
Nah, Untuk mendapatkan file beserta absolute pathnya, bisa dilakukan dengan menggunakan fungsi FindDefaultExecutablePath . Oleh karena itu, kita modifikasi kode di atas menjadi seperti di bawah ini.
var
  lProc: TProcess;
  lApp          : String;
begin
  lApp :=FindDefaultExecutablePath('pg_dump');
  if lApp='' then
  begin
     ShowMessage('pg_dump tidak ditemukan');
     exit;
  end;

  lStr :=TStringStream.Create('');
  lProc :=TProcess.Create(nil);
  lProc.Options:=[poUsePipes];
  lProc.Executable:=lApp;

Perhatikan kode di atas, kita tambahkan variabel lApp yang menyimpan file executable aplikasi beserta absolute pathnya. Jika aplikasi tidak ditemukan, maka tidak perlu dilanjutkan, langsung exit saja dengan memunculkan error dialog;
Selanjutnya tambahkan parameter-parameternya ke dalam property parameter.
  lProc :=TProcess.Create(nil);
  lProc.Options:=[poUsePipes];
  lProc.Executable:=lApp;

  lProc.Parameters.Add('-U');
  lProc.Parameters.Add('postgres');
  lProc.Parameters.Add('-h');
  lProc.Parameters.Add('localhost');
  lProc.Parameters.Add('-p');
  lProc.Parameters.Add('5432');
  lProc.Parameters.Add('-F');
  lProc.Parameters.Add('custom');
  lProc.Parameters.Add('-b');
  lProc.Parameters.Add('-v');
  lProc.Parameters.Add('-f');
  lProc.Parameters.Add('latihan.backup');
  lProc.Parameters.Add('db_sekolah');

Pada Contoh kode di atas, kita akan membackup database yang bernama db_sekolah yang berada di localhost dengan menggunakan user postgres dan hasilnya disimpan ke dalam file latihan.backup yang berada di dalam folder yang sama dengan aplikasi ini. Silahkan sesuaikan dengan setting anda.
Pada saat proses ini dieksekusi, kita akan membaca output yang dihasilkan pg_dump. Output proses ini dapat dibaca melalui Output stream atau Stderr stream. Untuk aplikasi ini, kita menggunakan Stderr. Oleh karena itu, siapkan terlebih dahulu variable-variable yang akan digunakan untuk membaca stream, yaitu :
procedure TForm1.Button1Click(Sender: TObject);
const
  BUF_SIZE = 2048; // Buffer size for reading the output in chunks
var
  lProc         : TProcess;
  lApp          : String;

  lBytesRead    : longint;
  lBuffer       : array[1..BUF_SIZE] of byte;
  lStr          : TStringStream;
begin
BUF_SIZE : konstanta ukuran buffer, biasanya = 2048 bit
lBytesRead : digunakan untuk mencatat berapa byte stream yang berhasil di baca. Jika gagal atau stream sudah habis, maka akan menghasilkan nilai 0
lBuffer : digunakan sebagai penyimpan stream sementara
lStr : digunakan sebagai penyimpan stream Stderr sebagai string
Selanjutnya lakukan eksekusi proses dan baca hasilnya.
...
  lProc.Parameters.Add('-f');
  lProc.Parameters.Add('latihan.backup');
  lProc.Parameters.Add('db_sekolah');

  // eksekusi proses
  lProc.Execute;

  // lakukan pembacaan output dari Stderr Stream sampai selesai
  repeat
    // baca stream output dan simpan ke dalam buffer
    lBytesRead :=lProc.Stderr.Read(lBuffer,BUF_SIZE);

    // simpan stream sebagai string
    lStr.Write(lBuffer,lBytesRead);

    // simpan verbose ke dalam Memo
    Memo1.Text:=lStr.DataString;
    // refresh form agar perubahan isi Memo terlihat langsung
    Application.ProcessMessages;

    // stream habis ditandai dengan byteread = 0
  until lBytesRead=0;

  if lProc.ExitStatus=0 then
     ShowMessage('Proses OK')
  else
    ShowMessage('Proses Fail');

  // cleanup memori yang sudah tidak diperlukan
  lProc.Free;
  lStr.Free;
end;  
Output/hasil verbose aplikasi eksternal dapat dibaca via Output Stream atau Stderr Stream. Untuk kasus ini, hasil verbose dibaca dari Stderr Steam.
Application.ProcessMessages diperlukan untuk memaksa refresh form agar tampilan Memo diupdate. Jika anda tidak menambahkan ini, maka selama proses Memo akan tetap terlihat kosong, dan baru akan berubah setelah proses backup selesai
Untuk mengetahui apakah eksternal aplikasi apakah berjalan dengan baik, salah satunya dapat diketahui dari ExitStatus-nya. Biasanya jika berjalan dengan baik/sukses maka ExitStatus-nya pasti 0. Sedangkan selain itu maka artinya terjadi error.

Sampai di sini, aplikasi ini sudah bisa berjalan, asalkan database tersebut dapat di akses tanpa password.

Jika database anda dapat diakses tanpa menggunakan password, maka silahkan anda compile dan jalankan aplikasi ini. Jika semuanya lancar, maka aplikasi ini sudah bisa membackup database yang anda inginkan.
Lalu bagaimana jika database tersebut hanya dapat diakses dengan password ? Maka kita harus menambahkan “informasi” password tersebut ke dalam aplikasi kita. Ada 2 cara yang saya ketahui, yaitu dengan menggunakan Environment variable dan dengan menginput password dengan input stream.
  1. Menginput password dengan Environment Variable
    Ini adalah salah satu fitur dari Posgresql, dan belum tentu ada pada database lain. Untuk menginput password dalam koneksi ke Postgresql, dapat dengan menggunakan Environment variable yang bernama PGPASSWORD. Artinya, jika PGPASSWORD terdapat di dalam environment variable, maka Postgresql akan menggunakannya sebagai password untuk koneksi. Untuk informasi lebih lanjut, silahkan melihat di sini : Menggunakan PGPASSWORD
    Jadi, yang akan kita lakukan adalah menambahkan PGPASSWORD ke dalam environment variable Process. Caranya adalah sebagai berikut.
      lProc.Environment.Append('PGPASSWORD=rahasia');
      lProc.Execute;
    

    Kode di atas artinya, kita menambahkan sebuah environment variable yang bernama PGPASSWORD yang bernilai rahasiarahasia ini adalah contoh password dari user postgres.
    Cara ini telah saya coba dan berhasil berjalan di lingkungan Linux. Akan tetapi akan GAGAL di lingkungan Windows.
    Jika cara ini gagal, maka hal ini kemungkinan dikarenakan semua environment variable telah dihilangkan dan diganti hanya dengan PGPASSWORD pada awal proses append Environment. Oleh karena itu, cara mengatasinya adalah dengan menambahkan terlebih dahulu semua environment variable yang ada, baru kemudian menambahkan PGPASSWORD. Caranya adalah sebagai berikut.
    Tambahkan variabel baru untuk melakukan looping. Kemudian looping dan baca semua environment variable yang ada.
    var 
      ...
      lIdx          : integer;
      ...
    begin
    
    
      ...
      // baca semua environment variable yang ada dengan looping
      for lIdx:=0 to GetEnvironmentVariableCount-1 do
      begin
        // tambahkan ke property Environment dari TProcess
        lProc.Environment.Append(GetEnvironmentString(lIdx));
      end;
      // tambahkan PGPASSWORD yang berisi password user
      lProc.Environment.Append('PGPASSWORD=rahasia');
    
      // eksekusi proses
      lProc.Execute;
      ...
    
    
    
    Sampai di sini seharusnya kita sudah bisa menjalankan aplikasi backup dengan dengan baik. 
    Berdasarkan link Postgresql di atas. Teknik ini tidak direkomendasikan dikarenakan masalah keamanan password. Password yang tersimpan di dalam environment variable ini secara teori bisa dilihat oleh user dengan menggunakan perintah ps.
  2. Menginput password dengan Input stream
    Pada saat anda mengakses suatu database yang dilindungi dengan password, maka akan muncul prompt input password. Nah prompt input ini pada TProcess dapat diakses atau ditulisi dengan menggunakan stream Input. Berikut ini adalah caranya:
    Tambahkan variable untuk menyimpan nilai password.
    var
    ... 
      lPass         : string;
    ...
    

    Setelah TProcess dieksekusi, tulis/kirimkan password tersebut ke dalam pg_dump via Input stream.
    ...
    
    // eksekusi proses
    lProc.Execute;
    
    // input password, ingat tambahkan LineEnding di akhir password sebagi tanda
    // akhir password
    lPass :='rahasia'+LineEnding;
    
    // kirimkan password ke pg_dump via input stream
    lProc.Input.Write(lPass[1],Length(lPass));
    
    ...
    
    

    Sampai di sini, jika semua lancar maka aplikasi anda akan dapat berjalan dengan baik. Silakan anda coba.
    Untuk Linux, jika anda menggunakan cara ini, maka JANGAN jalankan aplikasi tersebut via terminal, melainkan langsung ke aplikasi-nya dengan cara double klik. Karena jika anda menjalankan via terminal, maka input akan diredirect/muncul di terminal sehingga anda harus menginput password di sana.
Membuat aplikasi pembackup database Postgresql dengan Lazarus/Freepascal, TProcess, pg_dump
Gagal membackup database
Membuat aplikasi pembackup database Postgresql dengan Lazarus/Freepascal, TProcess, pg_dump
Berhasil membackup database

Berikut ini adalah kode program eksekusi secara lengkap.
procedure TForm1.Button1Click(Sender: TObject);
const
  BUF_SIZE = 2048; // Buffer size for reading the output in chunks
var
  lProc         : TProcess;
  lBytesRead    : longint;
  lBuffer       : array[1..BUF_SIZE] of byte;
  lStr          : TStringStream;
  lApp          : String;

  lPass         : string;

  lIdx          : integer;
  lEnv          : TStringList;

begin

  lApp :=FindDefaultExecutablePath('pg_dump');
  if lApp='' then
  begin
     ShowMessage('pg_dump tidak ditemukan');
     exit;
  end;

  lStr :=TStringStream.Create('');

  lProc :=TProcess.Create(nil);
  lProc.Options:=[poUsePipes];

  lProc.Executable:=lApp;
  lProc.Parameters.Add('-U');
  lProc.Parameters.Add('postgres');
  lProc.Parameters.Add('-h');
  lProc.Parameters.Add('localhost');
  lProc.Parameters.Add('-p');
  lProc.Parameters.Add('5432');
  lProc.Parameters.Add('-F');
  lProc.Parameters.Add('custom');
  lProc.Parameters.Add('-b');
  lProc.Parameters.Add('-v');
  lProc.Parameters.Add('-f');
  lProc.Parameters.Add('latihan.backup');
  lProc.Parameters.Add('db_sekolah');

  { Cara ini adalah dengan menggunakan Environmen variable
  // baca semua environment variable yang ada dengan looping
  for lIdx:=0 to GetEnvironmentVariableCount-1 do
  begin
    // tambahkan ke property Environment dari TProcess
    lProc.Environment.Append(GetEnvironmentString(lIdx));
  end;
  // tambahkan PGPASSWORD yang berisi password user
  lProc.Environment.Append('PGPASSWORD=rahasia');
  }

  // eksekusi proses
  lProc.Execute;

  { Cara ini adalah dengan menggunakan input stream}
  // input password, ingat tambahkan LineEnding di akhir password sebagi tanda
  // akhir password
  lPass :='rahasia'+LineEnding;
  // kirimkan password ke pg_dump via input stream
  lProc.Input.Write(lPass[1],Length(lPass));

  // lakukan pembacaan output sampai selesai
  repeat
    // baca stram output dan simpan ke dalam buffer
    lBytesRead :=lProc.Stderr.Read(lBuffer,BUF_SIZE);

    // simpan stream sebagai string
    lStr.Write(lBuffer,lBytesRead);

    // simpan verbose ke dalam Memo
    Memo1.Text:=lStr.DataString;
    // refresh form agar perubahan isi Memo terlihat langsung
    Application.ProcessMessages;

    // stream habis ditandai dengan byteread = 0

  until lBytesRead=0;

  if lProc.ExitStatus=0 then
     ShowMessage('Proses OK')
  else
    ShowMessage('Proses Fail');

  // cleanup memori yang sudah tidak diperlukan
  lProc.Free;
  lStr.Free;
end;

Selanjutnya, silahkan modifikasi aplikasi anda sesuai dengan keperluan anda. Misalkan, informasi hostnameport dan lain-lain tidak dihardcoded, melainkan dibaca dari suatu file config.
Sekian tip/trik kali ini. Selamat mencoba, semoga yang sedikit ini bermanfaat.
D. Prameswara
D. Prameswara Tukang ketik yang sedang belajar pemrograman #linux #android #vue #node.js
Load comments