diff options
author | jonas <jonas@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2015-01-21 23:28:34 +0000 |
---|---|---|
committer | jonas <jonas@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2015-01-21 23:28:34 +0000 |
commit | 1903b037de2fb3e75826406b46f055acb70963fa (patch) | |
tree | 604cd8b790fe14e5fbe441d4cd647c80d2a36a9a /packages/fcl-db/src/sqldb/sqldb.pp | |
parent | ad1141d52f8353457053b925cd674fe1d5c4eafc (diff) | |
parent | 953d907e4d6c3a5c2f8aaee6e5e4f73c55ce5985 (diff) | |
download | fpc-blocks.tar.gz |
* synchronised with trunk till r29513blocks
git-svn-id: http://svn.freepascal.org/svn/fpc/branches/blocks@29516 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'packages/fcl-db/src/sqldb/sqldb.pp')
-rw-r--r-- | packages/fcl-db/src/sqldb/sqldb.pp | 577 |
1 files changed, 469 insertions, 108 deletions
diff --git a/packages/fcl-db/src/sqldb/sqldb.pp b/packages/fcl-db/src/sqldb/sqldb.pp index c964cc8427..0c39f9d5c0 100644 --- a/packages/fcl-db/src/sqldb/sqldb.pp +++ b/packages/fcl-db/src/sqldb/sqldb.pp @@ -24,9 +24,7 @@ uses SysUtils, Classes, DB, bufdataset, sqlscript; type TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages, stSchemata); - TConnOption = (sqSupportParams, sqSupportEmptyDatabaseName, sqEscapeSlash, sqEscapeRepeat); - TConnOptions= set of TConnOption; - TConnInfoType=(citAll=-1, citServerType, citServerVersion, citServerVersionString, citClientName, citClientVersion); + TStatementType = (stUnknown, stSelect, stInsert, stUpdate, stDelete, stDDL, stGetSegment, stPutSegment, stExecProcedure, stStartTrans, stCommit, stRollback, stSelectForUpd); @@ -43,7 +41,6 @@ type end; -type TSQLConnection = class; TSQLTransaction = class; TCustomSQLQuery = class; @@ -141,9 +138,18 @@ type { TSQLConnection } + TConnOption = (sqSupportParams, sqSupportEmptyDatabaseName, sqEscapeSlash, sqEscapeRepeat, sqImplicitTransaction, sqLastInsertID); + TConnOptions= set of TConnOption; + + TSQLConnectionOption = (scoExplicitConnect, scoApplyUpdatesChecksRowsAffected); + TSQLConnectionOptions = Set of TSQLConnectionOption; + + TConnInfoType=(citAll=-1, citServerType, citServerVersion, citServerVersionString, citClientName, citClientVersion); + TSQLConnection = class (TDatabase) private FFieldNameQuoteChars : TQuoteChars; + FOptions : TSQLConnectionOptions; FPassword : string; FTransaction : TSQLTransaction; FUserName : string; @@ -153,24 +159,28 @@ type FStatements : TFPList; FLogEvents: TDBEventTypes; FOnLog: TDBLogNotifyEvent; + FInternalTransaction : TSQLTransaction; function GetPort: cardinal; + procedure SetOptions(AValue: TSQLConnectionOptions); procedure SetPort(const AValue: cardinal); protected FConnOptions : TConnOptions; FSQLFormatSettings : TFormatSettings; + // Updating of DB records is moved out of TSQLQuery. // It is done here, so descendents can override it and implement DB-specific. // 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 ConstructDeleteSQL(Query: TCustomSQLQuery): string; virtual; function ConstructInsertSQL(Query: TCustomSQLQuery): string; virtual; function ConstructUpdateSQL(Query: TCustomSQLQuery): string; virtual; + function ConstructDeleteSQL(Query: TCustomSQLQuery): string; virtual; + function ConstructRefreshSQL(Query: TCustomSQLQuery; UpdateKind : TUpdateKind): 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. procedure ApplyRecUpdate(Query : TCustomSQLQuery; UpdateKind : TUpdateKind); virtual; - // + function RefreshLastInsertID(Query : TCustomSQLQuery; Field : TField): Boolean; virtual; procedure GetDBInfo(const ASchemaType : TSchemaType; const ASchemaObjectName, AReturnField : string; AList: TStrings); procedure SetTransaction(Value : TSQLTransaction); virtual; procedure DoInternalConnect; override; @@ -182,7 +192,6 @@ type Procedure Log(EventType : TDBEventType; Const Msg : String); virtual; Procedure RegisterStatement(S : TCustomSQLStatement); Procedure UnRegisterStatement(S : TCustomSQLStatement); - Function AllocateCursorHandle : TSQLCursor; virtual; abstract; Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); virtual; abstract; function StrToStatementType(s : string) : TStatementType; virtual; @@ -200,6 +209,7 @@ type function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract; function Commit(trans : TSQLHandle) : boolean; virtual; abstract; function RollBack(trans : TSQLHandle) : boolean; virtual; abstract; + function StartImplicitTransaction(trans : TSQLHandle; aParams : string) : boolean; virtual; function StartdbTransaction(trans : TSQLHandle; aParams : string) : boolean; virtual; abstract; procedure CommitRetaining(trans : TSQLHandle); virtual; abstract; procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract; @@ -207,6 +217,8 @@ type procedure UpdateIndexDefs(IndexDefs : TIndexDefs; TableName : string); virtual; function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual; + Procedure MaybeConnect; + Property Statements : TFPList Read FStatements; property Port: cardinal read GetPort write SetPort; public @@ -235,8 +247,9 @@ type property HostName : string Read FHostName Write FHostName; Property OnLog : TDBLogNotifyEvent Read FOnLog Write FOnLog; Property LogEvents : TDBEventTypes Read FLogEvents Write FLogEvents Default LogAllEvents; - property Connected; + Property Options : TSQLConnectionOptions Read FOptions Write SetOptions; Property Role : String read FRole write FRole; + property Connected; property DatabaseName; property KeepConnection; property LoginPrompt; @@ -249,15 +262,22 @@ type TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback, caRollbackRetaining); + TSQLTransactionOption = (stoUseImplicit, stoExplicitStart); + TSQLTransactionOptions = Set of TSQLTransactionOption; + TSQLTransaction = class (TDBTransaction) private + FOptions : TSQLTransactionOptions; FTrans : TSQLHandle; FAction : TCommitRollbackAction; FParams : TStringList; function GetSQLConnection: TSQLConnection; + procedure SetOptions(AValue: TSQLTransactionOptions); procedure SetParams(const AValue: TStringList); procedure SetSQLConnection(AValue: TSQLConnection); protected + Procedure MaybeStartTransaction; + Function AllowClose(DS: TDBDataset): Boolean; override; function GetHandle : Pointer; virtual; Procedure SetDatabase (Value : TDatabase); override; Function LogEvent(EventType : TDBEventType) : Boolean; @@ -277,8 +297,11 @@ type property Action : TCommitRollbackAction read FAction write FAction Default caRollBack; property Database; property Params : TStringList read FParams write SetParams; + Property Options : TSQLTransactionOptions Read FOptions Write SetOptions; end; + + { TCustomSQLStatement } TCustomSQLStatement = Class(TComponent) @@ -293,6 +316,7 @@ type FTransaction: TSQLTransaction; FParseSQL: Boolean; FDataLink : TDataLink; + FRowsAffected : TRowsCount; procedure SetDatabase(AValue: TSQLConnection); procedure SetParams(AValue: TParams); procedure SetSQL(AValue: TStrings); @@ -350,15 +374,20 @@ type { TCustomSQLQuery } + TSQLQueryOption = (sqoKeepOpenOnCommit, sqoAutoApplyUpdates, sqoAutoCommit); + TSQLQueryOptions = Set of TSQLQueryOption; + TCustomSQLQuery = class (TCustomBufDataset) private + FOptions : TSQLQueryOptions; FSchemaType : TSchemaType; FUpdateable : boolean; FTableName : string; FStatement : TCustomSQLStatement; - FUpdateSQL, FInsertSQL, - FDeleteSQL : TStringList; + FUpdateSQL, + FDeleteSQL, + FRefreshSQL : TStringList; FIsEOF : boolean; FLoadingFieldDefs : boolean; FUpdateMode : TUpdateMode; @@ -374,27 +403,30 @@ type FSchemaObjectName : string; FSchemaPattern : string; + FInsertQry, FUpdateQry, - FDeleteQry, - FInsertQry : TCustomSQLStatement; + FDeleteQry : TCustomSQLStatement; procedure FreeFldBuffers; function GetParamCheck: Boolean; function GetParams: TParams; function GetParseSQL: Boolean; function GetServerIndexDefs: TServerIndexDefs; - function GetSQL: TStringlist; + function GetSQL: TStringList; function GetSQLConnection: TSQLConnection; function GetSQLTransaction: TSQLTransaction; function GetStatementType : TStatementType; + Function NeedLastInsertID: TField; + procedure SetOptions(AValue: TSQLQueryOptions); procedure SetParamCheck(AValue: Boolean); procedure SetSQLConnection(AValue: TSQLConnection); procedure SetSQLTransaction(AValue: TSQLTransaction); - procedure SetUpdateSQL(const AValue: TStringlist); - procedure SetDeleteSQL(const AValue: TStringlist); - procedure SetInsertSQL(const AValue: TStringlist); + procedure SetInsertSQL(const AValue: TStringList); + procedure SetUpdateSQL(const AValue: TStringList); + procedure SetDeleteSQL(const AValue: TStringList); + procedure SetRefreshSQL(const AValue: TStringList); procedure SetParams(AValue: TParams); procedure SetParseSQL(AValue : Boolean); - procedure SetSQL(const AValue: TStringlist); + procedure SetSQL(const AValue: TStringList); procedure SetUsePrimaryKeyAsKey(AValue : Boolean); procedure SetUpdateMode(AValue : TUpdateMode); procedure OnChangeModifySQL(Sender : TObject); @@ -402,6 +434,9 @@ type procedure ApplyFilter; Function AddFilter(SQLstr : string) : string; protected + Function RefreshLastInsertID(Field: TField): Boolean; virtual; + Function NeedRefreshRecord (UpdateKind: TUpdateKind): Boolean; virtual; + Function RefreshRecord (UpdateKind: TUpdateKind) : Boolean; virtual; Function Cursor : TSQLCursor; Function LogEvent(EventType : TDBEventType) : Boolean; Procedure Log(EventType : TDBEventType; Const Msg : String); virtual; @@ -410,6 +445,7 @@ type function LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override; procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField); override; procedure ApplyRecUpdate(UpdateKind : TUpdateKind); override; + procedure SetPacketRecords(aValue : integer); override; // abstract & virtual methods of TDataset procedure UpdateServerIndexDefs; virtual; procedure SetDatabase(Value : TDatabase); override; @@ -445,6 +481,11 @@ type Property Prepared : boolean read IsPrepared; Property SQLConnection : TSQLConnection Read GetSQLConnection Write SetSQLConnection; Property SQLTransaction: TSQLTransaction Read GetSQLTransaction Write SetSQLTransaction; + // overriden TBufDataSet methods + Procedure ApplyUpdates(MaxErrors: Integer); override; overload; + // overriden TDataSet methods + Procedure Post; override; + Procedure Delete; override; protected // redeclared TDataSet properties property Active; @@ -473,14 +514,16 @@ type property OnNewRecord; property OnPostError; property AutoCalcFields; + // protected property Database; - // protected - property SchemaType : TSchemaType read FSchemaType default stNoSchema; property Transaction; + property SchemaType : TSchemaType read FSchemaType default stNoSchema; property SQL : TStringlist read GetSQL write SetSQL; - property UpdateSQL : TStringlist read FUpdateSQL write SetUpdateSQL; - property InsertSQL : TStringlist read FInsertSQL write SetInsertSQL; - property DeleteSQL : TStringlist read FDeleteSQL write SetDeleteSQL; + property InsertSQL : TStringList read FInsertSQL write SetInsertSQL; + property UpdateSQL : TStringList read FUpdateSQL write SetUpdateSQL; + 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; property ParseSQL : Boolean read GetParseSQL write SetParseSQL default true; @@ -534,10 +577,12 @@ type property Transaction; property ReadOnly; property SQL; - property UpdateSQL; property InsertSQL; + property UpdateSQL; property DeleteSQL; + property RefreshSQL; property IndexDefs; + Property Options; property Params; Property ParamCheck; property ParseSQL; @@ -683,6 +728,10 @@ implementation uses dbconst, strutils; +Const + // Flags to check which fields must be refreshed. + RefreshFlags : Array [ukModify..ukInsert] of TProviderFlag = (pfRefreshOnUpdate,pfRefreshOnInsert); + function TimeIntervalToString(Time: TDateTime): string; var @@ -724,11 +773,18 @@ begin else Msg := Format(CompNameFmt, [Comp.Name,Fmt]); - inherited CreateFmt(Msg, Args); + if Length(Args) = 0 then + inherited Create(Msg) + else + inherited CreateFmt(Msg, Args); + ErrorCode := AErrorCode; SQLState := ASQLState; end; +Type + TInternalTransaction = Class(TSQLTransaction); + { TCustomSQLStatement } procedure TCustomSQLStatement.OnChangeSQL(Sender: TObject); @@ -784,7 +840,7 @@ begin FDataLink.DataSource:=AValue; end; -procedure TCustomSQLStatement.CopyParamsFromMaster(CopyBound : Boolean); +Procedure TCustomSQLStatement.CopyParamsFromMaster(CopyBound: Boolean); begin if Assigned(DataSource) and Assigned(DataSource.Dataset) then FParams.CopyParamValuesFromDataset(DataSource.Dataset,CopyBound); @@ -817,8 +873,9 @@ begin end; end; -procedure TCustomSQLStatement.DoExecute; +Procedure TCustomSQLStatement.DoExecute; begin + FRowsAffected:=-1; If (FParams.Count>0) and Assigned(DataSource) then CopyParamsFromMaster(False); If LogEvent(detExecute) then @@ -826,27 +883,27 @@ begin Database.Execute(FCursor,Transaction, FParams); end; -function TCustomSQLStatement.GetPrepared: Boolean; +Function TCustomSQLStatement.GetPrepared: Boolean; begin Result := Assigned(FCursor) and FCursor.FPrepared; end; -function TCustomSQLStatement.CreateDataLink: TDataLink; +Function TCustomSQLStatement.CreateDataLink: TDataLink; begin Result:=TDataLink.Create; end; -function TCustomSQLStatement.CreateParams: TSQLDBParams; +Function TCustomSQLStatement.CreateParams: TSQLDBParams; begin Result:=TSQLDBParams.Create(Nil); end; -function TCustomSQLStatement.LogEvent(EventType: TDBEventType): Boolean; +Function TCustomSQLStatement.LogEvent(EventType: TDBEventType): Boolean; begin Result:=Assigned(Database) and Database.LogEvent(EventType); end; -procedure TCustomSQLStatement.Log(EventType: TDBEventType; const Msg: String); +Procedure TCustomSQLStatement.Log(EventType: TDBEventType; Const Msg: String); Var M : String; @@ -883,6 +940,7 @@ begin FParams:=CreateParams; FParamCheck:=True; FParseSQL:=True; + FRowsAffected:=-1; end; destructor TCustomSQLStatement.Destroy; @@ -897,28 +955,29 @@ begin inherited Destroy; end; -function TCustomSQLStatement.GetSchemaType: TSchemaType; +Function TCustomSQLStatement.GetSchemaType: TSchemaType; begin Result:=stNoSchema end; -function TCustomSQLStatement.GetSchemaObjectName: String; +Function TCustomSQLStatement.GetSchemaObjectName: String; begin Result:=''; end; -function TCustomSQLStatement.GetSchemaPattern: String; +Function TCustomSQLStatement.GetSchemaPattern: String; begin Result:=''; end; -function TCustomSQLStatement.IsSelectable: Boolean; +Function TCustomSQLStatement.IsSelectable: Boolean; begin Result:=False; end; + procedure TCustomSQLStatement.GetStatementInfo(var ASQL: String; out Info: TSQLStatementInfo); begin @@ -961,7 +1020,7 @@ begin Database.PrepareStatement(FCursor,Transaction,FServerSQL,FParams); end; -procedure TCustomSQLStatement.Prepare; +Procedure TCustomSQLStatement.Prepare; begin if Prepared then exit; @@ -969,10 +1028,9 @@ begin DatabaseError(SErrDatabasenAssigned); if not assigned(Transaction) then DatabaseError(SErrTransactionnSet); - if not Database.Connected then - Database.Open; + Database.MaybeConnect; if not Transaction.Active then - Transaction.StartTransaction; + Transaction.MaybeStartTransaction; try DoPrepare; except @@ -981,7 +1039,7 @@ begin end; end; -procedure TCustomSQLStatement.Execute; +Procedure TCustomSQLStatement.Execute; begin Prepare; DoExecute; @@ -1008,7 +1066,7 @@ begin Result:=Nil; end; -procedure TCustomSQLStatement.Unprepare; +Procedure TCustomSQLStatement.Unprepare; begin // Some SQLConnections does not support statement [un]preparation, but they have allocated local cursor(s) // so let them do cleanup f.e. cancel pending queries and/or free resultset @@ -1017,17 +1075,19 @@ begin DoUnprepare; end; -function TCustomSQLStatement.ParamByName(const AParamName: String): TParam; +function TCustomSQLStatement.ParamByName(Const AParamName: String): TParam; begin Result:=FParams.ParamByName(AParamName); end; function TCustomSQLStatement.RowsAffected: TRowsCount; begin - Result := -1; - if not Assigned(Database) then - Exit; - Result:=Database.RowsAffected(FCursor); + if FRowsAffected=-1 then + begin + if Assigned(Database) then + FRowsAffected:=Database.RowsAffected(FCursor); + end; + Result:=FRowsAffected; end; { TSQLConnection } @@ -1072,6 +1132,7 @@ begin end; end; + procedure TSQLConnection.UpdateIndexDefs(IndexDefs : TIndexDefs; TableName : string); begin // Empty abstract @@ -1126,7 +1187,8 @@ begin DatabaseError(SErrTransactionnSet); if not Connected then Open; - if not ATransaction.Active then ATransaction.StartTransaction; + if not (ATransaction.Active or (stoUseImplicit in ATransaction.Options)) then + ATransaction.MaybeStartTransaction; try SQL := TrimRight(SQL); @@ -1152,6 +1214,13 @@ begin result := StrToIntDef(Params.Values['Port'],0); end; +procedure TSQLConnection.SetOptions(AValue: TSQLConnectionOptions); +begin + if FOptions=AValue then Exit; + FOptions:=AValue; +end; + + procedure TSQLConnection.SetPort(const AValue: cardinal); begin if AValue<>0 then @@ -1440,12 +1509,12 @@ begin Result := nil; end; -function TSQLConnection.LogEvent(EventType: TDBEventType): Boolean; +Function TSQLConnection.LogEvent(EventType: TDBEventType): Boolean; begin Result:=(Assigned(FOnLog) or Assigned(GlobalDBLogHook)) and (EventType in LogEvents); end; -procedure TSQLConnection.Log(EventType: TDBEventType; const Msg: String); +Procedure TSQLConnection.Log(EventType: TDBEventType; Const Msg: String); Var M : String; @@ -1466,18 +1535,19 @@ begin end; end; -procedure TSQLConnection.RegisterStatement(S: TCustomSQLStatement); +Procedure TSQLConnection.RegisterStatement(S: TCustomSQLStatement); begin if FStatements.IndexOf(S)=-1 then FStatements.Add(S); end; -procedure TSQLConnection.UnRegisterStatement(S: TCustomSQLStatement); +Procedure TSQLConnection.UnRegisterStatement(S: TCustomSQLStatement); begin if Assigned(FStatements) then // Can be nil, when we are destroying and datasets are uncoupled. FStatements.Remove(S); end; + function TSQLConnection.InitialiseUpdateStatement(Query : TCustomSQLQuery; var qry : TCustomSQLStatement): TCustomSQLStatement; begin @@ -1491,6 +1561,7 @@ begin Result:=qry; end; + procedure TSQLConnection.AddFieldToUpdateWherePart(var sql_where : string;UpdateMode : TUpdateMode; F : TField); begin @@ -1509,28 +1580,6 @@ begin end; end; -function TSQLConnection.ConstructUpdateSQL(Query: TCustomSQLQuery): string; - -var x : integer; - F : TField; - sql_set : string; - sql_where : string; - -begin - sql_set := ''; - sql_where := ''; - for x := 0 to Query.Fields.Count -1 do - begin - F:=Query.Fields[x]; - AddFieldToUpdateWherePart(sql_where,Query.UpdateMode,F); - if (pfInUpdate in F.ProviderFlags) and (not F.ReadOnly) then - sql_set := sql_set +FieldNameQuoteChars[0] + F.FieldName + FieldNameQuoteChars[1] +'=:"' + F.FieldName + '",'; - end; - if length(sql_set) = 0 then DatabaseErrorFmt(sNoUpdateFields,['update'],self); - setlength(sql_set,length(sql_set)-1); - if length(sql_where) = 0 then DatabaseErrorFmt(sNoWhereFields,['update'],self); - result := 'update ' + Query.FTableName + ' set ' + sql_set + ' where ' + sql_where; -end; function TSQLConnection.ConstructInsertSQL(Query : TCustomSQLQuery) : string; @@ -1560,6 +1609,30 @@ begin end; +function TSQLConnection.ConstructUpdateSQL(Query: TCustomSQLQuery): string; + +var x : integer; + F : TField; + sql_set : string; + sql_where : string; + +begin + sql_set := ''; + sql_where := ''; + for x := 0 to Query.Fields.Count -1 do + begin + F:=Query.Fields[x]; + AddFieldToUpdateWherePart(sql_where,Query.UpdateMode,F); + if (pfInUpdate in F.ProviderFlags) and (not F.ReadOnly) then + sql_set := sql_set +FieldNameQuoteChars[0] + F.FieldName + FieldNameQuoteChars[1] +'=:"' + F.FieldName + '",'; + end; + if length(sql_set) = 0 then DatabaseErrorFmt(sNoUpdateFields,['update'],self); + setlength(sql_set,length(sql_set)-1); + if length(sql_where) = 0 then DatabaseErrorFmt(sNoWhereFields,['update'],self); + result := 'update ' + Query.FTableName + ' set ' + sql_set + ' where ' + sql_where; +end; + + function TSQLConnection.ConstructDeleteSQL(Query : TCustomSQLQuery) : string; var @@ -1575,6 +1648,43 @@ begin result := 'delete from ' + Query.FTableName + ' where ' + sql_where; 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]; + 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; + procedure TSQLConnection.ApplyFieldUpdate(C : TSQLCursor; P : TSQLDBParam;F : TField; UseOldValue : Boolean); begin @@ -1622,13 +1732,20 @@ begin begin P:=Qry.Params[x]; S:=p.name; - B:=Sametext(leftstr(S,4),'OLD_'); + B:=SameText(leftstr(S,4),'OLD_'); if B then Delete(S,1,4); Fld:=Query.FieldByName(S); ApplyFieldUpdate(Query.Cursor,P as TSQLDBParam,Fld,B); end; - Qry.execute; + Qry.Execute; + if (scoApplyUpdatesChecksRowsAffected in Options) and (Qry.RowsAffected<>1) then + DatabaseErrorFmt(SErrFailedToUpdateRecord, [Qry.RowsAffected], Query); +end; + +function TSQLConnection.RefreshLastInsertID(Query: TCustomSQLQuery; Field: TField): Boolean; +begin + Result:=False; end; procedure TSQLConnection.FreeFldBuffers(cursor: TSQLCursor); @@ -1636,6 +1753,11 @@ begin // empty end; +function TSQLConnection.StartImplicitTransaction(trans: TSQLHandle; aParams: string): boolean; +begin + Result:=False; +end; + function TSQLConnection.GetSchemaInfoSQL( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; begin @@ -1646,6 +1768,16 @@ begin end; end; +Procedure TSQLConnection.MaybeConnect; +begin + If Not Connected then + begin + If (scoExplicitConnect in Options) then + DatabaseErrorFmt(SErrImplicitConnect,[Name]); + Connected:=True; + end; +end; + procedure TSQLConnection.CreateDB; begin @@ -1668,7 +1800,10 @@ begin Commit; caNone, caRollback, caRollbackRetaining : - RollBack; + if not (stoUseImplicit in Options) then + RollBack + else + CloseTrans; end; end; @@ -1682,24 +1817,50 @@ begin Result:=Database as TSQLConnection; end; +procedure TSQLTransaction.SetOptions(AValue: TSQLTransactionOptions); +begin + if FOptions=AValue then Exit; + if (stoUseImplicit in Avalue) and Assigned(SQLConnection) And Not (sqImplicitTransaction in SQLConnection.ConnOptions) then + DatabaseErrorFmt(SErrNoImplicitTransaction, [SQLConnection.ClassName]); + FOptions:=AValue; +end; + procedure TSQLTransaction.SetSQLConnection(AValue: TSQLConnection); begin Database:=AValue; end; +Procedure TSQLTransaction.MaybeStartTransaction; +begin + if not Active then + begin + if (stoExplicitStart in Options) then + DatabaseErrorFmt(SErrImplictTransactionStart, [Database.Name,Name]); + StartTransaction; + end; +end; + function TSQLTransaction.GetHandle: Pointer; begin Result := SQLConnection.GetTransactionHandle(FTrans); end; +Function TSQLTransaction.AllowClose(DS: TDBDataset): Boolean; +begin + if (DS is TSQLQuery) then + Result:=not (sqoKeepOpenOnCommit in TSQLQuery(DS).Options) + else + Result:=Inherited AllowClose(DS); +end; + procedure TSQLTransaction.Commit; begin - if Active then + if Active then begin CloseDataSets; If LogEvent(detCommit) then Log(detCommit,SCommitting); - if SQLConnection.Commit(FTrans) then + if (stoUseImplicit in Options) or SQLConnection.Commit(FTrans) then begin CloseTrans; FreeAndNil(FTrans); @@ -1721,6 +1882,8 @@ procedure TSQLTransaction.Rollback; begin if Active then begin + if (stoUseImplicit in Options) then + DatabaseError(SErrImplicitNoRollBack); CloseDataSets; If LogEvent(detRollback) then Log(detRollback,SRollingBack); @@ -1736,6 +1899,8 @@ procedure TSQLTransaction.RollbackRetaining; begin if Active then begin + if (stoUseImplicit in Options) then + DatabaseError(SErrImplicitNoRollBack); If LogEvent(detRollback) then Log(detRollback,SRollBackRetaining); SQLConnection.RollBackRetaining(FTrans); @@ -1755,11 +1920,20 @@ begin if Db = nil then DatabaseError(SErrDatabasenAssigned); - if not Db.Connected then - Db.Open; + Db.MaybeConnect; + if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle; - if Db.StartdbTransaction(FTrans,FParams.CommaText) then OpenTrans; + if (stoUseImplicit in Options) then + begin + if Db.StartImplicitTransaction(FTrans,FParams.CommaText) then + OpenTrans + end + else + begin + if Db.StartdbTransaction(FTrans,FParams.CommaText) then + OpenTrans + end; end; constructor TSQLTransaction.Create(AOwner : TComponent); @@ -1777,17 +1951,21 @@ begin inherited Destroy; end; -procedure TSQLTransaction.SetDatabase(Value: TDatabase); +Procedure TSQLTransaction.SetDatabase(Value: TDatabase); begin If Value<>Database then begin - if assigned(value) and not (Value is TSQLConnection) then + if Assigned(Value) and not (Value is TSQLConnection) then DatabaseErrorFmt(SErrNotASQLConnection,[value.Name],self); CheckInactive; + if (stoUseImplicit in Options) and Assigned(Value) and Not (sqImplicitTransaction in TSQLConnection(Value).ConnOptions) then + DatabaseErrorFmt(SErrNoImplicitTransaction,[Value.ClassName]); If Assigned(Database) then + begin with SQLConnection do if Transaction = self then Transaction := nil; + end; inherited SetDatabase(Value); If Assigned(Database) and not (csLoading in ComponentState) then If (SQLConnection.Transaction=Nil) then @@ -1795,12 +1973,12 @@ begin end; end; -function TSQLTransaction.LogEvent(EventType: TDBEventType): Boolean; +Function TSQLTransaction.LogEvent(EventType: TDBEventType): Boolean; begin Result:=Assigned(Database) and SQLConnection.LogEvent(EventType); end; -procedure TSQLTransaction.Log(EventType: TDBEventType; const Msg: String); +Procedure TSQLTransaction.Log(EventType: TDBEventType; Const Msg: String); Var M : String; @@ -1909,12 +2087,14 @@ begin F.FQuery:=Self; FStatement:=F; - FUpdateSQL := TStringList.Create; - FUpdateSQL.OnChange := @OnChangeModifySQL; FInsertSQL := TStringList.Create; FInsertSQL.OnChange := @OnChangeModifySQL; + FUpdateSQL := TStringList.Create; + FUpdateSQL.OnChange := @OnChangeModifySQL; FDeleteSQL := TStringList.Create; FDeleteSQL.OnChange := @OnChangeModifySQL; + FRefreshSQL := TStringList.Create; + FRefreshSQL.OnChange := @OnChangeModifySQL; FServerIndexDefs := TServerIndexDefs.Create(Self); @@ -1937,13 +2117,14 @@ begin UnPrepare; FreeAndNil(FStatement); FreeAndNil(FInsertSQL); - FreeAndNil(FDeleteSQL); FreeAndNil(FUpdateSQL); + FreeAndNil(FDeleteSQL); + FreeAndNil(FRefreshSQL); FServerIndexDefs.Free; inherited Destroy; end; -function TCustomSQLQuery.ParamByName(const AParamName: String): TParam; +function TCustomSQLQuery.ParamByName(Const AParamName: String): TParam; begin Result:=Params.ParamByName(AParamName); @@ -1955,7 +2136,7 @@ begin CheckInactive; end; -procedure TCustomSQLQuery.SetTransaction(Value: TDBTransaction); +Procedure TCustomSQLQuery.SetTransaction(Value: TDBTransaction); begin UnPrepare; @@ -1985,7 +2166,7 @@ begin end; end; -function TCustomSQLQuery.IsPrepared: Boolean; +Function TCustomSQLQuery.IsPrepared: Boolean; begin if Assigned(Fstatement) then @@ -1994,7 +2175,7 @@ begin Result := False; end; -function TCustomSQLQuery.AddFilter(SQLstr: string): string; +Function TCustomSQLQuery.AddFilter(SQLstr: string): string; begin if (FWhereStartPos > 0) and (FWhereStopPos > 0) then @@ -2012,6 +2193,76 @@ begin Result := SQLstr; end; +Function TCustomSQLQuery.NeedRefreshRecord(UpdateKind: TUpdateKind): Boolean; + + +Var + PF : TProviderFlag; + I : Integer; +begin + Result:=(FRefreshSQL.Count<>0); + if Not Result then + begin + PF:=RefreshFlags[UpdateKind]; + I:=0; + While (Not Result) and (I<Fields.Count) do + begin + Result:=PF 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; + +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 @@ -2025,7 +2276,7 @@ begin First; end; -procedure TCustomSQLQuery.SetActive(Value: Boolean); +Procedure TCustomSQLQuery.SetActive(Value: Boolean); begin inherited SetActive(Value); @@ -2056,6 +2307,7 @@ begin end; end; + procedure TCustomSQLQuery.Prepare; begin @@ -2114,7 +2366,7 @@ begin Result:=Transaction as TSQLTransaction; end; -function TCustomSQLQuery.Cursor: TSQLCursor; +Function TCustomSQLQuery.Cursor: TSQLCursor; begin Result:=FStatement.Cursor; end; @@ -2138,7 +2390,7 @@ end; function TCustomSQLQuery.RowsAffected: TRowsCount; begin - Result:=Fstatement.RowsAffected; + Result:=FStatement.RowsAffected; end; function TCustomSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; @@ -2268,6 +2520,12 @@ begin try Prepare; Execute; + If sqoAutoCommit in Options then + begin + // Retrieve rows affected + FStatement.RowsAffected; + SQLTransaction.Commit; + end; finally // Cursor has to be assigned, or else the prepare went wrong before PrepareStatment was // called, so UnPrepareStatement shoudn't be called either @@ -2276,6 +2534,31 @@ begin end; end; +Procedure TCustomSQLQuery.ApplyUpdates(MaxErrors: Integer); +begin + inherited ApplyUpdates(MaxErrors); + If sqoAutoCommit in Options then + begin + // Retrieve rows affected for last update. + FStatement.RowsAffected; + SQLTransaction.Commit; + end; +end; + +Procedure TCustomSQLQuery.Post; +begin + inherited Post; + If (sqoAutoApplyUpdates in Options) then + ApplyUpdates; +end; + +Procedure TCustomSQLQuery.Delete; +begin + inherited Delete; + If (sqoAutoApplyUpdates in Options) then + ApplyUpdates; +end; + procedure TCustomSQLQuery.SetReadOnly(AValue : Boolean); begin @@ -2297,11 +2580,6 @@ begin FStatement.SQL.Assign(AValue); end; -procedure TCustomSQLQuery.SetUpdateSQL(const AValue: TStringlist); -begin - FUpdateSQL.Assign(AValue); -end; - procedure TCustomSQLQuery.SetUsePrimaryKeyAsKey(AValue : Boolean); begin @@ -2321,12 +2599,75 @@ 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) or not Result.IsNull then + Result:=Nil; + Inc(I); + end; + end +end; + +Function TCustomSQLQuery.RefreshLastInsertID(Field : TField) : Boolean; + +begin + Result:=SQLConnection.RefreshLastInsertID(Self, Field); +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); + SQLConnection.ApplyRecUpdate(Self,UpdateKind); + + if UpdateKind=ukInsert then + LastIDField:=NeedLastInsertID + else + LastIDField:=nil; + DoRefresh:=(UpdateKind in [ukModify,ukInsert]) and NeedRefreshRecord(UpdateKind); + if assigned(LastIDField) or DoRefresh then + begin + // updates fields directly in record buffer of TBufDataSet + // TDataSet buffers are resynchronized at end of ApplyUpdates process + S:=SetTempState(dsRefreshFields); + try + RecordRefreshed:=False; + if assigned(LastIDField) then + RecordRefreshed:=RefreshLastInsertID(LastIDField); + if DoRefresh then + RecordRefreshed:=RefreshRecord(UpdateKind) or RecordRefreshed; + finally + RestoreState(S); + end; + if RecordRefreshed then + // Active buffer is updated, move to record. + //ActiveBufferToRecord; + end; +end; + +procedure TCustomSQLQuery.SetPacketRecords(aValue: integer); +begin + if (AValue=PacketRecords) then exit; + if (AValue<>-1) and (sqoKeepOpenOnCommit in Options) then + DatabaseError(SErrDisconnectedPacketRecords); + Inherited SetPacketRecords(aValue); end; @@ -2364,12 +2705,12 @@ begin UnPrepareStatement(Cursor); end; -function TCustomSQLQuery.LogEvent(EventType: TDBEventType): Boolean; +Function TCustomSQLQuery.LogEvent(EventType: TDBEventType): Boolean; begin Result:=Assigned(Database) and SQLConnection.LogEvent(EventType); end; -procedure TCustomSQLQuery.Log(EventType: TDBEventType; const Msg: String); +Procedure TCustomSQLQuery.Log(EventType: TDBEventType; Const Msg: String); Var M : String; @@ -2403,6 +2744,15 @@ begin FStatement.ParamCheck:=AValue; end; +procedure TCustomSQLQuery.SetOptions(AValue: TSQLQueryOptions); +begin + if FOptions=AValue then Exit; + CheckInactive; + FOptions:=AValue; + if sqoKeepOpenOnCommit in FOptions then + PacketRecords:=-1; +end; + procedure TCustomSQLQuery.SetSQLConnection(AValue: TSQLConnection); begin Database:=AValue; @@ -2413,22 +2763,33 @@ begin Transaction:=AValue; end; -procedure TCustomSQLQuery.SetDeleteSQL(const AValue: TStringlist); +procedure TCustomSQLQuery.SetInsertSQL(const AValue: TStringList); +begin + FInsertSQL.Assign(AValue); +end; + +procedure TCustomSQLQuery.SetUpdateSQL(const AValue: TStringList); +begin + FUpdateSQL.Assign(AValue); +end; + +procedure TCustomSQLQuery.SetDeleteSQL(const AValue: TStringList); begin FDeleteSQL.Assign(AValue); end; -procedure TCustomSQLQuery.SetInsertSQL(const AValue: TStringlist); +procedure TCustomSQLQuery.SetRefreshSQL(const AValue: TStringList); begin - FInsertSQL.Assign(AValue); + FRefreshSQL.Assign(AValue); end; + procedure TCustomSQLQuery.SetParams(AValue: TParams); begin FStatement.Params.Assign(AValue); end; -procedure TCustomSQLQuery.SetDataSource(AValue: TDataSource); +Procedure TCustomSQLQuery.SetDataSource(AValue: TDataSource); Var DS : TDataSource; @@ -2445,7 +2806,7 @@ begin end; end; -function TCustomSQLQuery.GetDataSource: TDataSource; +Function TCustomSQLQuery.GetDataSource: TDataSource; begin If Assigned(FStatement) then |