📄 ibsql.pas
字号:
destructor TIBInputRawFile.Destroy;
begin
FFile.Free;
inherited Destroy;
end;
function TIBInputRawFile.ReadParameters: Boolean;
var
i: Integer;
BytesRead: DWord;
begin
result := False;
if Assigned(FFile) then
begin
for i := 0 to Params.Count - 1 do
begin
BytesRead := FFile.Read(Params[i].Data^.sqldata^, Params[i].Data^.sqllen);
if BytesRead <> DWORD(Params[i].Data^.sqllen) then
exit;
end;
result := True;
end;
end;
procedure TIBInputRawFile.ReadyFile;
begin
if Assigned(FFile) then
FreeAndNil(FFile);
FFile := TFileStream.Create(FileName, fmOpenRead);
end;
{ TIBSQL }
constructor TIBSQL.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FIBLoaded := False;
CheckIBLoaded;
FIBLoaded := True;
FGenerateParamNames := False;
FGoToFirstRecordOnExecute := True;
FBase := TIBBase.Create(Self);
FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
FBase.BeforeTransactionEnd := BeforeTransactionEnd;
FBOF := False;
FEOF := False;
FPrepared := False;
FRecordCount := 0;
FSQL := TStringList.Create;
TStringList(FSQL).OnChanging := SQLChanging;
FProcessedSQL := TStringList.Create;
FHandle := nil;
FSQLParams := TIBXSQLDA.Create(self);
FSQLRecord := TIBXSQLDA.Create(self);
FSQLType := SQLUnknown;
FParamCheck := True;
FCursor := Name + RandomString(8);
if AOwner is TIBDatabase then
Database := TIBDatabase(AOwner)
else
if AOwner is TIBTransaction then
Transaction := TIBTransaction(AOwner);
end;
destructor TIBSQL.Destroy;
begin
if FIBLoaded then
begin
if (FOpen) then
Close;
if (FHandle <> nil) then
FreeHandle;
FSQL.Free;
FProcessedSQL.Free;
FBase.Free;
FSQLParams.Free;
FSQLRecord.Free;
end;
inherited Destroy;
end;
procedure TIBSQL.BatchInput(InputObject: TIBBatchInput);
begin
if not Prepared then
Prepare;
InputObject.Params := Self.FSQLParams;
InputObject.ReadyFile;
if FSQLType in [SQLInsert, SQLUpdate, SQLDelete, SQLExecProcedure] then
while InputObject.ReadParameters do
ExecQuery;
end;
procedure TIBSQL.BatchOutput(OutputObject: TIBBatchOutput);
begin
CheckClosed;
if not Prepared then
Prepare;
if FSQLType = SQLSelect then begin
try
ExecQuery;
OutputObject.Columns := Self.FSQLRecord;
OutputObject.ReadyFile;
if not FGoToFirstRecordOnExecute then
Next;
while (not Eof) and (OutputObject.WriteColumns) do
Next;
finally
Close;
end;
end;
end;
procedure TIBSQL.CheckClosed;
begin
if FOpen then IBError(ibxeSQLOpen, [nil]);
end;
procedure TIBSQL.CheckOpen;
begin
if not FOpen then IBError(ibxeSQLClosed, [nil]);
end;
procedure TIBSQL.CheckValidStatement;
begin
FBase.CheckTransaction;
if (FHandle = nil) then
IBError(ibxeInvalidStatementHandle, [nil]);
end;
procedure TIBSQL.Close;
var
isc_res: ISC_STATUS;
begin
try
if (FHandle <> nil) and (SQLType = SQLSelect) and FOpen then
begin
isc_res := Call(
isc_dsql_free_statement(StatusVector, @FHandle, DSQL_close),
False);
if (StatusVector^ = 1) and (isc_res > 0) and
not CheckStatusVector(
[isc_bad_stmt_handle, isc_dsql_cursor_close_err]) then
IBDatabaseError;
end;
finally
FEOF := False;
FBOF := False;
FOpen := False;
FRecordCount := 0;
end;
end;
function TIBSQL.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
begin
result := 0;
if Transaction <> nil then
result := Transaction.Call(ErrCode, RaiseError)
else
if RaiseError and (ErrCode > 0) then
IBDataBaseError;
end;
function TIBSQL.Current: TIBXSQLDA;
begin
result := FSQLRecord;
end;
procedure TIBSQL.DoBeforeDatabaseDisconnect(Sender: TObject);
begin
if (FHandle <> nil) then
begin
Close;
FreeHandle;
end;
end;
procedure TIBSQL.ExecQuery;
var
fetch_res: ISC_STATUS;
begin
CheckClosed;
if not Prepared then
Prepare;
CheckValidStatement;
case FSQLType of
SQLSelect:
begin
Call(isc_dsql_execute2(StatusVector,
TRHandle,
@FHandle,
Database.SQLDialect,
FSQLParams.AsXSQLDA,
nil), True);
Call(
isc_dsql_set_cursor_name(StatusVector, @FHandle, PChar(FCursor), 0),
True);
FOpen := True;
FBOF := True;
FEOF := False;
FRecordCount := 0;
if FGoToFirstRecordOnExecute then
Next;
end;
SQLExecProcedure:
begin
fetch_res := Call(isc_dsql_execute2(StatusVector,
TRHandle,
@FHandle,
Database.SQLDialect,
FSQLParams.AsXSQLDA,
FSQLRecord.AsXSQLDA), False);
if (fetch_res <> 0) then
begin
if (fetch_res <> isc_lock_conflict) then
begin
{ Sometimes a prepared stored procedure appears to get
off sync on the server ....This code is meant to try
to work around the problem simply by "retrying". This
need to be reproduced and fixed.
}
isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
PChar(FProcessedSQL.Text), 1, nil);
Call(isc_dsql_execute2(StatusVector,
TRHandle,
@FHandle,
Database.SQLDialect,
FSQLParams.AsXSQLDA,
FSQLRecord.AsXSQLDA), True);
end
else
IBDataBaseError; // go ahead and raise the lock conflict
end;
end
else
Call(isc_dsql_execute(StatusVector,
TRHandle,
@FHandle,
Database.SQLDialect,
FSQLParams.AsXSQLDA), True)
end;
if not (csDesigning in ComponentState) then
MonitorHook.SQLExecute(Self);
end;
function TIBSQL.GetEOF: Boolean;
begin
result := FEOF or not FOpen;
end;
function TIBSQL.FieldByName(FieldName: String): TIBXSQLVAR;
var
i: Integer;
begin
i := GetFieldIndex(FieldName);
if (i < 0) then
IBError(ibxeFieldNotFound, [FieldName]);
result := GetFields(i);
end;
function TIBSQL.GetFields(const Idx: Integer): TIBXSQLVAR;
begin
if (Idx < 0) or (Idx >= FSQLRecord.Count) then
IBError(ibxeFieldNotFound, [IntToStr(Idx)]);
result := FSQLRecord[Idx];
end;
function TIBSQL.GetFieldIndex(FieldName: String): Integer;
begin
if (FSQLRecord.GetXSQLVarByName(FieldName) = nil) then
result := -1
else
result := FSQLRecord.GetXSQLVarByName(FieldName).Index;
end;
function TIBSQL.Next: TIBXSQLDA;
var
fetch_res: ISC_STATUS;
begin
result := nil;
if not FEOF then
begin
CheckOpen;
{ Go to the next record... }
fetch_res :=
Call(isc_dsql_fetch(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.AsXSQLDA), False);
if (fetch_res = 100) or (CheckStatusVector([isc_dsql_cursor_err])) then
begin
FEOF := True;
end
else
if (fetch_res > 0) then
begin
try
IBDataBaseError;
except
Close;
raise;
end;
end
else
begin
Inc(FRecordCount);
FBOF := False;
result := FSQLRecord;
end;
if not (csDesigning in ComponentState) then
MonitorHook.SQLFetch(Self);
end;
end;
procedure TIBSQL.FreeHandle;
var
isc_res: ISC_STATUS;
begin
try
{ The following two lines merely set the SQLDA count
variable FCount to 0, but do not deallocate
That way the allocations can be reused for
a new query sring in the same SQL instance }
FSQLRecord.Count := 0;
FSQLParams.Count := 0;
if FHandle <> nil then begin
isc_res :=
Call(isc_dsql_free_statement(StatusVector, @FHandle, DSQL_drop), False);
if (StatusVector^ = 1) and (isc_res > 0) and (isc_res <> isc_bad_stmt_handle) then
IBDataBaseError;
end;
finally
FPrepared := False;
FHandle := nil;
end;
end;
function TIBSQL.GetDatabase: TIBDatabase;
begin
result := FBase.Database;
end;
function TIBSQL.GetDBHandle: PISC_DB_HANDLE;
begin
result := FBase.DBHandle;
end;
function TIBSQL.GetPlan: String;
var
result_buffer: array[0..16384] of Char;
result_length, i: Integer;
info_request: Char;
begin
if (not Prepared) or
(not (FSQLType in [SQLSelect, SQLSelectForUpdate,
{TODO: SQLExecProcedure, }
SQLUpdate, SQLDelete])) then
result := ''
else
begin
info_request := Char(isc_info_sql_get_plan);
Call(isc_dsql_sql_info(StatusVector, @FHandle, 2, @info_request,
SizeOf(result_buffer), result_buffer), True);
if (result_buffer[0] <> Char(isc_info_sql_get_plan)) then
IBError(ibxeUnknownPlan, [nil]);
result_length := isc_vax_integer(@result_buffer[1], 2);
SetString(result, nil, result_length);
for i := 1 to result_length do
result[i] := result_buffer[i + 2];
result := Trim(result);
end;
end;
function TIBSQL.GetRecordCount: Integer;
begin
result := FRecordCount;
end;
function TIBSQL.GetRowsAffected: integer;
var
result_buffer: array[0..1048] of Char;
info_request: Char;
begin
if not Prepared then
result := -1
else begin
info_request := Char(isc_info_sql_records);
if isc_dsql_sql_info(StatusVector, @FHandle, 1, @info_request,
SizeOf(result_buffer), result_buffer) > 0 then
IBDatabaseError;
if (result_buffer[0] <> Char(isc_info_sql_records)) then
result := -1
else
case SQLType of
SQLUpdate: Result := isc_vax_integer(@result_buffer[6], 4);
SQLDelete: Result := isc_vax_integer(@result_buffer[13], 4);
SQLInsert: Result := isc_vax_integer(@result_buffer[27], 4);
else
Result := -1;
end ;
end;
end;
function TIBSQL.GetSQLParams: TIBXSQLDA;
begin
if not Prepared then
Prepare;
result := FSQLParams;
end;
function TIBSQL.GetTransaction: TIBTransaction;
begin
result := FBase.Transaction;
end;
function TIBSQL.GetTRHandle: PISC_TR_HANDLE;
begin
result := FBase.TRHandle;
end;
{
Preprocess SQL
Using FSQL, process the typed SQL and put the process SQL
in FProcessedSQL and parameter names in FSQLParams
}
procedure TIBSQL.PreprocessSQL;
var
cCurChar, cNextChar, cQuoteChar: Char;
sSQL, sProcessedSQL, sParamName: String;
i, iLenSQL, iSQLPos: Integer;
iCurState, iCurParamState: Integer;
iParamSuffix: Integer;
slNames: TStrings;
const
DefaultState = 0;
CommentState = 1;
QuoteState = 2;
ParamState = 3;
ParamDefaultState = 0;
ParamQuoteState = 1;
procedure AddToProcessedSQL(cChar: Char);
begin
sProcessedSQL[iSQLPos] := cChar;
Inc(iSQLPos);
end;
begin
slNames := TStringList.Create;
try
{ Do some initializations of variables }
iParamSuffix := 0;
cQuoteChar := '''';
sSQL := FSQL.Text;
iLenSQL := Length(sSQL);
SetString(sProcessedSQL, nil, iLenSQL + 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -