Repository URL to install this package:
Version:
3.0.0 ▾
|
{
This file is part of the Free Component Library (FCL)
Copyright (c) 2012 by the Free Pascal development team
SQLite-based index database
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit SQLiteIndexDB;
{$mode objfpc}{$H+}
interface
uses
SysUtils, fpIndexer, ctypes, sqlite3;
type
TDatabaseID = record
ID: int64;
Name: string;
end;
{ TSQLiteIndexDB }
TSQLiteIndexDB = class(TSQLIndexDB)
private
db: Psqlite3;
FFileName: string;
Frow: integer;
FSearchClass: TFPSearch;
LanguageID: TDatabaseID;
QueryResult: string;
SearchWordID: TDatabaseID;
URLID: TDatabaseID;
procedure CheckSQLite(Rc: cint; pzErrMsg: PChar);
protected
class function AllowForeignKeyInTable: boolean; override;
function GetFieldType(FieldType: TIndexField): string; override;
function GetLanguageID(const ALanguage: string): int64;
function GetURLID(const URL: string; ATimeStamp: TDateTime; ALanguageID: int64; DoCreate: boolean = True): int64; override;
function GetWordID(const AWord: string): int64; virtual;
function InsertLanguage(const ALanguage: string): int64; virtual;
function InsertURL(const URL: string; ATimeStamp: TDateTime; ALanguageID: int64): int64;
function InsertWord(const AWord: string): int64; virtual;
procedure Execute(const sql: string; ignoreErrors: boolean = True); override;
public
destructor Destroy; override;
procedure AddSearchData(ASearchData: TSearchWordData); override;
procedure BeginTrans; override;
procedure CommitTrans; override;
procedure CompactDB; override;
procedure Connect; override;
procedure CreateDB; override;
procedure DeleteWordsFromFile(URL: string); override;
procedure FindSearchData(SearchWord: TWordParser; FPSearch: TFPSearch; SearchOptions: TSearchOptions); override;
published
property FileName: string read FFileName write FFileName;
end;
implementation
function SearchCallback(_para1: pointer; plArgc: longint; argv: PPchar; argcol: PPchar): longint; cdecl;
var
PVal: ^PChar;
SearchRes: TSearchWordData;
begin
PVal := argv;
with SearchRes do
begin
Position := StrToInt64(PVal^); Inc(PVal);
URL := PVal^; Inc(PVal);
Context := PVal^; Inc(PVal);
SearchWord := PVal^; Inc(PVal);
FileDate := ISO8601ToDate(PVal^); Inc(PVal);
Language := PVal^;
end;
with TSQLiteIndexDB(_para1) do
begin
FSearchClass.AddResult(FRow, SearchRes);
Inc(Frow);
end;
Result := 0;
end;
function IndexCallback(_para1: pointer; plArgc: longint; argv: PPchar; argcol: PPchar): longint; cdecl;
begin
//store the query result
TSQLiteIndexDB(_para1).QueryResult := argv^;
Result := 0;
end;
{ TSQLiteIndexDB }
procedure TSQLiteIndexDB.Execute(const sql: string; ignoreErrors: boolean = True);
var
pzErrMsg: PChar;
rc: cint;
begin
QueryResult := '';
//Writeln('Executing : ',SQL);
rc := sqlite3_exec(db, PChar(sql), @IndexCallback, self, @pzErrMsg);
if not ignoreErrors then
CheckSQLite(rc, pzErrMsg);
end;
function TSQLiteIndexDB.GetURLID(const URL: string; ATimeStamp: TDateTime; ALanguageID: int64; DoCreate: boolean): int64;
var
SQL: string;
begin
if (URL = URLID.Name) then
Result := URLID.ID
else
begin
SQL := Format(GetURLSQL(False), [QuoteString(URL)]);
Execute(SQL, False);
Result := StrToInT64Def(QueryResult, -1);
if (Result = -1) and DoCreate then
Result := InsertURL(URL, ATimeStamp, ALanguageID);
URLID.ID := Result;
URLID.Name := URL;
end;
end;
function TSQLiteIndexDB.GetLanguageID(const ALanguage: string): int64;
var
SQL: string;
begin
if (ALanguage = LanguageID.Name) then
Result := LanguageID.ID
else
begin
SQL := Format(GetLanguageSQL(False), [QuoteString(Alanguage)]);
Execute(SQL, False);
Result := StrToInT64Def(QueryResult, -1);
if (Result = -1) then
Result := InsertLanguage(ALanguage);
LanguageID.ID := Result;
LanguageID.Name := ALanguage;
end;
end;
function TSQLiteIndexDB.GetWordID(const AWord: string): int64;
var
SQL: string;
begin
if (AWord = SearchWordID.Name) then
Result := SearchWordID.ID
else
begin
SQL := Format(GetWordSQL(False), [QuoteString(AWord)]);
Execute(SQL, False);
Result := StrToInT64Def(QueryResult, -1);
if (Result = -1) then
Result := InsertWord(AWord);
SearchWordID.ID := Result;
SearchWordID.Name := AWord;
end;
end;
function TSQLiteIndexDB.InsertWord(const AWord: string): int64;
begin
Execute(Format(InsertSQL(itWords, False), ['Null', QuoteString(AWord)]), False);
Result := sqlite3_last_insert_rowid(db);
end;
function TSQLiteIndexDB.InsertURL(const URL: string; ATimeStamp: TDateTime; ALanguageID: int64): int64;
begin
// ifFilesID,ifFilesURL,ifFilesReindex,ifFilesUpdated,ifFilesTimeStamp,ifFilesLanguageID
Execute(Format(InsertSQL(itFiles, False), ['Null', QuoteString(URL), '0', '0', QuoteString(DateToISO8601(ATimeStamp)), IntToStr(AlanguageID)]), False);
Result := sqlite3_last_insert_rowid(db);
end;
function TSQLiteIndexDB.InsertLanguage(const ALanguage: string): int64;
begin
Execute(Format(InsertSQL(itLanguages, False), ['Null', QuoteString(ALanguage)]), False);
Result := sqlite3_last_insert_rowid(db);
end;
function TSQLiteIndexDB.GetFieldType(FieldType: TIndexField): string;
begin
Result := inherited GetFieldType(FieldType);
if (Result = PrimaryFieldType) then
Result := 'INTEGER PRIMARY KEY NOT NULL';
end;
class function TSQLiteIndexDB.AllowForeignKeyInTable: boolean;
begin
Result := True;
end;
procedure TSQLiteIndexDB.DeleteWordsFromFile(URL: string);
begin
inherited DeleteWordsFromFile(URL);
//reset the cached URL ID
URLID.ID := -1;
URLID.Name := '';
end;
procedure TSQLiteIndexDB.CreateDB;
begin
Connect;
CreateIndexerTables;
end;
procedure TSQLiteIndexDB.Connect;
var
rc: cint;
begin
if (Filename = '') then
raise EFPIndexer.Create('Error: no filename specified');
rc := sqlite3_open(PChar(FFilename), @db);
if rc <> SQLITE_OK then
raise EFPIndexer.CreateFmt('Cannot open database: %s', [filename]);
end;
destructor TSQLiteIndexDB.Destroy;
begin
sqlite3_close(db);
inherited Destroy;
end;
procedure TSQLiteIndexDB.BeginTrans;
begin
Execute('BEGIN IMMEDIATE TRANSACTION');
end;
procedure TSQLiteIndexDB.CommitTrans;
begin
Execute('COMMIT TRANSACTION');
end;
procedure TSQLiteIndexDB.CompactDB;
begin
{$note this does not work, why?}
//Execute('VACUUM');
end;
procedure TSQLiteIndexDB.AddSearchData(ASearchData: TSearchWordData);
var
WID, LID, FID: int64;
SQL: string;
begin
WID := GetWordID(ASearchData.SearchWord);
LID := GetLanguageID(ASearchData.Language);
FID := GetURLID(ASearchData.URL, ASearchData.FileDate, LID, True);
SQL := InsertSQL(itMatches, False);
// ifMatchesID,ifMatchesWordId,ifMatchesFileID,ifMatchesLanguageID,ifMatchesPosition,ifMatchesContext,
SQL := Format(SQL, ['Null', IntToStr(WID), IntToStr(FID), IntToStr(LID), IntToStr(ASearchData.Position), QuoteString(ASearchData.Context)]);
//add to SearchWordList
Execute(SQL, False);
// Result:=sqlite3_last_insert_rowid(db);
end;
procedure TSQLiteIndexDB.CheckSQLite(Rc: cint; pzErrMsg: PChar);
var
S: string;
begin
if (rc <> SQLITE_OK) then
begin
if (pzErrMsg <> nil) then
S := strPas(pzErrMsg);
raise EFPIndexer.CreateFmt('SQLite error: %s', [S]);
end;
end;
procedure TSQLiteIndexDB.FindSearchData(SearchWord: TWordParser; FPSearch: TFPSearch; SearchOptions: TSearchOptions);
var
pzErrMsg: PChar;
rc: cint;
sql: string;
begin
FSearchClass := FPSearch;
Frow := 0;
sql := GetMatchSQL(SearchOptions, SearchWord, False);
//sql := Format(sql, [SearchWord]);
rc := sqlite3_exec(db, PChar(sql), @SearchCallback, self, @pzErrMsg);
CheckSQLite(rc, pzErrMsg);
end;
end.