Skip to content

Instantly share code, notes, and snippets.

@pakLebah
Last active November 4, 2024 02:04
Show Gist options
  • Save pakLebah/277e0875a9ff50b9186fa9e166667add to your computer and use it in GitHub Desktop.
Save pakLebah/277e0875a9ff50b9186fa9e166667add to your computer and use it in GitHub Desktop.
Simple SQlite demo with Free Pascal.
program chinook;
{$MODE OBJFPC}{$H+}{$J-}
uses
CRT, Classes, SysUtils, SQLDB, SQLite3Conn;
var
sqlite3: TSQLite3Connection;
dbTrans: TSQLTransaction;
dbQuery: TSQLQuery;
slNames: TStringList;
quit: boolean = false;
function sqlDBError(const msg: string): string;
begin
// error message reformatting
result := 'ERROR: '+StringReplace(msg,'TSQLite3Connection : ','',[]);
end;
function openDB(const dbName: string): boolean;
begin
// create components
sqlite3 := TSQLite3Connection.Create(nil);
dbTrans := TSQLTransaction.Create(nil);
dbQuery := TSQLQuery.Create(nil);
slNames := TStringList.Create;
// setup components
sqlite3.Transaction := dbTrans;
dbTrans.Database := sqlite3;
dbQuery.Transaction := dbTrans;
dbQuery.Database := sqlite3;
slNames.CaseSensitive := false;
// setup db
sqlite3.DatabaseName := dbName;
sqlite3.HostName := 'localhost';
sqlite3.CharSet := 'UTF8';
// open db
if FileExists(dbName) then
try
sqlite3.Open;
result := sqlite3.Connected;
except
on E: Exception do
begin
sqlite3.Close;
writeln(sqlDBError(E.Message));
end;
end
else
begin
result := false;
writeln('Database file "',dbName,'" is not found.');
end;
end;
procedure closeDB;
begin
// disconnect
if sqlite3.Connected then
begin
dbTrans.Commit;
dbQuery.Close;
sqlite3.Close;
end;
// release
slNames.Free;
dbQuery.Free;
dbTrans.Free;
sqlite3.Free;
end;
procedure showTables(const clear: boolean = true);
var
i,j: integer;
begin
if clear then ClrScr;
// get and print list of tables
sqlite3.GetTableNames(slNames,false);
if slNames.Count > 0 then
begin
writeln('> "',sqlite3.DatabaseName,'" has ',slNames.Count,' table(s):');
j := 0;
for i := 0 to slNames.Count-1 do
// fix included system tables bug
if LowerCase(Copy(slNames[i],1,7)) <> 'sqlite_' then
begin
j := j+1;
writeln(j,'. ',slNames[i]);
end;
writeln('Found ',j,' data table(s).');
end
else
writeln('> "',sqlite3.DatabaseName,'" has no tables.');
end;
procedure execQuery(const sql: string);
begin
try
// execute command
dbQuery.SQL.Text := sql;
dbTrans.Active := true;
dbQuery.ExecSQL;
// print info
writeln('> Executing: "',sql,'"');
writeln('Affected ',dbQuery.RowsAffected,' record(s).');
// commit changes
dbTrans.Commit;
except
on E: Exception do
begin
// rollback changes
dbTrans.Rollback;
writeln(sqlDBError(E.Message));
end;
end;
end;
procedure openQuery(const sql: string);
var
i: integer;
begin
try
// fetch data
dbQuery.SQL.Text := sql;
dbQuery.Open;
if dbQuery.Active then
begin
ClrScr;
writeln('> Fetching: "',sql,'"');
// get and print headers
dbQuery.GetFieldNames(slNames);
for i := 0 to slNames.Count-1 do write('| ',UpperCase(slNames[i]),' ');
writeln;
// iterate rows
while not dbQuery.EOF do
begin
// print cell by column
for i := 0 to dbQuery.FieldCount-1 do
write('| ',dbQuery.Fields.Fields[i].AsString,' ');
writeln;
dbQuery.Next;
end;
// print summary
writeln('Found ',dbQuery.RecordCount,' record(s).');
end;
dbQuery.Close;
except
on E: Exception do writeln(sqlDBError(E.Message));
end;
end;
procedure showSchema(const sql: string);
var
i,p: integer;
t: string;
begin
// check table name
p := Pos(' ',sql);
if p = 0 then writeln('ERROR: Schema requires a table name.');
if p = 0 then exit;
// check table existence
sqlite3.GetTableNames(slNames,false);
t := LowerCase(Copy(sql, p+1, Length(sql)-p));
p := slNames.IndexOf(t);
if p = -1 then writeln('ERROR: Table "',t,'" is not found.');
if p = -1 then exit;
// fetch table schema
try
ClrScr;
writeln('> Schema of "',t,'"');
// schema command
dbQuery.SQL.Text := 'select name, sql from sqlite_master '+
'where type="table" and name="'+t+'"';
dbQuery.Open;
// print schema
writeln(dbQuery.Fields.Fields[1].AsString);
dbQuery.Close;
except
on E: Exception do writeln(sqlDBError(E.Message));
end;
end;
procedure runQuery;
var
p: integer;
q,s: string;
begin
if sqlite3.Connected then
begin
// query input
writeln('_____');
writeln('Enter SQL query:');
write('» ');
readln(q);
q := Trim(q);
// filter input to process custom commands
if q <> '' then
begin
// read first word
p := Pos(' ',q);
if p = 0 then p := Length(q) else p := p-1;
s := LowerCase(Copy(q,1,p));
// run query
if (s = 'quit') or (s = 'exit') then quit := true
else if (s = 'clear') and (p = Length(q)) then ClrScr
else if (s = 'tables') and (p = Length(q)) then showTables
else if s = 'schema' then showSchema(q)
else if s = 'select' then openQuery(q)
else execQuery(q);
end;
end;
end;
//# main program
begin
try
if openDB('chinook.db') then
begin
showTables(false);
repeat runQuery until quit;
end;
finally
closeDB;
end;
end.
@PierreCrette
Copy link

Thank you. Samples are too often difficult to find and most of them are for the GUI not the bare FPC.

@pakLebah
Copy link
Author

Thank you. The code is actually part of my SQLdb tutorial. It's written in Indonesian, but you may use Google Translate.

@billb2050
Copy link

Thanks, nice example. I found if you copy the chinook.lpr into a regular .pas file you can just use Free Pascal (fpc) to compile it. And the executable it will be half the size. At least that's true on Linux.

@pa-0
Copy link

pa-0 commented Nov 4, 2024

Hey would you please share the JSON snippet for that fpc/Execute Code task?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment