mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-11 10:26:05 +02:00
* Implemented RefreshSQL and fetching value for AutoInc fields
git-svn-id: trunk@29183 -
This commit is contained in:
parent
2bf0abcdfa
commit
6d5622aca3
@ -512,6 +512,7 @@ type
|
||||
procedure RemoveRecordFromIndexes(const ABookmark : TBufBookmark);
|
||||
protected
|
||||
// abstract & virtual methods of TDataset
|
||||
procedure ActiveBufferToRecord;
|
||||
procedure SetPacketRecords(aValue : integer); virtual;
|
||||
procedure UpdateIndexDefs; override;
|
||||
procedure SetRecNo(Value: Longint); override;
|
||||
@ -2540,8 +2541,7 @@ begin
|
||||
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
move(ActiveBuffer^,FCurrentIndex.CurrentBuffer^,FRecordSize);
|
||||
ActiveBufferToRecord;
|
||||
|
||||
// new data are now in current record so reorder current record if needed
|
||||
for i := 1 to FIndexesCount-1 do
|
||||
@ -2549,6 +2549,12 @@ begin
|
||||
FIndexes[i].OrderCurrentRecord;
|
||||
end;
|
||||
|
||||
procedure TCustomBufDataset.ActiveBufferToRecord;
|
||||
|
||||
begin
|
||||
move(ActiveBuffer^,FCurrentIndex.CurrentBuffer^,FRecordSize);
|
||||
end;
|
||||
|
||||
procedure TCustomBufDataset.CalcRecordSize;
|
||||
|
||||
var x : longint;
|
||||
|
@ -61,7 +61,7 @@ type
|
||||
TUpdateMode = (upWhereAll, upWhereChanged, upWhereKeyOnly);
|
||||
TResolverResponse = (rrSkip, rrAbort, rrMerge, rrApply, rrIgnore);
|
||||
|
||||
TProviderFlag = (pfInUpdate, pfInWhere, pfInKey, pfHidden);
|
||||
TProviderFlag = (pfInUpdate, pfInWhere, pfInKey, pfHidden, pfRefreshOnInsert,pfRefreshOnUpdate);
|
||||
TProviderFlags = set of TProviderFlag;
|
||||
|
||||
{ Forward declarations }
|
||||
@ -1232,6 +1232,19 @@ type
|
||||
end;
|
||||
TParamClass = Class of TParam;
|
||||
|
||||
{ TParamsEnumerator }
|
||||
|
||||
TParamsEnumerator = class
|
||||
private
|
||||
FPosition: Integer;
|
||||
FParams: TParams;
|
||||
function GetCurrent: TParam;
|
||||
public
|
||||
constructor Create(AParams: TParams);
|
||||
function MoveNext: Boolean;
|
||||
property Current: TParam read GetCurrent;
|
||||
end;
|
||||
|
||||
{ TParams }
|
||||
|
||||
TParams = class(TCollection)
|
||||
@ -1256,6 +1269,7 @@ type
|
||||
Function FindParam(const Value: string): TParam;
|
||||
Procedure GetParamList(List: TList; const ParamNames: string);
|
||||
Function IsEqual(Value: TParams): Boolean;
|
||||
Function GetEnumerator: TParamsEnumerator;
|
||||
Function ParamByName(const Value: string): TParam;
|
||||
Function ParseSQL(SQL: String; DoCreate: Boolean): String; overload;
|
||||
Function ParseSQL(SQL: String; DoCreate, EscapeSlash, EscapeRepeat : Boolean; ParameterStyle : TParamStyle): String; overload;
|
||||
@ -2216,6 +2230,7 @@ begin
|
||||
Pos:=i;
|
||||
end;
|
||||
|
||||
|
||||
{ EUpdateError }
|
||||
constructor EUpdateError.Create(NativeError, Context : String;
|
||||
ErrCode, PrevError : integer; E: Exception);
|
||||
|
@ -117,7 +117,10 @@ Resourcestring
|
||||
SErrNoImplicitTransaction = 'Connection %s does not allow implicit transactions.';
|
||||
SErrImplictTransactionStart = 'Error: attempt to implicitly start a transaction on Connection "%s", transaction "%s".';
|
||||
SErrImplicitConnect = 'Error: attempt to implicitly activate connection "%s".';
|
||||
SErrFailedToUpdateRecord = '%q: Failed to apply record updates: %d rows updated.';
|
||||
SErrFailedToUpdateRecord = 'Failed to apply record updates: %d rows updated.';
|
||||
SErrRefreshNotSingleton = 'Refresh SQL resulted in multiple records: %d.';
|
||||
SErrRefreshEmptyResult = 'Refresh SQL resulted in empty result set.';
|
||||
SErrNoKeyFieldForRefreshClause = 'No key field found to construct refresh SQL WHERE clause';
|
||||
|
||||
Implementation
|
||||
|
||||
|
@ -22,29 +22,49 @@ begin
|
||||
until notRepeatEscaped;
|
||||
end;
|
||||
|
||||
{ TParamsEnumerator }
|
||||
|
||||
function TParamsEnumerator.GetCurrent: TParam;
|
||||
begin
|
||||
Result := FParams[FPosition];
|
||||
end;
|
||||
|
||||
constructor TParamsEnumerator.Create(AParams: TParams);
|
||||
begin
|
||||
inherited Create;
|
||||
FParams := AParams;
|
||||
FPosition := -1;
|
||||
end;
|
||||
|
||||
function TParamsEnumerator.MoveNext: Boolean;
|
||||
begin
|
||||
inc(FPosition);
|
||||
Result := FPosition < FParams.Count;
|
||||
end;
|
||||
|
||||
{ TParams }
|
||||
|
||||
function TParams.GetItem(Index: Integer): TParam;
|
||||
Function TParams.GetItem(Index: Integer): TParam;
|
||||
begin
|
||||
Result:=(Inherited GetItem(Index)) as TParam;
|
||||
end;
|
||||
|
||||
function TParams.GetParamValue(const ParamName: string): Variant;
|
||||
Function TParams.GetParamValue(const ParamName: string): Variant;
|
||||
begin
|
||||
Result:=ParamByName(ParamName).Value;
|
||||
end;
|
||||
|
||||
procedure TParams.SetItem(Index: Integer; Value: TParam);
|
||||
Procedure TParams.SetItem(Index: Integer; Value: TParam);
|
||||
begin
|
||||
Inherited SetItem(Index,Value);
|
||||
end;
|
||||
|
||||
procedure TParams.SetParamValue(const ParamName: string; const Value: Variant);
|
||||
Procedure TParams.SetParamValue(const ParamName: string; const Value: Variant);
|
||||
begin
|
||||
ParamByName(ParamName).Value:=Value;
|
||||
end;
|
||||
|
||||
procedure TParams.AssignTo(Dest: TPersistent);
|
||||
Procedure TParams.AssignTo(Dest: TPersistent);
|
||||
begin
|
||||
if (Dest is TParams) then
|
||||
TParams(Dest).Assign(Self)
|
||||
@ -52,7 +72,7 @@ begin
|
||||
inherited AssignTo(Dest);
|
||||
end;
|
||||
|
||||
function TParams.GetDataSet: TDataSet;
|
||||
Function TParams.GetDataSet: TDataSet;
|
||||
begin
|
||||
If (FOwner is TDataset) Then
|
||||
Result:=TDataset(FOwner)
|
||||
@ -60,17 +80,17 @@ begin
|
||||
Result:=Nil;
|
||||
end;
|
||||
|
||||
function TParams.GetOwner: TPersistent;
|
||||
Function TParams.GetOwner: TPersistent;
|
||||
begin
|
||||
Result:=FOwner;
|
||||
end;
|
||||
|
||||
class function TParams.ParamClass: TParamClass;
|
||||
Class Function TParams.ParamClass: TParamClass;
|
||||
begin
|
||||
Result:=TParam;
|
||||
end;
|
||||
|
||||
constructor TParams.Create(AOwner: TPersistent; AItemClass: TCollectionItemClass
|
||||
Constructor TParams.Create(AOwner: TPersistent; AItemClass: TCollectionItemClass
|
||||
);
|
||||
begin
|
||||
Inherited Create(AItemClass);
|
||||
@ -78,22 +98,22 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
constructor TParams.Create(AOwner: TPersistent);
|
||||
Constructor TParams.Create(AOwner: TPersistent);
|
||||
begin
|
||||
Create(AOwner,ParamClass);
|
||||
end;
|
||||
|
||||
constructor TParams.Create;
|
||||
Constructor TParams.Create;
|
||||
begin
|
||||
Create(TPersistent(Nil));
|
||||
end;
|
||||
|
||||
procedure TParams.AddParam(Value: TParam);
|
||||
Procedure TParams.AddParam(Value: TParam);
|
||||
begin
|
||||
Value.Collection:=Self;
|
||||
end;
|
||||
|
||||
procedure TParams.AssignValues(Value: TParams);
|
||||
Procedure TParams.AssignValues(Value: TParams);
|
||||
|
||||
Var
|
||||
I : Integer;
|
||||
@ -109,7 +129,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TParams.CreateParam(FldType: TFieldType; const ParamName: string;
|
||||
Function TParams.CreateParam(FldType: TFieldType; const ParamName: string;
|
||||
ParamType: TParamType): TParam;
|
||||
|
||||
begin
|
||||
@ -119,7 +139,7 @@ begin
|
||||
Result.ParamType:=ParamType;
|
||||
end;
|
||||
|
||||
function TParams.FindParam(const Value: string): TParam;
|
||||
Function TParams.FindParam(const Value: string): TParam;
|
||||
|
||||
Var
|
||||
I : Integer;
|
||||
@ -134,7 +154,7 @@ begin
|
||||
Dec(i);
|
||||
end;
|
||||
|
||||
procedure TParams.GetParamList(List: TList; const ParamNames: string);
|
||||
Procedure TParams.GetParamList(List: TList; const ParamNames: string);
|
||||
|
||||
Var
|
||||
P: TParam;
|
||||
@ -152,7 +172,7 @@ begin
|
||||
until StrPos > Length(ParamNames);
|
||||
end;
|
||||
|
||||
function TParams.IsEqual(Value: TParams): Boolean;
|
||||
Function TParams.IsEqual(Value: TParams): Boolean;
|
||||
|
||||
Var
|
||||
I : Integer;
|
||||
@ -167,14 +187,19 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TParams.ParamByName(const Value: string): TParam;
|
||||
Function TParams.GetEnumerator: TParamsEnumerator;
|
||||
begin
|
||||
Result:=TParamsEnumerator.Create(Self);
|
||||
end;
|
||||
|
||||
Function TParams.ParamByName(const Value: string): TParam;
|
||||
begin
|
||||
Result:=FindParam(Value);
|
||||
If (Result=Nil) then
|
||||
DatabaseErrorFmt(SParameterNotFound,[Value],Dataset);
|
||||
end;
|
||||
|
||||
function TParams.ParseSQL(SQL: String; DoCreate: Boolean): String;
|
||||
Function TParams.ParseSQL(SQL: String; DoCreate: Boolean): String;
|
||||
|
||||
var pb : TParamBinding;
|
||||
rs : string;
|
||||
@ -183,7 +208,7 @@ begin
|
||||
Result := ParseSQL(SQL,DoCreate,True,True,psInterbase, pb, rs);
|
||||
end;
|
||||
|
||||
function TParams.ParseSQL(SQL: String; DoCreate, EscapeSlash,
|
||||
Function TParams.ParseSQL(SQL: String; DoCreate, EscapeSlash,
|
||||
EscapeRepeat: Boolean; ParameterStyle: TParamStyle): String;
|
||||
|
||||
var pb : TParamBinding;
|
||||
@ -193,7 +218,7 @@ begin
|
||||
Result := ParseSQL(SQL,DoCreate,EscapeSlash,EscapeRepeat,ParameterStyle,pb, rs);
|
||||
end;
|
||||
|
||||
function TParams.ParseSQL(SQL: String; DoCreate, EscapeSlash,
|
||||
Function TParams.ParseSQL(SQL: String; DoCreate, EscapeSlash,
|
||||
EscapeRepeat: Boolean; ParameterStyle: TParamStyle; out
|
||||
ParamBinding: TParambinding): String;
|
||||
|
||||
@ -246,7 +271,7 @@ begin
|
||||
end; {case}
|
||||
end;
|
||||
|
||||
function TParams.ParseSQL(SQL: String; DoCreate, EscapeSlash,
|
||||
Function TParams.ParseSQL(SQL: String; DoCreate, EscapeSlash,
|
||||
EscapeRepeat: Boolean; ParameterStyle: TParamStyle; out
|
||||
ParamBinding: TParambinding; out ReplaceString: string): String;
|
||||
|
||||
@ -435,7 +460,7 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TParams.RemoveParam(Value: TParam);
|
||||
Procedure TParams.RemoveParam(Value: TParam);
|
||||
begin
|
||||
Value.Collection:=Nil;
|
||||
end;
|
||||
@ -1123,7 +1148,7 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TParams.CopyParamValuesFromDataset(ADataset: TDataset;
|
||||
Procedure TParams.CopyParamValuesFromDataset(ADataset: TDataset;
|
||||
CopyBound: Boolean);
|
||||
|
||||
Var
|
||||
|
@ -99,6 +99,7 @@ Type
|
||||
procedure ExecuteDirectMySQL(const query : string);
|
||||
function EscapeString(const Str : string) : string;
|
||||
protected
|
||||
function GetLastInsertIDForField(Query : TCustomSQLQuery; AField : TField): Boolean; override;
|
||||
function StrToStatementType(s : string) : TStatementType; override;
|
||||
Procedure ConnectToServer; virtual;
|
||||
Procedure SelectDatabase; virtual;
|
||||
@ -329,7 +330,7 @@ begin
|
||||
Result := mysql_stat(FMYSQL);
|
||||
end;
|
||||
|
||||
function TConnectionName.GetInsertID: Int64;
|
||||
Function TConnectionName.GetInsertID: int64;
|
||||
begin
|
||||
CheckConnected;
|
||||
Result:=mysql_insert_id(GetHandle);
|
||||
@ -404,14 +405,14 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TConnectionName.ConnectToServer;
|
||||
Procedure TConnectionName.ConnectToServer;
|
||||
begin
|
||||
ConnectMySQL(FMySQL);
|
||||
FServerInfo := strpas(mysql_get_server_info(FMYSQL));
|
||||
FHostInfo := strpas(mysql_get_host_info(FMYSQL));
|
||||
end;
|
||||
|
||||
procedure TConnectionName.SelectDatabase;
|
||||
Procedure TConnectionName.SelectDatabase;
|
||||
begin
|
||||
if mysql_select_db(FMySQL,pchar(DatabaseName))<>0 then
|
||||
MySQLError(FMySQL,SErrDatabaseSelectFailed,Self);
|
||||
@ -463,6 +464,14 @@ begin
|
||||
SetLength(result,Len);
|
||||
end;
|
||||
|
||||
function TConnectionName.GetLastInsertIDForField(Query: TCustomSQLQuery;
|
||||
AField: TField): Boolean;
|
||||
begin
|
||||
Result:=inherited GetLastInsertIDForField(Query, AField);
|
||||
if Result then
|
||||
AField.AsLargeInt:=GetInsertID;
|
||||
end;
|
||||
|
||||
procedure TConnectionName.DoInternalConnect;
|
||||
var
|
||||
FullVersion: string;
|
||||
@ -493,7 +502,7 @@ begin
|
||||
Result:=FMySQL;
|
||||
end;
|
||||
|
||||
function TConnectionName.AllocateCursorHandle: TSQLCursor;
|
||||
Function TConnectionName.AllocateCursorHandle: TSQLCursor;
|
||||
begin
|
||||
{$IFDEF mysql56}
|
||||
Result:=TMySQL56Cursor.Create;
|
||||
@ -524,7 +533,7 @@ begin
|
||||
FreeAndNil(cursor);
|
||||
end;
|
||||
|
||||
function TConnectionName.AllocateTransactionHandle: TSQLHandle;
|
||||
Function TConnectionName.AllocateTransactionHandle: TSQLHandle;
|
||||
begin
|
||||
// Result:=TTransactionName.Create;
|
||||
Result := nil;
|
||||
@ -1122,7 +1131,7 @@ constructor TConnectionName.Create(AOwner: TComponent);
|
||||
const SingleBackQoutes: TQuoteChars = ('`','`');
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FConnOptions := FConnOptions + [sqEscapeRepeat, sqEscapeSlash, sqImplicitTransaction];
|
||||
FConnOptions := FConnOptions + [sqEscapeRepeat, sqEscapeSlash, sqImplicitTransaction, sqLastInsertID];
|
||||
FieldNameQuoteChars:=SingleBackQoutes;
|
||||
FMySQL := Nil;
|
||||
end;
|
||||
|
@ -138,7 +138,7 @@ type
|
||||
|
||||
{ TSQLConnection }
|
||||
|
||||
TConnOption = (sqSupportParams, sqSupportEmptyDatabaseName, sqEscapeSlash, sqEscapeRepeat, sqImplicitTransaction);
|
||||
TConnOption = (sqSupportParams, sqSupportEmptyDatabaseName, sqEscapeSlash, sqEscapeRepeat, sqImplicitTransaction, sqLastInsertID);
|
||||
TConnOptions= set of TConnOption;
|
||||
|
||||
TSQLConnectionOption = (scoExplicitConnect, scoApplyUpdatesChecksRowsAffected);
|
||||
@ -172,12 +172,14 @@ type
|
||||
// One day, this may be factored out to a TSQLResolver class.
|
||||
// The following allow construction of update queries. They can be adapted as needed by descendents to fit the DB engine.
|
||||
procedure AddFieldToUpdateWherePart(var sql_where: string; UpdateMode : TUpdateMode; F: TField); virtual;
|
||||
function ConstructRefreshSQL(Query: TCustomSQLQuery; UpdateKind : TUpdateKind): string; virtual;
|
||||
function ConstructDeleteSQL(Query: TCustomSQLQuery): string; virtual;
|
||||
function ConstructInsertSQL(Query: TCustomSQLQuery): string; virtual;
|
||||
function ConstructUpdateSQL(Query: TCustomSQLQuery): string; virtual;
|
||||
function InitialiseUpdateStatement(Query: TCustomSQLQuery; var qry: TCustomSQLStatement): TCustomSQLStatement;
|
||||
procedure ApplyFieldUpdate(C : TSQLCursor; P: TSQLDBParam; F: TField; UseOldValue: Boolean); virtual;
|
||||
// This is the call that updates a record, it used to be in TSQLQuery.
|
||||
function GetLastInsertIDForField(Query : TCustomSQLQuery; AField : TField): Boolean; virtual;
|
||||
procedure ApplyRecUpdate(Query : TCustomSQLQuery; UpdateKind : TUpdateKind); virtual;
|
||||
procedure GetDBInfo(const ASchemaType : TSchemaType; const ASchemaObjectName, AReturnField : string; AList: TStrings);
|
||||
procedure SetTransaction(Value : TSQLTransaction); virtual;
|
||||
@ -382,6 +384,7 @@ type
|
||||
FUpdateable : boolean;
|
||||
FTableName : string;
|
||||
FStatement : TCustomSQLStatement;
|
||||
FRefreshSQL,
|
||||
FUpdateSQL,
|
||||
FInsertSQL,
|
||||
FDeleteSQL : TStringList;
|
||||
@ -412,8 +415,10 @@ type
|
||||
function GetSQLConnection: TSQLConnection;
|
||||
function GetSQLTransaction: TSQLTransaction;
|
||||
function GetStatementType : TStatementType;
|
||||
Function NeedLastinsertID: TField;
|
||||
procedure SetOptions(AValue: TSQLQueryOptions);
|
||||
procedure SetParamCheck(AValue: Boolean);
|
||||
procedure SetRefreshSQL(AValue: TStringlist);
|
||||
procedure SetSQLConnection(AValue: TSQLConnection);
|
||||
procedure SetSQLTransaction(AValue: TSQLTransaction);
|
||||
procedure SetUpdateSQL(const AValue: TStringlist);
|
||||
@ -429,6 +434,9 @@ type
|
||||
procedure ApplyFilter;
|
||||
Function AddFilter(SQLstr : string) : string;
|
||||
protected
|
||||
Function UpdateLastInsertIDField(F: TField): Boolean; virtual;
|
||||
Function NeedRefreshRecord (UpdateKind: TUpdateKind): Boolean; virtual;
|
||||
Function RefreshRecord (UpdateKind: TUpdateKind) : Boolean; virtual;
|
||||
procedure SetPacketRecords(aValue : integer); override;
|
||||
Function Cursor : TSQLCursor;
|
||||
Function LogEvent(EventType : TDBEventType) : Boolean;
|
||||
@ -512,6 +520,7 @@ type
|
||||
property UpdateSQL : TStringlist read FUpdateSQL write SetUpdateSQL;
|
||||
property InsertSQL : TStringlist read FInsertSQL write SetInsertSQL;
|
||||
property DeleteSQL : TStringlist read FDeleteSQL write SetDeleteSQL;
|
||||
property RefreshSQL : TStringlist read FRefreshSQL write SetRefreshSQL;
|
||||
Property Options : TSQLQueryOptions Read FOptions Write SetOptions;
|
||||
property Params : TParams read GetParams Write SetParams;
|
||||
Property ParamCheck : Boolean Read GetParamCheck Write SetParamCheck default true;
|
||||
@ -568,6 +577,7 @@ type
|
||||
property SQL;
|
||||
property UpdateSQL;
|
||||
property InsertSQL;
|
||||
property RefreshSQL;
|
||||
property DeleteSQL;
|
||||
property IndexDefs;
|
||||
Property Options;
|
||||
@ -716,6 +726,10 @@ implementation
|
||||
|
||||
uses dbconst, strutils;
|
||||
|
||||
Const
|
||||
// Flags to check which fields must be refreshed. Index is false for update, true for insert
|
||||
RefreshFlags : Array [Boolean] of TProviderFlag = (pfRefreshOnUpdate,pfRefreshOnUpdate);
|
||||
|
||||
|
||||
function TimeIntervalToString(Time: TDateTime): string;
|
||||
var
|
||||
@ -1563,6 +1577,45 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSQLConnection.ConstructRefreshSQL(Query: TCustomSQLQuery;
|
||||
UpdateKind: TUpdateKind): string;
|
||||
|
||||
Var
|
||||
F : TField;
|
||||
PF : TProviderFlag;
|
||||
Where : String;
|
||||
|
||||
begin
|
||||
Where:='';
|
||||
Result:=Query.RefreshSQL.Text;
|
||||
if (Result='') then
|
||||
begin
|
||||
PF:=RefreshFlags[UpdateKind=ukInsert];
|
||||
For F in Query.Fields do
|
||||
begin
|
||||
if PF in F.ProviderFlags then
|
||||
begin
|
||||
if (Result<>'') then
|
||||
Result:=Result+', ';
|
||||
if (F.Origin<>'') and (F.Origin<>F.FieldName) then
|
||||
Result:=Result+F.Origin+' as '+F.FieldName
|
||||
else
|
||||
Result:=Result+FieldNameQuoteChars[0]+F.FieldName+FieldNameQuoteChars[1]
|
||||
end;
|
||||
if pfInkey in F.ProviderFlags then
|
||||
begin
|
||||
if (Where<>'') then
|
||||
Where:=Where+' AND ';
|
||||
Where:=Where+'('+FieldNameQuoteChars[0]+F.FieldName+FieldNameQuoteChars[0]+' = :'+F.FieldName+')';
|
||||
end;
|
||||
end;
|
||||
if (Where='') then
|
||||
DatabaseError(SErrNoKeyFieldForRefreshClause,Query);
|
||||
Result:='SELECT '+Result+' FROM '+Query.FTableName+' WHERE '+Where;
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
function TSQLConnection.ConstructUpdateSQL(Query: TCustomSQLQuery): string;
|
||||
|
||||
var x : integer;
|
||||
@ -1639,6 +1692,11 @@ begin
|
||||
P.FFieldDef:=F.FieldDef;
|
||||
end;
|
||||
|
||||
function TSQLConnection.GetLastInsertIDForField(Query: TCustomSQLQuery; AField: TField): Boolean;
|
||||
begin
|
||||
Result:=sqLastInsertID in ConnOptions;
|
||||
end;
|
||||
|
||||
procedure TSQLConnection.ApplyRecUpdate(Query: TCustomSQLQuery; UpdateKind: TUpdateKind);
|
||||
|
||||
var
|
||||
@ -2033,6 +2091,8 @@ begin
|
||||
FInsertSQL.OnChange := @OnChangeModifySQL;
|
||||
FDeleteSQL := TStringList.Create;
|
||||
FDeleteSQL.OnChange := @OnChangeModifySQL;
|
||||
FRefreshSQL := TStringList.Create;
|
||||
FRefreshSQL.OnChange := @OnChangeModifySQL;
|
||||
|
||||
FServerIndexDefs := TServerIndexDefs.Create(Self);
|
||||
|
||||
@ -2141,6 +2201,77 @@ begin
|
||||
Result := SQLstr;
|
||||
end;
|
||||
|
||||
Function TCustomSQLQuery.NeedRefreshRecord(UpdateKind: TUpdateKind): Boolean;
|
||||
|
||||
|
||||
Var
|
||||
F : TProviderFlag;
|
||||
I : Integer;
|
||||
begin
|
||||
Result:=(FRefreshSQL.Count<>0);
|
||||
if Not Result then
|
||||
begin
|
||||
F:=RefreshFlags[UpdateKind=ukInsert];
|
||||
I:=0;
|
||||
While (Not Result) and (I<Fields.Count) do
|
||||
begin
|
||||
Result:=F in Fields[i].ProviderFlags;
|
||||
Inc(I);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
Function TCustomSQLQuery.RefreshRecord(UpdateKind: TUpdateKind) : Boolean;
|
||||
|
||||
Var
|
||||
Q : TCustomSQLQuery;
|
||||
P : TParam;
|
||||
F,FD : TField;
|
||||
N : String;
|
||||
S : TDatasetState;
|
||||
|
||||
begin
|
||||
Result:=False;
|
||||
Q:=TCustomSQLQuery.Create(Nil);
|
||||
try
|
||||
Q.Database:=Self.Database;
|
||||
Q.Transaction:=Self.Transaction;
|
||||
Q.SQL.Text:=SQLConnection.ConstructRefreshSQL(Self,UpdateKind);
|
||||
For P in Q.Params do
|
||||
begin
|
||||
N:=P.Name;
|
||||
If CompareText(Copy(N,1,4),'OLD_')=0 then
|
||||
system.Delete(N,1,4);
|
||||
F:=Fields.FindField(N);
|
||||
if Assigned(F) then
|
||||
P.AssignField(F);
|
||||
end;
|
||||
Q.Open;
|
||||
try
|
||||
if (Q.EOF and Q.BOF) then
|
||||
DatabaseError(SErrRefreshEmptyResult,Self)
|
||||
else
|
||||
begin
|
||||
if Q.RecordCount<>1 then
|
||||
DatabaseErrorFmt(SErrRefreshNotSingleton,[Q.RecordCount],Self);
|
||||
For F in Q.Fields do
|
||||
begin
|
||||
FD:=Fields.FindField(F.FieldName);
|
||||
if Assigned(FD) then
|
||||
begin
|
||||
FD.Assign(F);
|
||||
Result:=True; // We could check if the new value differs from the old, but we won't.
|
||||
end;
|
||||
end;
|
||||
end
|
||||
finally
|
||||
Q.Close;
|
||||
end;
|
||||
finally
|
||||
Q.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSQLQuery.ApplyFilter;
|
||||
|
||||
begin
|
||||
@ -2479,12 +2610,63 @@ begin
|
||||
SQLConnection.UpdateIndexDefs(ServerIndexDefs,FTableName);
|
||||
end;
|
||||
|
||||
Function TCustomSQLQuery.NeedLastinsertID : TField;
|
||||
|
||||
Var
|
||||
I : Integer;
|
||||
|
||||
begin
|
||||
Result:=Nil;
|
||||
if sqLastInsertID in SQLConnection.ConnOptions then
|
||||
begin
|
||||
I:=0;
|
||||
While (Result=Nil) and (I<Fields.Count) do
|
||||
begin
|
||||
Result:=Fields[i];
|
||||
if Result.DataType<>ftAutoInc then
|
||||
Result:=Nil;
|
||||
Inc(I);
|
||||
end;
|
||||
end
|
||||
end;
|
||||
|
||||
Function TCustomSQLQuery.UpdateLastInsertIDField(F : TField) : Boolean;
|
||||
|
||||
begin
|
||||
Result:=SQLConnection.GetLastInsertIDForField(Self,F);
|
||||
end;
|
||||
|
||||
procedure TCustomSQLQuery.ApplyRecUpdate(UpdateKind: TUpdateKind);
|
||||
|
||||
Var
|
||||
DoRefresh,RecordRefreshed : Boolean;
|
||||
LastIDField : TField;
|
||||
S : TDatasetState;
|
||||
|
||||
begin
|
||||
// Moved to connection: the SQLConnection always has more information about types etc.
|
||||
// than SQLQuery itself.
|
||||
SQLConnection.ApplyRecupdate(Self,UpdateKind);
|
||||
if (UpdateKind=ukInsert) then
|
||||
LastIDField:=NeedLastInsertID;
|
||||
DoRefresh:=(UpdateKind in [ukModify,ukInsert]) and NeedRefreshRecord(UpdateKind);
|
||||
if ((LastIDField<>Nil) or DoRefresh) then
|
||||
begin
|
||||
S:=State;
|
||||
try
|
||||
RecordRefreshed:=False;
|
||||
SetState(dsNewValue);
|
||||
if LastIDField<>Nil then
|
||||
RecordRefreshed:=UpdateLastInsertIDField(LastIDField);
|
||||
if DoRefresh then
|
||||
RecordRefreshed:=RefreshRecord(UpdateKind) or RecordRefreshed;
|
||||
finally
|
||||
SetState(S);
|
||||
end;
|
||||
if RecordRefreshed then
|
||||
// Active buffer is updated, move to record.
|
||||
ActiveBufferToRecord;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
@ -2570,6 +2752,12 @@ begin
|
||||
PacketRecords:=-1;
|
||||
end;
|
||||
|
||||
procedure TCustomSQLQuery.SetRefreshSQL(AValue: TStringlist);
|
||||
begin
|
||||
if FRefreshSQL=AValue then Exit;
|
||||
FRefreshSQL.Assign(AValue);
|
||||
end;
|
||||
|
||||
procedure TCustomSQLQuery.SetSQLConnection(AValue: TSQLConnection);
|
||||
begin
|
||||
Database:=AValue;
|
||||
|
@ -55,6 +55,7 @@ type
|
||||
foptions: TSQLiteOptions;
|
||||
procedure setoptions(const avalue: tsqliteoptions);
|
||||
protected
|
||||
function GetLastInsertIDForField(Query : TCustomSQLQuery; AField : TField): Boolean; override;
|
||||
function stringsquery(const asql: string): TArrayStringArray;
|
||||
procedure checkerror(const aerror: integer);
|
||||
|
||||
@ -334,12 +335,12 @@ begin
|
||||
ABlobBuf^.BlobBuffer^.Size := int1;
|
||||
end;
|
||||
|
||||
function TSQLite3Connection.AllocateTransactionHandle: TSQLHandle;
|
||||
Function TSQLite3Connection.AllocateTransactionHandle: TSQLHandle;
|
||||
begin
|
||||
result:= tsqlhandle.create;
|
||||
end;
|
||||
|
||||
function TSQLite3Connection.AllocateCursorHandle: TSQLCursor;
|
||||
Function TSQLite3Connection.AllocateCursorHandle: TSQLCursor;
|
||||
|
||||
Var
|
||||
Res : TSQLite3Cursor;
|
||||
@ -350,7 +351,7 @@ begin
|
||||
Result:=Res;
|
||||
end;
|
||||
|
||||
procedure TSQLite3Connection.DeAllocateCursorHandle(var cursor: TSQLCursor);
|
||||
Procedure TSQLite3Connection.DeAllocateCursorHandle(var cursor: TSQLCursor);
|
||||
begin
|
||||
freeandnil(cursor);
|
||||
end;
|
||||
@ -499,7 +500,8 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSQLite3Connection.Execute(cursor: TSQLCursor; atransaction: tsqltransaction; AParams: TParams);
|
||||
procedure TSQLite3Connection.Execute(cursor: TSQLCursor;
|
||||
atransaction: tSQLtransaction; AParams: TParams);
|
||||
var
|
||||
SC : TSQLite3Cursor;
|
||||
|
||||
@ -886,7 +888,7 @@ end;
|
||||
constructor TSQLite3Connection.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FConnOptions := FConnOptions + [sqEscapeRepeat] + [sqEscapeSlash];
|
||||
FConnOptions := FConnOptions + [sqEscapeRepeat,sqEscapeSlash,sqLastInsertID];
|
||||
FieldNameQuoteChars:=DoubleQuotes;
|
||||
end;
|
||||
|
||||
@ -948,7 +950,7 @@ begin
|
||||
IXFields.Free;
|
||||
end;
|
||||
|
||||
function TSQLite3Connection.getinsertid: int64;
|
||||
function TSQLite3Connection.GetInsertID: int64;
|
||||
begin
|
||||
result:= sqlite3_last_insert_rowid(fhandle);
|
||||
end;
|
||||
@ -1002,7 +1004,7 @@ begin
|
||||
CheckError(sqlite3_create_collation(fhandle, PChar(CollationName), eTextRep, Arg, Compare));
|
||||
end;
|
||||
|
||||
procedure TSQLite3Connection.LoadExtension(LibraryFile: String);
|
||||
procedure TSQLite3Connection.LoadExtension(LibraryFile: string);
|
||||
var
|
||||
LoadResult: integer;
|
||||
begin
|
||||
@ -1036,6 +1038,14 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSQLite3Connection.GetLastInsertIDForField(Query: TCustomSQLQuery;
|
||||
AField: TField): Boolean;
|
||||
begin
|
||||
Result:=inherited GetLastInsertIDForField(Query, AField);
|
||||
if Result then
|
||||
AField.AsLargeInt:=GetInsertID;
|
||||
end;
|
||||
|
||||
{ TSQLite3ConnectionDef }
|
||||
|
||||
class function TSQLite3ConnectionDef.TypeName: string;
|
||||
|
@ -47,6 +47,13 @@ type
|
||||
Procedure TestAutoApplyUpdatesDelete;
|
||||
Procedure TestCheckRowsAffected;
|
||||
Procedure TestAutoCommit;
|
||||
Procedure TestRefreshSQL;
|
||||
Procedure TestGeneratedRefreshSQL;
|
||||
Procedure TestGeneratedRefreshSQL1Field;
|
||||
Procedure TestGeneratedRefreshSQLNoKey;
|
||||
Procedure TestRefreshSQLMultipleRecords;
|
||||
Procedure TestRefreshSQLNoRecords;
|
||||
Procedure TestFetchAutoInc;
|
||||
end;
|
||||
|
||||
{ TTestTSQLConnection }
|
||||
@ -236,7 +243,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.SetQueryOptions;
|
||||
Procedure TTestTSQLQuery.SetQueryOPtions;
|
||||
|
||||
begin
|
||||
FMyQ.Options:=[sqoKeepOpenOnCommit];
|
||||
@ -400,6 +407,202 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.TestRefreshSQL;
|
||||
var
|
||||
Q: TSQLQuery;
|
||||
T : TSQLTransaction;
|
||||
I, J : Integer;
|
||||
begin
|
||||
with SQLDBConnector do
|
||||
begin
|
||||
TryDropIfExist('testdefval');
|
||||
ExecuteDirect('create table testdefval (id integer not null, a varchar(10) default ''abcde'', constraint pk_testdefval primary key(id))');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
end;
|
||||
Q:=SQLDBConnector.Query;
|
||||
Q.SQL.Text:='select * from testdefval';
|
||||
Q.InsertSQL.Text:='insert into testdefval (id) values (:id)';
|
||||
Q.RefreshSQL.Text:='SELECT a FROM testdefval WHERE (id=:id)';
|
||||
Q.Open;
|
||||
Q.Insert;
|
||||
Q.FieldByName('id').AsInteger:=1;
|
||||
Q.Post;
|
||||
AssertTrue('field value has not been fetched after post',Q.FieldByName('a').IsNull);
|
||||
Q.ApplyUpdates(0);
|
||||
AssertEquals('Still on correc field',1,Q.FieldByName('id').AsInteger);
|
||||
AssertEquals('field value has been fetched from the database ','abcde',Q.FieldByName('a').AsString);
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.TestGeneratedRefreshSQL;
|
||||
|
||||
var
|
||||
Q: TSQLQuery;
|
||||
T : TSQLTransaction;
|
||||
I, J : Integer;
|
||||
|
||||
begin
|
||||
with SQLDBConnector do
|
||||
begin
|
||||
TryDropIfExist('testdefval');
|
||||
ExecuteDirect('create table testdefval (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint pk_testdefval primary key(id))');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
end;
|
||||
Q:=SQLDBConnector.Query;
|
||||
Q.SQL.Text:='select * from testdefval';
|
||||
Q.InsertSQL.Text:='insert into testdefval (id) values (:id)';
|
||||
Q.Open;
|
||||
With Q.FieldByName('id') do
|
||||
ProviderFlags:=ProviderFlags+[pfInKey];
|
||||
With Q.FieldByName('a') do
|
||||
ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
|
||||
With Q.FieldByName('b') do
|
||||
ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
|
||||
Q.Insert;
|
||||
Q.FieldByName('id').AsInteger:=1;
|
||||
Q.Post;
|
||||
AssertTrue('field value has not been fetched after post',Q.FieldByName('a').IsNull);
|
||||
Q.ApplyUpdates(0);
|
||||
AssertEquals('Still on correc field',1,Q.FieldByName('id').AsInteger);
|
||||
AssertEquals('field value has been fetched from the database ','abcde',Q.FieldByName('a').AsString);
|
||||
AssertEquals('field value has been fetched from the database ','fgh',Q.FieldByName('b').AsString);
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.TestGeneratedRefreshSQL1Field;
|
||||
var
|
||||
Q: TSQLQuery;
|
||||
T : TSQLTransaction;
|
||||
I, J : Integer;
|
||||
|
||||
begin
|
||||
with SQLDBConnector do
|
||||
begin
|
||||
TryDropIfExist('testdefval');
|
||||
ExecuteDirect('create table testdefval (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint pk_testdefval primary key(id))');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
end;
|
||||
Q:=SQLDBConnector.Query;
|
||||
Q.SQL.Text:='select * from testdefval';
|
||||
Q.InsertSQL.Text:='insert into testdefval (id) values (:id)';
|
||||
Q.Open;
|
||||
With Q.FieldByName('id') do
|
||||
ProviderFlags:=ProviderFlags+[pfInKey];
|
||||
With Q.FieldByName('a') do
|
||||
ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
|
||||
Q.Insert;
|
||||
Q.FieldByName('id').AsInteger:=1;
|
||||
Q.Post;
|
||||
AssertTrue('field value has not been fetched after post',Q.FieldByName('a').IsNull);
|
||||
Q.ApplyUpdates(0);
|
||||
AssertEquals('Still on correc field',1,Q.FieldByName('id').AsInteger);
|
||||
AssertEquals('field value a has been fetched from the database ','abcde',Q.FieldByName('a').AsString);
|
||||
AssertEquals('field value b has NOT been fetched from the database ','',Q.FieldByName('b').AsString);
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.TestGeneratedRefreshSQLNoKey;
|
||||
begin
|
||||
with SQLDBConnector do
|
||||
begin
|
||||
TryDropIfExist('testdefval');
|
||||
ExecuteDirect('create table testdefval (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint pk_testdefval primary key(id))');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
end;
|
||||
FMyQ:=SQLDBConnector.Query;
|
||||
FMyQ.SQL.Text:='select * from testdefval';
|
||||
FMyQ.InsertSQL.Text:='insert into testdefval (id) values (:id)';
|
||||
FMyQ.Open;
|
||||
With FMyQ.FieldByName('id') do
|
||||
ProviderFlags:=ProviderFlags-[pfInKey];
|
||||
With FMyQ.FieldByName('a') do
|
||||
ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
|
||||
FMyQ.Insert;
|
||||
FMyQ.FieldByName('id').AsInteger:=1;
|
||||
FMyQ.Post;
|
||||
AssertException('Cannot refresh without primary key',EUpdateError,@DoApplyUpdates);
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.TestRefreshSQLMultipleRecords;
|
||||
|
||||
begin
|
||||
with SQLDBConnector do
|
||||
begin
|
||||
TryDropIfExist('testdefval');
|
||||
ExecuteDirect('create table testdefval (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint pk_testdefval primary key(id))');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
ExecuteDirect('insert into testdefval (id) values (123)');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
end;
|
||||
FMyQ:=SQLDBConnector.Query;
|
||||
FMyQ.SQL.Text:='select * from testdefval';
|
||||
FMyQ.InsertSQL.Text:='insert into testdefval (id) values (:id)';
|
||||
FMyQ.RefreshSQL.Text:='select * from testdefval';
|
||||
FMyQ.Open;
|
||||
With FMyQ.FieldByName('id') do
|
||||
ProviderFlags:=ProviderFlags+[pfInKey];
|
||||
With FMyQ.FieldByName('a') do
|
||||
ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
|
||||
FMyQ.Insert;
|
||||
FMyQ.FieldByName('id').AsInteger:=1;
|
||||
FMyQ.Post;
|
||||
AssertException('Multiple records returned by RefreshSQL gives an error',EUpdateError,@DoApplyUpdates);
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.TestRefreshSQLNoRecords;
|
||||
begin
|
||||
with SQLDBConnector do
|
||||
begin
|
||||
TryDropIfExist('testdefval');
|
||||
ExecuteDirect('create table testdefval (id integer not null, a varchar(10) default ''abcde'', b varchar(5) default ''fgh'', constraint pk_testdefval primary key(id))');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
ExecuteDirect('insert into testdefval (id) values (123)');
|
||||
if Transaction.Active then
|
||||
Transaction.Commit;
|
||||
end;
|
||||
FMyQ:=SQLDBConnector.Query;
|
||||
FMyQ.SQL.Text:='select * from testdefval';
|
||||
FMyQ.InsertSQL.Text:='insert into testdefval (id) values (:id)';
|
||||
FMyQ.RefreshSQL.Text:='select * from testdefval where 1=2';
|
||||
FMyQ.Open;
|
||||
With FMyQ.FieldByName('id') do
|
||||
ProviderFlags:=ProviderFlags+[pfInKey];
|
||||
With FMyQ.FieldByName('a') do
|
||||
ProviderFlags:=ProviderFlags+[pfRefreshOnInsert,pfRefreshOnUpdate];
|
||||
FMyQ.Insert;
|
||||
FMyQ.FieldByName('id').AsInteger:=1;
|
||||
FMyQ.Post;
|
||||
AssertException('Multiple records returned by RefreshSQL gives an error',EUpdateError,@DoApplyUpdates);
|
||||
end;
|
||||
|
||||
Procedure TTestTSQLQuery.TestFetchAutoInc;
|
||||
begin
|
||||
with SQLDBConnector do
|
||||
begin
|
||||
if not (sqLastInsertID in Connection.ConnOptions) then
|
||||
Ignore(STestNotApplicable);
|
||||
TryDropIfExist('testautoinc');
|
||||
// Syntax may vary. This works for MySQL.
|
||||
ExecuteDirect('create table testautoinc (id integer auto_increment, a varchar(5), constraint PK_AUTOINC primary key(id))');
|
||||
CommitDDL;
|
||||
end;
|
||||
FMyQ:=SQLDBConnector.Query;
|
||||
FMyQ.SQL.Text:='select * from testautoinc';
|
||||
FMyQ.Open;
|
||||
FMyQ.Insert;
|
||||
FMyQ.FieldByName('a').AsString:='b';
|
||||
FMyQ.Post;
|
||||
AssertTrue('ID field null after post',FMyQ.FieldByname('id').IsNull);
|
||||
FMyQ.ApplyUpdates(0);
|
||||
AssertTrue('ID field no longer null after applyupdates',Not FMyQ.FieldByname('id').IsNull);
|
||||
// Should be 1 after the table was created, but this is not guaranteed... So we just test positive values.
|
||||
AssertTrue('ID field has positive value',FMyQ.FieldByname('id').AsLargeInt>0);
|
||||
end;
|
||||
|
||||
|
||||
{ TTestTSQLConnection }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user