Extra Systems Database
unit database;
interface
uses
common, inet, windows, classes, sysutils;
type
TGetRecordResult = (grrOK, grrError, grrRecordDeleted);
HTABLE = Integer;
TESDBField = record
FieldName : String[MaxFieldNameLength - 1];
FieldType : Integer;
FieldLength : Integer;
FieldPosition : Integer;
end;
TESDBFieldsList = class(TObject)
private
FList : TList;
public
constructor Create;
destructor Destroy; override;
procedure AddItem(const Field:TESDBField);
function GetItem(const ItemNumber:Integer; var Field:TESDBField):Boolean;
function SetItem(const ItemNumber:Integer; const Field:TESDBField):Boolean;
function Count:Integer;
procedure Clear;
end;
TESDBTablesList = class(TObject)
private
FList : TList;
procedure AddItem(const Table:TESDBTableItem);
public
constructor Create;
destructor Destroy; override;
function GetItem(const ItemNumber:Integer; var Table:TESDBTableItem):Boolean;
function Count:Integer;
end;
TESDBDatabase = class(TObject)
private
FActive : Boolean;
FLockGate : Integer;
FSocket : TSocket;
procedure Lock;
procedure Unlock;
function FindField(const FieldName:String; var Field:TESDBField; const FieldsList:TESDBFieldsList):Boolean;
public
constructor Create;
destructor Destroy; override;
function CreateDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
function DropDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
function Open(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
procedure Close;
function CreateTable(const TableName:String; const FieldsList:TESDBFieldsList):Boolean;
function OpenTable(const TableName:String):HTABLE;
procedure CloseTable(const hTable:HTABLE);
function PackTable(const hTable:HTABLE):Boolean;
procedure GetTableStructure(const hTable:HTABLE; const FieldsList:TESDBFieldsList);
function PostTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):Boolean;
function GetTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):TGetRecordResult;
function DeleteTableRecord(const hTable:HTABLE; const RecordNumber:Integer):Boolean;
function GetServerInfo(var ServerInfo:TESDBServerInfo):Boolean;
function GetTotalRecordsCount(const hTable:HTABLE):Integer;
function GetRealRecordsCount(const hTable:HTABLE):Integer;
function GetStructure(var TablesList:TESDBTablesList):Boolean;
function DropTable(const TableName:String):Boolean;
function LocalBackup(const FileName:String):Boolean;
function RemoteBackup(const FileName:String):Boolean;
function SetIntegerField(const FieldName:String; const Value:Integer; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
function SetStringField(const FieldName:String; const Value:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
function SetDateTimeField(const FieldName:String; const Value:TDateTime; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
function GetIntegerField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Integer;
function GetStringField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):String;
function GetDateTimeField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):TDateTime;
property Active:Boolean read FActive;
end;
implementation
constructor TESDBDatabase.Create;
begin
inherited Create;
FLockGate:=0;
FActive:=False;
FSocket:=TSocket.Create(864000, 864000);
end;
destructor TESDBDatabase.Destroy;
begin
Close;
FSocket.Free;
inherited Destroy;
end;
procedure TESDBDatabase.Close;
begin
if not FActive then Exit;
FSocket.Close;
FActive:=False;
end;
procedure TESDBDatabase.Lock;
var
CurVal:Integer;
i:Integer;
j:Pointer;
begin
i:=0;
j:=@FLockGate;
while true do begin
asm
push eax
push edx
mov eax,1
mov edx,j
xchg eax,[edx]
mov CurVal,eax
pop edx
pop eax
end;
if (CurVal = 0) then Break;
Sleep(i);
i:=i + 1;
end;
end;
procedure TESDBDatabase.Unlock;
begin
FLockGate:=0;
end;
function TESDBDatabase.Open(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
var
i:Integer;
begin
Result:=False;
Close;
if not FSocket.Connect(@ServerAddress[1], ServerPort) then Exit;
i:=ESDB_COMMAND_CONNECT_DATABASE;
FSocket.SendBuffer(@i, 4);
i:=Length(DatabaseName);
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(@DatabaseName[1], i);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
if Result then begin
FActive:=True;
end else begin
FSocket.Close;
end;
end;
function TESDBDatabase.CreateDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
var
i:Integer;
begin
Result:=False;
if FActive then Exit;
if not FSocket.Connect(@ServerAddress[1], ServerPort) then Exit;
i:=ESDB_COMMAND_CREATE_DATABASE;
FSocket.SendBuffer(@i, 4);
i:=Length(DatabaseName);
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(@DatabaseName[1], i);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
FSocket.Close;
end;
function TESDBDatabase.DropDatabase(const DatabaseName,ServerAddress:String; const ServerPort:Integer):Boolean;
var
i:Integer;
begin
Result:=False;
if FActive then Exit;
if not FSocket.Connect(@ServerAddress[1], ServerPort) then Exit;
i:=ESDB_COMMAND_DROP_DATABASE;
FSocket.SendBuffer(@i, 4);
i:=Length(DatabaseName);
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(@DatabaseName[1], i);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
FSocket.Close;
end;
function TESDBDatabase.CreateTable(const TableName:String; const FieldsList:TESDBFieldsList):Boolean;
var
i,j:Integer;
FieldDescriptor:TFieldDescriptor;
Field:TESDBField;
begin
Result:=False;
if not FActive then Exit;
if (FieldsList.Count = 0) then Exit;
Lock;
i:=ESDB_COMMAND_CREATE_TABLE;
FSocket.SendBuffer(@i, 4);
i:=Length(TableName);
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(@TableName[1], i);
i:=FieldsList.Count;
FSocket.SendBuffer(@i, 4);
for i:=0 to FieldsList.Count - 1 do begin
FieldsList.GetItem(i, Field);
FieldDescriptor.FieldType:=Field.FieldType;
FieldDescriptor.FieldLength:=Field.FieldLength;
FillChar(FieldDescriptor.FieldName, Sizeof(FieldDescriptor.FieldName), 0);
j:=Length(Field.FieldName);
if (j > (Sizeof(FieldDescriptor.FieldName) - 1)) then j:=(Sizeof(FieldDescriptor.FieldName) - 1);
Move(Field.FieldName[1], FieldDescriptor.FieldName[1], j);
FSocket.SendBuffer(@FieldDescriptor, Sizeof(FieldDescriptor));
end;
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
Unlock;
end;
constructor TESDBFieldsList.Create;
begin
inherited Create;
FList:=TList.Create;
end;
destructor TESDBFieldsList.Destroy;
var
i:Integer;
begin
for i:=0 to FList.Count - 1 do FreeMem(FList[i]);
FList.Free;
inherited Destroy;
end;
procedure TESDBFieldsList.AddItem(const Field:TESDBField);
var
i:Pointer;
begin
GetMem(i, Sizeof(TESDBField));
if (i = nil) then Exit;
Move(Field, i^, Sizeof(TESDBField));
FList.Add(i);
end;
function TESDBFieldsList.GetItem(const ItemNumber:Integer; var Field:TESDBField):Boolean;
begin
Result:=False;
if ((ItemNumber < 0) or (ItemNumber >= FList.Count)) then Exit;
Result:=True;
Move(FList[ItemNumber]^, Field, Sizeof(TESDBField));
end;
function TESDBFieldsList.SetItem(const ItemNumber:Integer; const Field:TESDBField):Boolean;
begin
Result:=False;
if ((ItemNumber < 0) or (ItemNumber >= FList.Count)) then Exit;
Result:=True;
Move(Field, FList[ItemNumber]^, Sizeof(TESDBField));
end;
function TESDBFieldsList.Count:Integer;
begin
Result:=FList.Count;
end;
procedure TESDBDatabase.CloseTable(const hTable:HTABLE);
var
i:Integer;
begin
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_CLOSE_TABLE;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
Unlock;
end;
function TESDBDatabase.OpenTable(const TableName:String):HTABLE;
var
i:Integer;
begin
Result:=0;
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_OPEN_TABLE;
FSocket.SendBuffer(@i, 4);
i:=Length(TableName);
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(@TableName[1], i);
FSocket.ReadToBuffer(@Result, 4);
Unlock;
end;
procedure TESDBFieldsList.Clear;
begin
FList.Clear;
end;
procedure TESDBDatabase.GetTableStructure(const hTable:HTABLE; const FieldsList:TESDBFieldsList);
var
i,j:Integer;
FieldDescriptor:TFieldDescriptor;
Field:TESDBField;
begin
FieldsList.Clear;
Lock;
i:=ESDB_QUERY_TABLE_GET_STRUCTURE;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
if (i > 0) then begin
j:=(i div Sizeof(TFieldDescriptor));
i:=0;
while (i < j) do begin
FSocket.ReadToBuffer(@FieldDescriptor, Sizeof(TFieldDescriptor));
Field.FieldType:=FieldDescriptor.FieldType;
Field.FieldLength:=FieldDescriptor.FieldLength;
Field.FieldName:=StrPas(@FieldDescriptor.FieldName[1]);
FieldsList.AddItem(Field);
i:=i + 1;
end;
end;
Unlock;
end;
function TESDBDatabase.GetServerInfo(var ServerInfo:TESDBServerInfo):Boolean;
var
i:Integer;
begin
Result:=False;
if not FActive then Exit;
Lock;
i:=ESDB_QUERY_GETSERVERINFO;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
if (i = Sizeof(TESDBServerInfo)) then begin
Result:=True;
FSocket.ReadToBuffer(@ServerInfo, i);
end;
Unlock;
end;
function TESDBDatabase.PostTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):Boolean;
var
i:Integer;
begin
Result:=False;
if not FActive then Exit;
Lock;
if (RecordNumber = 0) then begin
i:=ESDB_COMMAND_TABLE_APPEND_RECORD;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
end else begin
i:=ESDB_COMMAND_TABLE_SET_THIS_RECORD;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
i:=RecordNumber;
FSocket.SendBuffer(@i, 4);
end;
i:=RecordSize;
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(RecordBuffer, i);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
Unlock;
end;
function TESDBDatabase.DeleteTableRecord(const hTable:HTABLE; const RecordNumber:Integer):Boolean;
var
i:Integer;
begin
Result:=False;
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_TABLE_DELETE_THIS_RECORD;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
i:=RecordNumber;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
Unlock;
end;
function TESDBDatabase.GetTableRecord(const hTable:HTABLE; const RecordNumber:Integer; const RecordBuffer:Pointer; const RecordSize:Integer):TGetRecordResult;
var
i:Integer;
begin
Result:=grrError;
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_TABLE_GET_THIS_RECORD;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
i:=RecordNumber;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
if (i = RecordSize) then begin
if (FSocket.ReadToBuffer(RecordBuffer, i) = RecordSize) then Result:=grrOK;
end else begin
if (i = ESDB_RESULT_ERROR_RECORDDELETED) then Result:=grrRecordDeleted;
end;
Unlock;
end;
function TESDBDatabase.GetTotalRecordsCount(const hTable:HTABLE):Integer;
var
i:Integer;
begin
Result:=0;
if not FActive then Exit;
Lock;
i:=ESDB_QUERY_TABLE_TOTAL_RECORDS_COUNT;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
if (i > 0) then Result:=i;
Unlock;
end;
function TESDBDatabase.GetRealRecordsCount(const hTable:HTABLE):Integer;
var
i:Integer;
begin
Result:=0;
if not FActive then Exit;
Lock;
i:=ESDB_QUERY_TABLE_REAL_RECORDS_COUNT;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
if (i > 0) then Result:=i;
Unlock;
end;
constructor TESDBTablesList.Create;
begin
inherited Create;
FList:=TList.Create;
end;
destructor TESDBTablesList.Destroy;
var
i:Integer;
begin
i:=0;
while (i < FList.Count) do begin
FreeMem(FList.Items[i]);
i:=i + 1;
end;
FList.Free;
inherited Destroy;
end;
procedure TESDBTablesList.AddItem(const Table:TESDBTableItem);
var
i:Pointer;
begin
GetMem(i, Sizeof(TESDBTableItem));
if (i = nil) then Exit;
Move(Table, i^, Sizeof(TESDBTableItem));
FList.Add(i);
end;
function TESDBTablesList.GetItem(const ItemNumber:Integer; var Table:TESDBTableItem):Boolean;
begin
Result:=False;
if ((ItemNumber < 0) or (ItemNumber >= FList.Count)) then Exit;
Result:=True;
Move(FList[ItemNumber]^, Table, Sizeof(TESDBTableItem));
end;
function TESDBTablesList.Count:Integer;
begin
Result:=FList.Count;
end;
function TESDBDatabase.GetStructure(var TablesList:TESDBTablesList):Boolean;
var
i,j:Integer;
TableItem:TESDBTableItem;
begin
Result:=False;
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_DATABASE_GETSTRUCTURE;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
if (i >= 0) then begin
Result:=True;
TablesList.FList.Clear;
j:=i div Sizeof(TESDBTableItem);
i:=0;
while (i < j) do begin
FSocket.ReadToBuffer(@TableItem, Sizeof(TESDBTableItem));
TablesList.AddItem(TableItem);
i:=i + 1;
end;
end;
Unlock;
end;
function TESDBDatabase.DropTable(const TableName:String):Boolean;
var
i:Integer;
begin
Result:=False;
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_DROP_TABLE;
FSocket.SendBuffer(@i, 4);
i:=Length(TableName);
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(@TableName[1], i);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
Unlock;
end;
function TESDBDatabase.LocalBackup(const FileName:String):Boolean;
var
i:Integer;
hFile:Cardinal;
CurBuf:array[1..512] of Char;
BufSiz:Integer;
k:array[1..2] of DWORD;
j:Pointer;
begin
Result:=False;
if not FActive then Exit;
hFile:=_lcreat(@FileName[1], 0);
if (hFile = HFILE_ERROR) then Exit;
Lock;
i:=ESDB_COMMAND_DATABASE_LOCAL_BACKUP;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@k[1], 8);
if ((k[1] <> 0) or (k[2] <> 0)) then begin
Result:=True;
j:=@k[1];
while ((k[1] <> 0) or (k[2] <> 0)) do begin
BufSiz:=FSocket.ReadToBuffer(@CurBuf[1], Sizeof(CurBuf));
if (BufSiz <= 0) then begin
Result:=False;
Break;
end;
_lwrite(hFile, @CurBuf[1], BufSiz);
asm
push eax
push edx
push ebx
mov ebx,j
mov eax,[ebx]
mov edx,[ebx + 4]
sub eax,BufSiz
sbb edx,0
mov [ebx],eax
mov [ebx + 4],edx
pop ebx
pop edx
pop eax
end;
end;
end;
Unlock;
_lclose(hFile);
if not Result then DeleteFile(FileName);
end;
function TESDBDatabase.RemoteBackup(const FileName:String):Boolean;
var
i:Integer;
begin
Result:=False;
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_DATABASE_REMOTE_BACKUP;
FSocket.SendBuffer(@i, 4);
i:=Length(FileName);
FSocket.SendBuffer(@i, 4);
FSocket.SendBuffer(@FileName[1], i);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
Unlock;
end;
function TESDBDatabase.PackTable(const hTable:HTABLE):Boolean;
var
i:Integer;
begin
Result:=False;
if not FActive then Exit;
Lock;
i:=ESDB_COMMAND_PACK_TABLE;
FSocket.SendBuffer(@i, 4);
i:=hTable;
FSocket.SendBuffer(@i, 4);
FSocket.ReadToBuffer(@i, 4);
Result:=(i = ESDB_RESULT_OK);
Unlock;
end;
function TESDBDatabase.FindField(const FieldName:String; var Field:TESDBField; const FieldsList:TESDBFieldsList):Boolean;
var
i:Integer;
begin
i:=0;
Result:=False;
while ((i < FieldsList.Count) and (not Result)) do begin
if FieldsList.GetItem(i, Field) then begin
Result:=(AnsiUpperCase(FieldName) = AnsiUpperCase(Field.FieldName));
end;
i:=i + 1;
end;
end;
function TESDBDatabase.SetIntegerField(const FieldName:String; const Value:Integer; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
var
FieldAddress:Pointer;
Field:TESDBField;
begin
Result:=False;
if not FindField(FieldName, Field, FieldsList) then Exit;
if (Field.FieldType <> FIELD_TYPE_INTEGER) then Exit;
FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
Move(Value, FieldAddress^, 4);
Result:=True;
end;
function TESDBDatabase.SetStringField(const FieldName:String; const Value:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
var
FieldAddress:Pointer;
Field:TESDBField;
i,j:Integer;
begin
Result:=False;
if not FindField(FieldName, Field, FieldsList) then Exit;
if (Field.FieldType <> FIELD_TYPE_CHAR) then Exit;
FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
i:=Length(Value);
j:=Field.FieldLength - 1;
if (j <= 0) then Exit;
if (i > j) then i:=j;
FillChar(FieldAddress^, Field.FieldLength, 0);
Move(Value[1], FieldAddress^, i);
Result:=True;
end;
function TESDBDatabase.SetDateTimeField(const FieldName:String; const Value:TDateTime; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Boolean;
var
FieldAddress:Pointer;
Field:TESDBField;
FieldData:_SYSTEMTIME;
begin
Result:=False;
if not FindField(FieldName, Field, FieldsList) then Exit;
if (Field.FieldType <> FIELD_TYPE_DATE) then Exit;
DateTimeToSystemTime(Value, FieldData);
FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
Move(FieldData, FieldAddress^, Sizeof(_SYSTEMTIME));
Result:=True;
end;
function TESDBDatabase.GetIntegerField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):Integer;
var
Field:TESDBField;
FieldAddress:PInteger;
begin
Result:=0;
if not FindField(FieldName, Field, FieldsList) then Exit;
if (Field.FieldType <> FIELD_TYPE_INTEGER) then Exit;
FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
Result:=FieldAddress^;
end;
function TESDBDatabase.GetStringField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):String;
var
Field:TESDBField;
FieldAddress:PChar;
begin
Result:='';
if not FindField(FieldName, Field, FieldsList) then Exit;
if (Field.FieldType <> FIELD_TYPE_CHAR) then Exit;
FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
Result:=StrPas(FieldAddress);
end;
function TESDBDatabase.GetDateTimeField(const FieldName:String; const FieldsList:TESDBFieldsList; const RecordBuffer:Pointer):TDateTime;
var
Field:TESDBField;
FieldAddress:Pointer;
FieldData:_SYSTEMTIME;
begin
Result:=0;
if not FindField(FieldName, Field, FieldsList) then Exit;
if (Field.FieldType <> FIELD_TYPE_DATE) then Exit;
FieldAddress:=Pointer(Longint(RecordBuffer) + Field.FieldPosition);
Move(FieldAddress^, FieldData, Sizeof(_SYSTEMTIME));
Result:=SystemTimeToDateTime(FieldData);
end;
end.