1 {*********************************************************}
3 { Zeos Database Objects }
4 { Generic Cached Resolver }
6 { Originally written by Sergey Seroukhov }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
52 unit ZDbcGenericResolver;
59 Types, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils, Contnrs,
60 ZVariant, ZDbcIntfs, ZDbcCache, ZDbcCachedResultSet, ZCompatibility,
65 {** Implements a resolver parameter object. }
66 TZResolverParameter = class (TObject)
68 FColumnIndex: Integer;
70 FColumnType: TZSQLType;
72 FDefaultValue: string;
74 constructor Create(ColumnIndex: Integer; ColumnName: string;
75 ColumnType: TZSQLType; NewValue: Boolean; DefaultValue: string);
77 property ColumnIndex: Integer read FColumnIndex write FColumnIndex;
78 property ColumnName: string read FColumnName write FColumnName;
79 property ColumnType: TZSQLType read FColumnType write FColumnType;
80 property NewValue: Boolean read FNewValue write FNewValue;
81 property DefaultValue: string read FDefaultValue write FDefaultValue;
85 Implements a generic cached resolver object which generates
86 DML SQL statements and posts resultset updates to database.
89 { TZGenericCachedResolver }
91 TZGenericCachedResolver = class (TInterfacedObject, IZCachedResolver)
93 FConnection: IZConnection;
94 FStatement : IZStatement;
95 FMetadata: IZResultSetMetadata;
96 FDatabaseMetadata: IZDatabaseMetadata;
97 FIdentifierConvertor: IZIdentifierConvertor;
99 FInsertColumns: TObjectList;
100 FUpdateColumns: TObjectList;
101 FWhereColumns: TObjectList;
103 FCalcDefaults: Boolean;
107 InsertStatement : IZPreparedStatement;
108 UpdateStatement : IZPreparedStatement;
109 DeleteStatement : IZPreparedStatement;
112 procedure CopyResolveParameters(FromList, ToList: TObjectList);
113 function ComposeFullTableName(Catalog, Schema, Table: string): string;
114 function DefineTableName: string;
116 function CreateResolverStatement(SQL : String):IZPreparedStatement;
118 procedure DefineCalcColumns(Columns: TObjectList;
119 RowAccessor: TZRowAccessor);
120 procedure DefineInsertColumns(Columns: TObjectList);
121 procedure DefineUpdateColumns(Columns: TObjectList;
122 OldRowAccessor, NewRowAccessor: TZRowAccessor);
123 procedure DefineWhereKeyColumns(Columns: TObjectList);
124 procedure DefineWhereAllColumns(Columns: TObjectList; IgnoreKeyColumn: Boolean = False);
125 function CheckKeyColumn(ColumnIndex: Integer): Boolean; virtual;
127 procedure FillStatement(Statement: IZPreparedStatement;
128 Params: TObjectList; OldRowAccessor, NewRowAccessor: TZRowAccessor);
130 property Connection: IZConnection read FConnection write FConnection;
131 property Metadata: IZResultSetMetadata read FMetadata write FMetadata;
132 property DatabaseMetadata: IZDatabaseMetadata read FDatabaseMetadata
133 write FDatabaseMetadata;
134 property IdentifierConvertor: IZIdentifierConvertor
135 read FIdentifierConvertor write FIdentifierConvertor;
137 property InsertColumns: TObjectList read FInsertColumns;
138 property UpdateColumns: TObjectList read FUpdateColumns;
139 property WhereColumns: TObjectList read FWhereColumns;
141 property CalcDefaults: Boolean read FCalcDefaults write FCalcDefaults;
142 property WhereAll: Boolean read FWhereAll write FWhereAll;
143 property UpdateAll: Boolean read FUpdateAll write FUpdateAll;
146 constructor Create(Statement: IZStatement; Metadata: IZResultSetMetadata);
147 destructor Destroy; override;
149 function FormWhereClause(Columns: TObjectList;
150 OldRowAccessor: TZRowAccessor): string; virtual;
151 function FormInsertStatement(Columns: TObjectList;
152 NewRowAccessor: TZRowAccessor): string;
153 function FormUpdateStatement(Columns: TObjectList;
154 OldRowAccessor, NewRowAccessor: TZRowAccessor): string;
155 function FormDeleteStatement(Columns: TObjectList;
156 OldRowAccessor: TZRowAccessor): string;
157 function FormCalculateStatement(Columns: TObjectList): string; virtual;
159 procedure CalculateDefaults(Sender: IZCachedResultSet;
160 RowAccessor: TZRowAccessor);
161 procedure PostUpdates(Sender: IZCachedResultSet;
162 UpdateType: TZRowUpdateType;
163 OldRowAccessor, NewRowAccessor: TZRowAccessor); virtual;
164 {BEGIN of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
165 procedure UpdateAutoIncrementFields(Sender: IZCachedResultSet;
166 UpdateType: TZRowUpdateType;
167 OldRowAccessor, NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver); virtual;
168 {END of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
169 procedure RefreshCurrentRow(Sender: IZCachedResultSet;RowAccessor: TZRowAccessor); //FOS+ 07112006
175 uses ZMessages, ZSysUtils, ZDbcMetadata, ZDbcUtils;
177 { TZResolverParameter }
180 Constructs this resolver parameter and assignes the main properties.
181 @param ColumnIndex a result set column index.
182 @param ColumnName a result set column name.
183 @param NewValue <code>True</code> for new value and <code>False</code>
185 @param DefaultValue a default column value to evalute on server.
187 constructor TZResolverParameter.Create(ColumnIndex: Integer;
188 ColumnName: string; ColumnType: TZSQLType; NewValue: Boolean; DefaultValue: string);
190 FColumnType := ColumnType;
191 FColumnIndex := ColumnIndex;
192 FColumnName := ColumnName;
193 FNewValue := NewValue;
194 FDefaultValue := DefaultValue;
197 { TZGenericCachedResolver }
200 Creates a cached resolver and assignes the main properties.
201 @param ResultSet a related ResultSet object.
203 constructor TZGenericCachedResolver.Create(Statement: IZStatement;
204 Metadata: IZResultSetMetadata);
206 FStatement := Statement;
207 FConnection := Statement.GetConnection;
208 FMetadata := Metadata;
209 FDatabaseMetadata := Statement.GetConnection.GetMetadata;
210 FIdentifierConvertor := FDatabaseMetadata.GetIdentifierConvertor;
212 FInsertColumns := TObjectList.Create(True);
213 FWhereColumns := TObjectList.Create(True);
214 FUpdateColumns := TObjectList.Create(True);
216 FCalcDefaults := StrToBoolEx(DefineStatementParameter(Statement,
217 'defaults', 'true'));
218 FUpdateAll := UpperCase(DefineStatementParameter(Statement,
219 'update', 'changed')) = 'ALL';
220 FWhereAll := UpperCase(DefineStatementParameter(Statement,
221 'where', 'keyonly')) = 'ALL';
223 InsertStatement := nil;
224 UpdateStatement := nil;
225 DeleteStatement := nil;
230 Destroys this object and cleanups the memory.
232 destructor TZGenericCachedResolver.Destroy;
235 FDatabaseMetadata := nil;
237 FreeAndNil(FInsertColumns);
238 FreeAndNil(FUpdateColumns);
239 FreeAndNil(FWhereColumns);
245 Copies resolver parameters from source list to destination list.
246 @param FromList the source object list.
247 @param ToList the destination object list.
249 procedure TZGenericCachedResolver.CopyResolveParameters(
250 FromList: TObjectList; ToList: TObjectList);
253 Current: TZResolverParameter;
255 for I := 0 to FromList.Count - 1 do
257 Current := TZResolverParameter(FromList[I]);
258 if Current.ColumnName <> '' then
259 ToList.Add(TZResolverParameter.Create(Current.ColumnIndex,
260 Current.ColumnName, Current.ColumnType, Current.NewValue, ''));
265 Composes a fully quilified table name.
266 @param Catalog a table catalog name.
267 @param Schema a table schema name.
268 @param Table a table name.
269 @return a fully qualified table name.
271 function TZGenericCachedResolver.ComposeFullTableName(Catalog, Schema,
272 Table: string): string;
276 Result := IdentifierConvertor.Quote(Table);
278 Result := IdentifierConvertor.Quote(Schema) + '.' + Result;
279 if Catalog <> '' then
280 Result := IdentifierConvertor.Quote(Catalog) + '.' + Result;
287 Defines a table name from the select statement.
289 function TZGenericCachedResolver.DefineTableName: string;
295 for I := 1 to Metadata.GetColumnCount do
297 Temp := ComposeFullTableName(Metadata.GetCatalogName(I),
298 Metadata.GetSchemaName(I), Metadata.GetTableName(I));
299 if (Result = '') and (Temp <> '') then
301 else if (Result <> '') and (Temp <> '') and (Temp <> Result) then
302 raise EZSQLException.Create(SCanNotUpdateComplexQuery);
305 raise EZSQLException.Create(SCanNotUpdateThisQueryType);
308 function TZGenericCachedResolver.CreateResolverStatement(SQL: String): IZPreparedStatement;
312 if StrToBoolEx(FStatement.GetParameters.Values['preferprepared']) then
314 Temp := TStringList.Create;
315 Temp.Values['preferprepared'] := 'true';
316 if not ( Connection.GetParameters.Values['chunk_size'] = '' ) then //ordered by precedence
317 Temp.Values['chunk_size'] := Connection.GetParameters.Values['chunk_size']
319 Temp.Values['chunk_size'] := FStatement.GetParameters.Values['chunk_size'];
320 Result := Connection.PrepareStatementWithParams(SQL, Temp);
324 Result := Connection.PrepareStatement(SQL);
329 Gets a collection of data columns for INSERT statements.
330 @param Columns a collection of columns.
332 procedure TZGenericCachedResolver.DefineInsertColumns(Columns: TObjectList);
336 { Precache insert parameters. }
337 if InsertColumns.Count = 0 then
339 for I := 1 to Metadata.GetColumnCount do
341 if (Metadata.GetTableName(I) <> '') and (Metadata.GetColumnName(I) <> '')
342 and Metadata.IsWritable(I) then
344 InsertColumns.Add(TZResolverParameter.Create(I,
345 Metadata.GetColumnName(I), Metadata.GetColumnType(I), True, ''));
349 { Use cached insert parameters }
350 CopyResolveParameters(InsertColumns, Columns);
354 Gets a collection of data columns for UPDATE statements.
355 @param Columns a collection of columns.
356 @param OldRowAccessor an accessor object to old column values.
357 @param NewRowAccessor an accessor object to new column values.
359 procedure TZGenericCachedResolver.DefineUpdateColumns(
360 Columns: TObjectList; OldRowAccessor, NewRowAccessor: TZRowAccessor);
363 ColumnIndices: TIntegerDynArray;
364 ColumnDirs: TBooleanDynArray;
366 { Use precached parameters. }
367 if UpdateAll and (UpdateColumns.Count > 0) then
369 CopyResolveParameters(UpdateColumns, Columns);
373 { Defines parameters for UpdateAll mode. }
376 for I := 1 to Metadata.GetColumnCount do
378 if (Metadata.GetTableName(I) <> '') and (Metadata.GetColumnName(I) <> '')
379 and Metadata.IsWritable(I) then
381 UpdateColumns.Add(TZResolverParameter.Create(I,
382 Metadata.GetColumnName(I), Metadata.GetColumnType(I), True, ''));
385 CopyResolveParameters(UpdateColumns, Columns);
387 { Defines parameters for UpdateChanged mode. }
390 SetLength(ColumnIndices, 1);
391 SetLength(ColumnDirs, 1);
392 ColumnDirs[0] := True;
393 for I := 1 to Metadata.GetColumnCount do
395 ColumnIndices[0] := I;
396 if (Metadata.GetTableName(I) <> '') and (Metadata.GetColumnName(I) <> '')
397 and Metadata.IsWritable(I) and (OldRowAccessor.CompareBuffers(
398 OldRowAccessor.RowBuffer, NewRowAccessor.RowBuffer, ColumnIndices,
399 ColumnDirs) <> 0)then
401 Columns.Add(TZResolverParameter.Create(I,
402 Metadata.GetColumnName(I), Metadata.GetColumnType(I), True, ''));
409 Gets a collection of where key columns for DELETE or UPDATE DML statements.
410 @param Columns a collection of key columns.
412 procedure TZGenericCachedResolver.DefineWhereKeyColumns(Columns: TObjectList);
417 Catalog, Schema, Table: string;
418 PrimaryKeys: IZResultSet;
420 { Use precached values. }
421 if WhereColumns.Count > 0 then
423 CopyResolveParameters(WhereColumns, Columns);
427 { Defines catalog, schema and a table. }
428 Table := DefineTableName;
429 for I := 1 to Metadata.GetColumnCount do
431 Table := Metadata.GetTableName(I);
434 Schema := Metadata.GetSchemaName(I);
435 Catalog := Metadata.GetCatalogName(I);
440 { Tryes to define primary keys. }
443 {For exact results: quote all identifiers SEE: http://sourceforge.net/p/zeoslib/tickets/81/
444 If table names have mixed case ConstructNameCondition will return wrong results
445 and we fall back to WhereAll}
446 PrimaryKeys := DatabaseMetadata.GetPrimaryKeys(IdentifierConvertor.Quote(Catalog),
447 IdentifierConvertor.Quote(Schema), IdentifierConvertor.Quote(Table));
448 while PrimaryKeys.Next do
450 ColumnName := PrimaryKeys.GetString(4);
452 for I := 1 to Metadata.GetColumnCount do
454 if (ColumnName = Metadata.GetColumnName(I))
455 and (Table = Metadata.GetTableName(I)) then
466 WhereColumns.Add(TZResolverParameter.Create(I, ColumnName,
467 stUnknown, False, ''));
471 if WhereColumns.Count > 0 then
472 CopyResolveParameters(WhereColumns, Columns)
474 DefineWhereAllColumns(Columns);
478 Gets a collection of where all columns for DELETE or UPDATE DML statements.
479 @param Columns a collection of key columns.
481 procedure TZGenericCachedResolver.DefineWhereAllColumns(Columns: TObjectList;
482 IgnoreKeyColumn: Boolean = False);
486 { Use precached values. }
487 if WhereColumns.Count > 0 then
489 CopyResolveParameters(WhereColumns, Columns);
493 { Takes a a key all non-blob fields. }
494 for I := 1 to Metadata.GetColumnCount do
496 if CheckKeyColumn(I) then
497 WhereColumns.Add(TZResolverParameter.Create(I,
498 Metadata.GetColumnName(I), Metadata.GetColumnType(I), False, ''))
500 if IgnoreKeyColumn then
501 WhereColumns.Add(TZResolverParameter.Create(I,
502 Metadata.GetColumnName(I), Metadata.GetColumnType(I), False, ''));
504 if ( WhereColumns.Count = 0 ) and ( not IgnoreKeyColumn ) then
505 DefineWhereAllColumns(Columns, True)
507 { Copy defined parameters to target columns }
508 CopyResolveParameters(WhereColumns, Columns);
512 Checks is the specified column can be used in where clause.
513 @param ColumnIndex an index of the column.
514 @returns <code>true</code> if column can be included into where clause.
516 function TZGenericCachedResolver.CheckKeyColumn(ColumnIndex: Integer): Boolean;
518 Result := (Metadata.GetTableName(ColumnIndex) <> '')
519 and (Metadata.GetColumnName(ColumnIndex) <> '')
520 and Metadata.IsSearchable(ColumnIndex)
521 and not (Metadata.GetColumnType(ColumnIndex)
522 in [stUnknown, stAsciiStream, stBinaryStream, stUnicodeStream]);
526 Gets a collection of data columns to initialize before INSERT statements.
527 @param Columns a collection of columns.
528 @param RowAccessor an accessor object to column values.
530 procedure TZGenericCachedResolver.DefineCalcColumns(Columns: TObjectList;
531 RowAccessor: TZRowAccessor);
535 for I := 1 to Metadata.GetColumnCount do
537 if RowAccessor.IsNull(I) and (Metadata.GetTableName(I) <> '')
538 and ((Metadata.GetDefaultValue(I) <> '') or (RowAccessor.GetColumnDefaultExpression(I) <> '')) then
540 // DefaultExpression takes takes precedence on database default value
541 if RowAccessor.GetColumnDefaultExpression(I) <> '' then
542 Columns.Add(TZResolverParameter.Create(I,
543 Metadata.GetColumnName(I), Metadata.GetColumnType(I),
544 True, RowAccessor.GetColumnDefaultExpression(I)))
546 Columns.Add(TZResolverParameter.Create(I,
547 Metadata.GetColumnName(I), Metadata.GetColumnType(I),
548 True, Metadata.GetDefaultValue(I)));
554 Fills the specified statement with stored or given parameters.
555 @param ResultSet a source result set object.
556 @param Statement a DBC statement object.
557 @param Config an UpdateStatement configuration.
558 @param OldRowAccessor an accessor object to old column values.
559 @param NewRowAccessor an accessor object to new column values.
561 procedure TZGenericCachedResolver.FillStatement(Statement: IZPreparedStatement;
562 Params: TObjectList; OldRowAccessor, NewRowAccessor: TZRowAccessor);
565 ColumnIndex: Integer;
566 Current: TZResolverParameter;
567 RowAccessor: TZRowAccessor;
571 for I := 0 to Params.Count - 1 do
573 Current := TZResolverParameter(Params[I]);
574 if Current.NewValue then
575 RowAccessor := NewRowAccessor
577 RowAccessor := OldRowAccessor;
578 ColumnIndex := Current.ColumnIndex;
580 if FCalcDefaults then
581 Statement.SetDefaultValue(I + 1, Metadata.GetDefaultValue(ColumnIndex));
583 case Metadata.GetColumnType(ColumnIndex) of
585 Statement.SetBoolean(I + 1,
586 RowAccessor.GetBoolean(ColumnIndex, WasNull));
588 Statement.SetByte(I + 1, RowAccessor.GetByte(ColumnIndex, WasNull));
590 Statement.SetShort(I + 1, RowAccessor.GetShort(ColumnIndex, WasNull));
592 Statement.SetInt(I + 1, RowAccessor.GetInt(ColumnIndex, WasNull));
594 Statement.SetLong(I + 1, RowAccessor.GetLong(ColumnIndex, WasNull));
596 Statement.SetFloat(I + 1, RowAccessor.GetFloat(ColumnIndex, WasNull));
598 Statement.SetDouble(I + 1, RowAccessor.GetDouble(ColumnIndex, WasNull));
600 Statement.SetBigDecimal(I + 1,
601 RowAccessor.GetBigDecimal(ColumnIndex, WasNull));
603 Statement.SetString(I + 1, RowAccessor.GetString(ColumnIndex, WasNull));
605 Statement.SetUnicodeString(I + 1,
606 RowAccessor.GetUnicodeString(ColumnIndex, WasNull));
608 Statement.SetBytes(I + 1, RowAccessor.GetBytes(ColumnIndex, WasNull));
610 Statement.SetDate(I + 1, RowAccessor.GetDate(ColumnIndex, WasNull));
612 Statement.SetTime(I + 1, RowAccessor.GetTime(ColumnIndex, WasNull));
614 Statement.SetTimestamp(I + 1,
615 RowAccessor.GetTimestamp(ColumnIndex, WasNull));
617 Statement.SetBlob(I + 1, stAsciiStream,
618 RowAccessor.GetBlob(ColumnIndex, WasNull));
620 Statement.SetBlob(I + 1, stUnicodeStream,
621 RowAccessor.GetBlob(ColumnIndex, WasNull));
623 Statement.SetBlob(I + 1, stBinaryStream,
624 RowAccessor.GetBlob(ColumnIndex, WasNull));
627 Statement.SetNull(I + 1, Metadata.GetColumnType(ColumnIndex))
632 Forms a where clause for UPDATE or DELETE DML statements.
633 @param Columns a collection of key columns.
634 @param OldRowAccessor an accessor object to old column values.
636 function TZGenericCachedResolver.FormWhereClause(Columns: TObjectList;
637 OldRowAccessor: TZRowAccessor): string;
640 Current: TZResolverParameter;
643 N := Columns.Count - WhereColumns.Count;
645 for I := 0 to WhereColumns.Count - 1 do
647 Current := TZResolverParameter(WhereColumns[I]);
649 Result := Result + ' AND ';
651 Result := Result + IdentifierConvertor.Quote(Current.ColumnName);
652 if OldRowAccessor.IsNull(Current.ColumnIndex) then
654 Result := Result + ' IS NULL ';
659 Result := Result + '=?';
665 Result := ' WHERE ' + Result;
669 Forms a where clause for INSERT statements.
670 @param Columns a collection of key columns.
671 @param NewRowAccessor an accessor object to new column values.
673 function TZGenericCachedResolver.FormInsertStatement(Columns: TObjectList;
674 NewRowAccessor: TZRowAccessor): string;
677 Current: TZResolverParameter;
679 Temp1, Temp2: string;
682 procedure Append(const app: String);
684 if Length(Temp1) < l1 + length(app) then
685 SetLength(Temp1, 2 * (length(app) + l1));
686 Move(app[1], Temp1[l1+1], length(app)*SizeOf(Char));
687 Inc(l1, length(app));
691 TableName := DefineTableName;
692 DefineInsertColumns(Columns);
693 if Columns.Count = 0 then
699 Temp1 := ''; l1 := 0;
700 SetLength(Temp2, 2 * Columns.Count - 1);
701 for I := 0 to Columns.Count - 1 do
703 Current := TZResolverParameter(Columns[I]);
706 Append(IdentifierConvertor.Quote(Current.ColumnName));
711 SetLength(Temp1, l1);
712 Result := Format('INSERT INTO %s (%s) VALUES (%s)', [TableName, Temp1, Temp2]);
716 Forms a where clause for UPDATE statements.
717 @param Columns a collection of key columns.
718 @param OldRowAccessor an accessor object to old column values.
719 @param NewRowAccessor an accessor object to new column values.
721 function TZGenericCachedResolver.FormUpdateStatement(Columns: TObjectList;
722 OldRowAccessor, NewRowAccessor: TZRowAccessor): string;
725 Current: TZResolverParameter;
729 TableName := DefineTableName;
730 DefineUpdateColumns(Columns, OldRowAccessor, NewRowAccessor);
731 if Columns.Count = 0 then
738 for I := 0 to Columns.Count - 1 do
740 Current := TZResolverParameter(Columns[I]);
743 Temp := Temp + IdentifierConvertor.Quote(Current.ColumnName) + '=?';
746 Result := Format('UPDATE %s SET %s', [TableName, Temp]);
747 DefineWhereKeyColumns(Columns);
748 Result := Result + FormWhereClause(Columns, OldRowAccessor);
752 Forms a where clause for DELETE statements.
753 @param Columns a collection of key columns.
754 @param OldRowAccessor an accessor object to old column values.
756 function TZGenericCachedResolver.FormDeleteStatement(Columns: TObjectList;
757 OldRowAccessor: TZRowAccessor): string;
761 TableName := DefineTableName;
762 Result := Format('DELETE FROM %s', [TableName]);
763 DefineWhereKeyColumns(Columns);
764 Result := Result + FormWhereClause(Columns, OldRowAccessor);
768 Forms a where clause for SELECT statements to calculate default values.
769 @param Columns a collection of key columns.
770 @param OldRowAccessor an accessor object to old column values.
772 function TZGenericCachedResolver.FormCalculateStatement(
773 Columns: TObjectList): string;
776 Current: TZResolverParameter;
779 if Columns.Count = 0 then
782 for I := 0 to Columns.Count - 1 do
784 Current := TZResolverParameter(Columns[I]);
786 Result := Result + ',';
787 if Current.DefaultValue <> '' then
788 Result := Result + Current.DefaultValue
790 Result := Result + 'NULL';
792 Result := 'SELECT ' + Result;
796 Posts updates to database.
797 @param Sender a cached result set object.
798 @param UpdateType a type of updates.
799 @param OldRowAccessor an accessor object to old column values.
800 @param NewRowAccessor an accessor object to new column values.
802 procedure TZGenericCachedResolver.PostUpdates(Sender: IZCachedResultSet;
803 UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
805 Statement : IZPreparedStatement;
807 SQLParams : TObjectList;
808 lUpdateCount : Integer;
809 lValidateUpdateCount : Boolean;
812 if (UpdateType = utDeleted)
813 and (OldRowAccessor.RowBuffer.UpdateType = utInserted) then
816 SQLParams := TObjectList.Create(True);
821 SQL := FormInsertStatement(SQLParams, NewRowAccessor);
822 If Assigned(InsertStatement) and (SQL <> InsertStatement.GetSQL) then
823 InsertStatement := nil;
824 If not Assigned(InsertStatement) then
825 InsertStatement := CreateResolverStatement(SQL);
826 Statement := InsertStatement;
830 SQL := FormDeleteStatement(SQLParams, OldRowAccessor);
831 If Assigned(DeleteStatement) and (SQL <> DeleteStatement.GetSQL) then
832 DeleteStatement := nil;
833 If not Assigned(DeleteStatement) then
834 DeleteStatement := CreateResolverStatement(SQL);
835 Statement := DeleteStatement;
839 SQL := FormUpdateStatement(SQLParams, OldRowAccessor, NewRowAccessor);
840 If SQL =''then // no fields have been changed
842 If Assigned(UpdateStatement) and (SQL <> UpdateStatement.GetSQL) then
843 UpdateStatement := nil;
844 If not Assigned(UpdateStatement) then
845 UpdateStatement := CreateResolverStatement(SQL);
846 Statement := UpdateStatement;
855 FillStatement(Statement, SQLParams, OldRowAccessor, NewRowAccessor);
856 // if Property ValidateUpdateCount isn't set : assume it's true
857 lValidateUpdateCount := (Sender.GetStatement.GetParameters.IndexOfName('ValidateUpdateCount') = -1)
858 or StrToBoolEx(Sender.GetStatement.GetParameters.Values['ValidateUpdateCount']);
860 lUpdateCount := Statement.ExecuteUpdatePrepared;
861 {$IFDEF WITH_VALIDATE_UPDATE_COUNT}
862 if (lValidateUpdateCount) and (lUpdateCount <> 1 ) then
863 raise EZSQLException.Create(Format(SInvalidUpdateCount, [lUpdateCount]));
867 FreeAndNil(SQLParams);
871 procedure TZGenericCachedResolver.RefreshCurrentRow(Sender: IZCachedResultSet; RowAccessor: TZRowAccessor);
873 raise EZSQLException.Create(SRefreshRowOnlySupportedWithUpdateObject);
877 Calculate default values for the fields.
878 @param Sender a cached result set object.
879 @param RowAccessor an accessor object to column values.
881 procedure TZGenericCachedResolver.CalculateDefaults(
882 Sender: IZCachedResultSet; RowAccessor: TZRowAccessor);
886 SQLParams: TObjectList;
887 Statement: IZStatement;
888 ResultSet: IZResultSet;
889 Metadata: IZResultSetMetadata;
890 Current: TZResolverParameter;
892 if not FCalcDefaults then
895 SQLParams := TObjectList.Create(True);
897 DefineCalcColumns(SQLParams, RowAccessor);
898 SQL := FormCalculateStatement(SQLParams);
902 { Executes statement and fills default fields. }
903 Statement := Connection.CreateStatement;
905 ResultSet := Statement.ExecuteQuery(SQL);
906 if ResultSet.Next then
908 Metadata := ResultSet.GetMetadata;
909 for I := 1 to Metadata.GetColumnCount do
911 Current := TZResolverParameter(SQLParams[I - 1]);
913 case Current.ColumnType of
915 RowAccessor.SetBoolean(Current.ColumnIndex,
916 ResultSet.GetBoolean(I));
918 RowAccessor.SetByte(Current.ColumnIndex, ResultSet.GetByte(I));
920 RowAccessor.SetShort(Current.ColumnIndex, ResultSet.GetShort(I));
922 RowAccessor.SetInt(Current.ColumnIndex, ResultSet.GetInt(I));
924 RowAccessor.SetLong(Current.ColumnIndex, ResultSet.GetLong(I));
926 RowAccessor.SetFloat(Current.ColumnIndex, ResultSet.GetFloat(I));
928 RowAccessor.SetDouble(Current.ColumnIndex, ResultSet.GetDouble(I));
930 RowAccessor.SetBigDecimal(Current.ColumnIndex, ResultSet.GetBigDecimal(I));
931 stString, stAsciiStream:
932 RowAccessor.SetString(Current.ColumnIndex, ResultSet.GetString(I));
933 stUnicodeString, stUnicodeStream:
934 RowAccessor.SetUnicodeString(Current.ColumnIndex, ResultSet.GetUnicodeString(I));
936 RowAccessor.SetBytes(Current.ColumnIndex, ResultSet.GetBytes(I));
938 RowAccessor.SetDate(Current.ColumnIndex, ResultSet.GetDate(I));
940 RowAccessor.SetTime(Current.ColumnIndex, ResultSet.GetTime(I));
942 RowAccessor.SetTimestamp(Current.ColumnIndex,
943 ResultSet.GetTimestamp(I));
946 if ResultSet.WasNull then
947 RowAccessor.SetNull(Current.ColumnIndex);
949 { Supress any errors in default fields. }
958 FreeAndNil(SQLParams);
962 {BEGIN of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }
963 procedure TZGenericCachedResolver.UpdateAutoIncrementFields(
964 Sender: IZCachedResultSet; UpdateType: TZRowUpdateType; OldRowAccessor,
965 NewRowAccessor: TZRowAccessor; Resolver: IZCachedResolver);
967 //Should be implemented at Specific database Level Cached resolver
969 {END of PATCH [1185969]: Do tasks after posting updates. ie: Updating AutoInc fields in MySQL }