lazarus-ccr/components/zmsql/source/zmquerydataset.pas

2723 lines
100 KiB
ObjectPascal

{*********************************************************}
{ }
{ ZMSQL }
{ SQL enhanced in-memory dataset }
{ }
{ Original developer: Zlatko Matić, 2009 }
{ e-mail: matalab@gmail.com }
{ Milkovićeva 6, Mala Ostrna, 10370 Dugo Selo, Croatia. }
{ }
{*********************************************************}
{
This file is copyright (c) 2011 by Zlatko Matić
This source code is distributed under
the Library GNU General Public License (see the file
COPYING) with the following modification:
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,
and to copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the terms
and conditions of the license of that module. An independent module is a module
which is not derived from or based on this library. If you modify this
library, you may extend this exception to your version of the library, but you are
not obligated to do so. If you do not wish to do so, delete this exception
statement from your version.
If you didn't receive a copy of the file COPYING, contact:
Free Software Foundation
675 Mass Ave
Cambridge, MA 02139
USA
Modifications are made by Zlatko Matić and contributors for purposes of zmsql package.
**********************************************************************}
{-----------------------------------------------------------------------------
The original developer of the code is Zlatko Matić
(matalab@gmail.com or matic.zlatko@gmail.com).
Contributor(s):
-Mario Ferrari (mario.ferrari@edis.it or mario@marioferrari.org)
Changes enclosed within {MF begin} and {MF end}
Last Modified: 07.03.2014
Known Issues:
- Extremely slow query execution when more than one table joined in query when there is
additional where clause in query. It can be overcomed with "ASSIGN TO variable" non-standard expression
-->first execute query on a table with where clasue, assign resultset to a variable and then use
the variable in second query (instead of the table).
- JanSQL has problems with typecasts.
- Parameters support is currently quite limited. Basically, named parameters must be used and they are replaced by its values as literal strings.
-->You must enclose parameter identifiers in SQL string by quotes!
History (Change log):
ZMSQL version 0.1.0, 13.07.2011: by Zlatko Matić
ZMSQL version 0.1.1, 26.07.2011: by Zlatko Matić
ZMSQL version 0.1.2, 28.07.2011: by Zlatko Matić
ZMSQL version 0.1.3, 02.08.2011: by Zlatko Matić
ZMSQL version 0.1.5, 12.08.2011: by Zlatko Matić
ZMSQL version 0.1.6, 28.12.2011: by Zlatko Matić
ZMSQL version 0.1.7, 01.01.2012: by Zlatko Matić
ZMSQL version 0.1.8, 08.01.2012: by Zlatko Matić
ZMSQL version 0.1.9, 15.01.2012: by Zlatko Matić
ZMSQL version 0.1.10, 20.01.2012: by Zlatko Matić
ZMSQL version 0.1.11, 05.02.2012: by Zlatko Matić
ZMSQL version 0.1.12, 12.02.2012: by Zlatko Matić
ZMSQL version 0.1.13, 13.01.2013: by Zlatko Matić
ZMSQL version 0.1.14, 01.01.2014: by Zlatko Matić
ZMSQL version 0.1.15, 28.01.2014: by Zlatko Matić
*Internal optimizations and bugfixes.
*Autoincrement fields (ftAutoInc) are now working.
*Improved visibility of TDataset methods and properties.
*ZMQueryDataset now works with TBufDataset as ancestor (as in CodeTyphon v.4.70). ZMBufDataset upgraded to the current TBufDataset in CodeTyphon v. 4.70.
*Added property MasterDetailFilter: Boolean which switches master/detail filtration on/off.
*Removed property DecimalSeparator. ZMSQL now use system settings for decimal and thousand separator.
*ZMQueryDataset can handle float value even if thousand separator is present (in a .csv file).
*Better handling locale settings and conversion from ANSI to UTF8.
*Persistent fields are working now.
(Solved by a trick: persistent fields loaded from .lfm are recreated, propertis from old fields are copied to new fields and old fields are deleted.
ZMSQL version 0.1.16, 28.01.2014: by Zlatko Matić
*Internal optimizations and bugfixes.
*Creation of JanSQL instances moved from ZMConnection to ZMQueryDataset, in order that ZMQueryDataset can be used in multithreaded applications.
*New properties (ReferentialUpdateFired, ReferentialDeleteFired, ReferentialInsertFired) that tells that a referential action is in progress.
ZMSQL version 0.1.17, 07.03.2014: by Mario Ferrari
*Error situations that used ShowMessage now raise a generic exception containing the message itself. Only one ShowMessage remains for a design-time case.
ZMSQL version 0.1.18, 10.04.2014: by Zlatko Matić
*Bugfix release. There was funny bug in zmquerydataset destroy method - dataset would be saved prior destroying if persistent save was enebaled.
This was wrong, causing saving CSV file copy in wrong directories.
ZMSQL version 0.1.19, 08.02.2015: by Zlatko Matić
*New component TZMQueryBuilder, based on Open QBuilder Engine, is added to the zmsql package.
TZMQueryBuilder uses TOQBEngineZmsql, which is TOQBEngine descendant.
TOQBEngineZmsql is in based on code of the Open QBuilder Engine for SQLDB Sources created by Reinier Olislagers, modified and adapted for the ZMSQL by Zlatko Matić.
It incorporates QBuilder visual query builder(Copyright (c) 1996-2003 Sergey Orlik , Copyright (c) 2003 Fast Reports, Inc.)
*Added procedure TZMConnection.GetTableNames(FileList: TStrings);
*Added procedure TZMQueryDataSet.LoadTableSchema;
-----------------------------------------------------------------------------}
unit ZMQueryDataSet;
{$mode objfpc}{$H+}
{$off DEFINE ZMBufDataset} //=== ct9999 for CodeTyphon ==============
{Use "$DEFINE ZMBufDataset" compiler directive to base TZMQueryDataset on TZMBufDataset
or use "$OFF DEFINE ZMBufDataset" compiler directive to base TZMQueryDataset on TBufDataset.
Optionally you can set {$DEFINE ZMBufDataset} in zmsql package under Options/Compiler Options/Other/Conditionals/Custom Options/Defines.
if you switch it on, TZMBufDataset is ancestor, if you swithc it off, TBufDataset is ancestor.}
interface
uses
{$IFDEF UNIX}{ clocale, cwstring,}{$ENDIF}
Classes, SysUtils, {LResources, Forms, Controls, Graphics, Dialogs,}
db, TypInfo, fpDBExport, fpcsvexport, fpstdexports, SdfData, StrUtils,
FileUtil, LConvEncoding, lazutf8,
{$IFDEF ZMBufDataset}
ZMBufDataset,
{$ELSE}
BufDataset,
{$ENDIF}
ZMConnection, jansql
{$ifdef VISUAL}
,ComponentEditors, PropEdits,
FormEditingIntf,
FieldsEditor{$endif};
type
TSourceData=(sdSdfDataset, sdJanSQL, sdOtherDataset, sdInternal);
TInspectFields=(ifCreateFieldsFromFieldDefs, ifCreateFieldDefsAndFields, ifDoNothing, ifNewIsEmpty, ifOther);
TFieldDelimiter = (fdSemicolon, fdTab, fdComma, fdBar, fdColon, fdDash, fdSlash, fdBackSlash);
// ; #9 , | : - / \
{ TZMQueryDataSet }
{$IFDEF ZMBufDataset}
TZMQueryDataSet = class(TZMBufDataSet)
{$ELSE}
TZMQueryDataSet = class(TBufDataSet)
{$ENDIF}
private
{ Private declarations }
FAutoIncIdx: integer; /// edgarrod71@gmail.com
FAutoIncValue: SizeInt; /// edgarrod71@gmail.com
FBulkInsert:Boolean;
FCSVExporterExport: TCSVExporter;
FDisableMasterDetailFiltration: Boolean;
FDoReferentialUpdate:Boolean;
FDynamicFieldsCreated: Boolean;
FFieldCount:Integer; //This is number of columns (fielddefs) that dataset will have after an action (after loading from a table, after loading from a dataset, after query execution....)
FFieldDelimiter: TFieldDelimiter;
FFieldsLoaded: boolean; //// edgarrod71@gmail.com
FJanSQLInstance:TjanSQL;
FMasterDataSetTo: TList;
FMasterDetailFiltration: Boolean;
FMasterFields: TStrings;
FMasterReferentialKeys: TList;
FMasterSource: TDataSource;
FMemoryDataSetOpened: Boolean;
FOldMasterSource:TDataSource;
FOldRecord:{$IFDEF ZMBufDataset} TZMBufDataSet {$ELSE} TBufDataSet {$ENDIF};
FOriginalSQL:String;
FOtherDatasetImport:TDataset;
FParameters: TParams;
FPersistentFieldsCreated: Boolean;
FPersistentSave: Boolean;
FPreparedSQL:String;
FQueryExecuted: Boolean;
FRecordCount:Longint;
FRecordsetIndex:Integer;
FReferentialDeleteFired: Boolean;
FReferentialInsertFired: Boolean;
FReferentialUpdateFired: Boolean;
FSdfDatasetImport:TSdfDataset;
FSlaveReferentialKeys: TList;
FSourceData:TSourceData;
FSQL: TStrings;
FTableFile: TFileStream;
FTableLoaded: Boolean;
FTableName: String;
FTableSaved: Boolean;
FZMConnection: TZMConnection;
procedure DoCopyFromDataset(pDataset:TDataset);
procedure DoCreatePersistentFieldsFromFieldDefs;
procedure DoLoadTableSchema;
procedure DoLoadFromTable;
procedure DoQueryExecute;
procedure ManageFields;
procedure SetConnection(const AValue: TZMConnection);
procedure SetDynamicFieldsCreated(AValue: Boolean);
procedure SetMasterDetailFiltration(AValue: Boolean);
procedure SetMemoryDataSetOpened(AValue: Boolean);
procedure SetDisableMasterDetailFiltration(const AValue: Boolean);
procedure SetMasterDataSetTo(const AValue: TList);
procedure SetMasterReferentialKeys(const AValue: TList);
procedure SetPersistentFieldsCreated(AValue: Boolean);
procedure SetSlaveReferentialKeys(const AValue: TList);
procedure SetZMConnection(const AValue: TZMConnection);
procedure SetMasterFields(const AValue: TStrings);
procedure SetMasterSource(const AValue: TDataSource);
procedure SetParameters(const AValue: TParams);
procedure SetPersistentSave(const AValue: Boolean);
procedure SetTableLoaded(const AValue: Boolean);
procedure SetTableName(const AValue: String);
procedure SetTableSaved(const AValue: Boolean);
procedure SetQueryExecuted(const AValue: Boolean);
procedure SetSQL(const AValue: TStrings);
procedure PassQueryResult;
procedure FieldsFromFieldDefs;
procedure FieldsFromScratch;
procedure EmptySdfDataSet;
procedure ClearSdfDataSet;
procedure InsertDataFromCSV;
procedure InsertDataFromJanSQL;
function InspectFields:TInspectFields;
procedure UpdateMasterDataSetTo;
procedure CopyARowFromDataset(pDataset: TDataSet);
procedure UpdateFOldRecord;
function FormatStringToFloat (pFloatString:string):Extended;
procedure SetFloatDisplayFormat;
procedure SetFloatPrecision;
Function ZMInitializePersistentField(AOwner: TComponent; AFieldDef:TFieldDef; AOldPersistentField:TField): TField;
protected
{ Protected declarations }
procedure DoFilterRecord({var} out Acceptable: Boolean);override;
procedure DoOnNewRecord; override;
procedure DoAfterScroll;override;
procedure DoBeforeDelete;override;
procedure DoBeforeInsert;override;
procedure DoBeforeEdit;override;
procedure DoBeforePost;override;
procedure DoAfterInsert;override;
procedure DoAfterPost;override;
procedure DoAfterDelete;override;
procedure InternalRefresh;override; { TODO : To investigate procedure InternalRefresh;override;! Currently this method is overriden to do nothing. }
procedure DoAfterClose;override;
procedure dummyProc;
public
{ Public declarations }
//Master/detail filtration
property MasterDataSetTo:TList read FMasterDataSetTo write SetMasterDataSetTo; // Defines datasets to which self is master in master/detail filtration.
property DisableMasterDetailFiltration:Boolean read FDisableMasterDetailFiltration write SetDisableMasterDetailFiltration; //Master/detail filrtation should be temporarily desabled during bulk inserts or updates...
//Properties needed for master/detail and referential integrity
property MasterReferentialKeys:TList read FMasterReferentialKeys write SetMasterReferentialKeys;//Defines list of referential keys in which self is master dataset.
property SlaveReferentialKeys:TList read FSlaveReferentialKeys write SetSlaveReferentialKeys; //Defines list of referential keys in which self is slave dataset.
//Other
procedure QueryExecute; //Executes SQL query defined in SQL property, on .csv files that are placed in folder defined in ZMConnection property. Resultset of select query is loaded into the the zmquerydataset (self).
procedure PrepareQuery; //Prepares parameterized queries for execution: replaces parameters with parameter values for parameterized queries.
procedure EmptyDataSet; //Deletes all records from dataset.
procedure ClearDataSet; //Deletes records, fields and fielddefs.
procedure CopyFromDataset (pDataset:TDataSet); //Copies schema and data from any TDataset.
function SortDataset (const pFieldName:String):Boolean; //Ascending/Descending sorting of memory dataset.
procedure LoadFromTable; //Loads data (or data and schema) from a .csv file (TableName.csv), set in property TableName, from path specified in ZMConnection property.
procedure LoadTableSchema;//Load schema only (without data) from a .csv file (TableName.csv), set in property TableName, from path specified in ZMConnection property.
function LoadTableFields: boolean; // edgarrod71@gmail.com Load fields from .csv file (TableName.csv), for single tables.
procedure LoadLastRecord;
procedure SaveToTable;overload; //Saves data and schema to a .csv file (TableName.csv), set in Tablename property, in path specified in ZMConnection property.
procedure SaveToTable(pDecimaSeparator:Char);overload; //Saves data and schema to a .csv file (TableName.csv), set in Tablename property, in path specified in ZMConnection property.
procedure CreateDynamicFieldsFromFieldDefs; // Creates fields from predefined fielddefs. To be used in design-time or run-time for memory dataset creation according to predefined fielddefs.
procedure CreatePersistentFieldsFromFieldDefs; // Creates PERSISTENT fields from predefined fielddefs. To be used in design-time only.
procedure MemoryDataSetOpen; //Executes CreateDynamicFieldsFromFieldDefs and set dataset to Active.
procedure MemOpen; /// MemoryDataSetOpen will be maintained for some time for compatibility, but is redundant to say ZMQueryDataSet.MemoryDataSetOpen, instead is shorter this definition... edgarrod71@gmail.com
procedure InitializePersistentFields; // Activates persistent fields loaded from .lfm.
procedure ResetAutoInc(pStart:SizeInt); //Resets AutoIncrement value to an integer.
function AddRecord(const Values: array of Const; pAutoIncPos: Integer=-1): boolean; //// edgarrod71@gmail.com Adds a Record at the end of the DataSet.
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property OldRecord:{$IFDEF ZMBufDataset} TZMBufDataSet {$ELSE} TBufDataSet {$ENDIF} read FOldRecord; //Last delete/insert/edit is preserved in this property.
property AutoIncValue: SizeInt read FAutoIncValue;
property ReferentialUpdateFired:Boolean read FReferentialUpdateFired; //Signalize that referential update is in progress
property ReferentialDeleteFired:Boolean read FReferentialDeleteFired; //Signalize that referential delete is in progress
property ReferentialInsertFired:Boolean read FReferentialInsertFired; //Signalize that referential insert is in progress
property FieldsLoaded: Boolean read FFieldsLoaded write FFieldsLoaded; //// edgarrod71@gmail.com
procedure Insert; virtual; //// edgarrod71@gmail.com
procedure Post; override; //// edgarrod71@gmail.com
published
{ Published declarations }
property ZMConnection:TZMConnection read FZMConnection write SetConnection; //Defines "database" folder path where .csv tables are placed. Instantiates JanSQL database engine.
property SQL:TStrings read FSQL write SetSQL; //Unprepared SQL query text.
property QueryExecuted:Boolean read FQueryExecuted write SetQueryExecuted; //"True" executes QueryExecute and loads resultset into dataset.
property TableName:String read FTableName write SetTableName; //Name of .csv file (without extension) from which is data loaded by LoadFromTable and to which is data and schema saved by SaveToTable.
property TableLoaded:Boolean read FTableLoaded write SetTableLoaded; //"True" executes LoadFromTable and loads resultset into dataset.
property TableSaved:Boolean read FTableSaved write SetTableSaved; //"True" executes SaveToTable and saves dataset to .csv file defined in TableName property, placed in folder specified by ZMConnection property.
property DynamicFieldsCreated:Boolean read FDynamicFieldsCreated write SetDynamicFieldsCreated; //"True" executes CreateDynamicFieldsFromFieldDefs, which creates fields from predefined fielddefs.
property PeristentFieldsCreated:Boolean read FPersistentFieldsCreated write SetPersistentFieldsCreated; //"True" executes CreatePersistentFieldsFromFieldDefs, which creates PERSISTENT fields from predefined fielddefs.
property MemoryDataSetOpened:Boolean read FMemoryDataSetOpened write SetMemoryDataSetOpened; //"True" executes CreateDynamicFieldsFromFieldDefs and activates dataset for editing.
property PersistentSave:Boolean read FPersistentSave write SetPersistentSave; //If "True", insert/delete/edit will immediately be written to underlying .csv file. If "False", then dataset is only in-memory.
property Parameters: TParams read FParameters write SetParameters; //Parameters for parameterized SQL text.
property FieldDelimiter: TFieldDelimiter read FFieldDelimiter write FFieldDelimiter default fdSemicolon;
//Read-only properties for getting info about referential integrity
property MasterRefKeysList:TList read FMasterReferentialKeys;//List of referential keys in which self is master dataset.
property SlaveRefKeysList:TList read FSlaveReferentialKeys; //List of referential keys in which self is slave dataset.
//Master/detail filtration
property MasterFields: TStrings read FMasterFields write SetMasterFields; //Fields in masterdatasource, (separated by ";") to be used for master/detail filtration.
property MasterSource: TDataSource read FMasterSource write SetMasterSource;//Master datasource for master/detail filtration.
property MasterDetailFiltration: Boolean read FMasterDetailFiltration write SetMasterDetailFiltration; //Switches master/detail filtration on/off.
property MasterDataSetToList:TList read FMasterDataSetTo; // List of datasets to which self is master in master/detail filtration.
//Inherited properties from TZMBufDataset
property State;
//property Fields;
Property FieldDefs;
property Filter;
property Filtered;
property FilterOptions;
property Active;
property AutoCalcFields;
property BeforeOpen;
property AfterOpen;
property BeforeClose;
property AfterClose;
property BeforeInsert;
property AfterInsert;
property BeforeEdit;
property AfterEdit;
property BeforePost;
property AfterPost;
property BeforeCancel;
property AfterCancel;
property BeforeDelete;
property AfterDelete;
property BeforeScroll;
property AfterScroll;
property BeforeRefresh;
property AfterRefresh;
property OnCalcFields;
property OnDeleteError;
property OnEditError;
property OnFilterRecord;
property OnNewRecord;
property OnPostError;
end;
implementation
uses
Variants,
ZMReferentialKey;
const
DELIMITERS: array[TFieldDelimiter] of char = (';', #9, ',', '|', ':', '-', '/', '\');
//fdSemicolon, fdTab, fdComma, fdBar, fdColon, fdDash, fdSlash, fdBackSlash);
{ TZMQueryDataSet }
procedure TZMQueryDataSet.SetZMConnection(const AValue: TZMConnection);
begin
if FZMConnection=AValue then exit;
FZMConnection := AValue;
end;
procedure TZMQueryDataSet.SetConnection(const AValue: TZMConnection);
begin
if FZMConnection=AValue then exit;
FZMConnection := AValue;
end;
procedure TZMQueryDataSet.DoQueryExecute;
var
vSqlResult:Integer;
vSqlText:String;
vDisableMasterDetailFiltration:Boolean;
begin
try
vDisableMasterDetailFiltration:=DisableMasterDetailFiltration;
//Set bulk insert flag and suppress master/detail filtration
FBulkInsert := True;
DisableMasterDetailFiltration:=True;
if not ZMConnection.Connected then
ZMConnection.Connect;
//Free existing and create new JanSQL database -->It seems that this is neccessary when changing connection.
{ TODO : Investigate why JanSQL sometimes fails to correctly execute consecutive queries in the same jansql instance. Values stay from previous query.
This is a serious bug in jansql.
As a temporary solution, jansql instance is recreated for every query. }
FJanSQLInstance.Free;
FJanSQLInstance:=TJanSQL.Create;
try
//Connect to JanSQL "database".
vSqlText:='connect to '''+ZMConnection.DatabasePath{Full}+'''';
{ShowMessage(vSqlText);}
vSqlResult:=FJanSQLInstance.SQLDirect(vSqlText);
if vSqlResult<>0 then
{ShowMessage('Successfully connected to database:'+ZMConnection.DatabasePath)}
else
{MF begin}
// was: ShowMessage('Connection to database: '+ ZMConnection.DatabasePath +' failed! Error: '+FJanSQLInstance.Error);
raise Exception.Create('Connection to database: '+ ZMConnection.DatabasePath +' failed! Error: '+FJanSQLInstance.Error);
{MF end}
{ShowMessage(IntToStr(vSqlResult));}
finally
FJanSQLInstance.ReleaseRecordset(vSqlResult);
end;
//Delete previous records
Close; //This closes dataset and delets all records from memory
{EmptyDataSet;} //This would be alternative to Close, but it has problems if DisableMasterDetailFiltration=True.
//Prepare SQL string
PrepareQuery;
//Execute query in JanSQL engine
try
FRecordsetIndex:=0;
{ShowMessage('Prepared SQL: '+FPreparedSQL);}
FRecordsetIndex:=FJanSQLInstance.SQLDirect(FPreparedSQL);
{ShowMessage(IntToStr(FRecordsetIndex));}
{ShowMessage('FJanSQLInstance.RecordsetCount: '+IntToStr(FJanSQLInstance.RecordsetCount));}
except
{MF begin}
// was: ShowMessage ('Error while trying to execute query.' +FJanSQLInstance.Error);
on e:Exception do begin
raise Exception.Create('Error while trying to execute query.' +FJanSQLInstance.Error);
end;
{MF end}
end;
//If there is a resultset, pass it to ZMQueryDataSet.
if FRecordsetIndex>0 then
begin
try
try
//Load query result into zmquerydataset
PassQueryResult;
finally
//Persistent save
if (FPersistentSave=True) then
begin
if (FTableName<>null) then SaveToTable
{MF begin}
// was: else ShowMessage('Dataset '+Name+' can not be saved ' +'because TableName property is not set');
else raise Exception.Create('Dataset '+Name+' can not be saved '#10 +'because TableName property is not set');
{MF end}
end;
end;
finally
FJanSQLInstance.ReleaseRecordset(FRecordsetIndex);
FRecordsetIndex:=0;
end;
end;
finally
//Remove bulk insert flag and enable master/detail filtration
FBulkInsert:=False;
DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
end;
end;
procedure TZMQueryDataSet.ManageFields;
begin
//Decide what to do with FieldDefs and Fields
{ShowMessage('InspectFields for dataset: '+self.Name);}
Case InspectFields of
ifCreateFieldsFromFieldDefs:
begin
{ShowMessage('InspectFields for dataset: '+self.Name+', '+InspectFields function returned ifCreateFieldsFromFieldDefs.');}
FieldsFromFieldDefs; //Create fields from fielddefs
//Deal with mutually exclusive properties
//// FDynamicFieldsCreated:=True;
//// FPersistentFieldsCreated:=False;
end;
ifCreateFieldDefsAndFields:
begin
{ShowMessage('InspectFields for dataset: '+self.Name+', '+'InspectFields function returned ifCreateFieldDefsAndFields.');}
FieldsFromScratch; //Create both fielddefs and fields
//Deal with mutually exclusive properties
FDynamicFieldsCreated:=True;
FPersistentFieldsCreated:=False;
end;
ifDoNothing:
begin
{ShowMessage('InspectFields for dataset: '+self.Name+', '+'InspectFields function returned ifDoNothing.');}
//Do nothing
end;
ifNewIsEmpty:
begin
{MF begin}
// was: ShowMessage('InspectFields for dataset: '+self.Name+', '+'Error: InspectFields function returned ifNewIsEmpty! Canceling...');
raise Exception.Create('InspectFields for dataset: '+self.Name+', '+'Error: InspectFields function returned ifNewIsEmpty! Canceling...');
{MF end}
end;
ifOther:
begin
{MF begin}
// was: ShowMessage('InspectFields for dataset: '+self.Name+', '+'Error: InspectFields function returned ifOther! Canceling...');
raise Exception.Create('InspectFields for dataset: '+self.Name+', '+'Error: InspectFields function returned ifOther! Canceling...');
{MF end}
end;
end;
end;
procedure TZMQueryDataSet.DoLoadTableSchema;
var
vDisableMasterDetailFiltration:Boolean;
begin
try
vDisableMasterDetailFiltration:=DisableMasterDetailFiltration;
//Set bulk inbsert flag and suppress master/detail filtration
FBulkInsert:=True;
DisableMasterDetailFiltration:=True;
if not ZMConnection.Connected then
ZMConnection.Connect;
//Delete previous records
Close; //This closes dataset and delets all records from memory
{EmptyDataSet;} //This would be alternative to Close, but it has problems if DisableMasterDetailFiltration=True.
// with FSdfDatasetImport do begin
FSdfDatasetImport.Close;
FSdfDatasetImport.FileName:=ZMConnection.DatabasePath{Full}+ TableName + '.csv';
FSdfDatasetImport.FirstLineAsSchema:=True;
FSdfDatasetImport.Delimiter := DELIMITERS[FFieldDelimiter];
FSdfDatasetImport.FileMustExist:=False;
FSdfDatasetImport.Open;
//Let object knows data source...
FSourceData:=sdSdfDataset;
FFieldCount:=FSdfDatasetImport.FieldDefs.Count;
FRecordCount:=FSdfDatasetImport.RecordCount;
// end;
//Prepare ZMQueryDataset
with self do begin
Close;
//Decide what to do with FieldDefs and Fields.
ManageFields;
end;
Open;
finally
//UnSet bulk inbsert flag and Unsuppress master/detail filtration
FBulkInsert:=False;
DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
end;
end;
procedure TZMQueryDataSet.DoLoadFromTable;
var
vDisableMasterDetailFiltration:Boolean;
begin
try
vDisableMasterDetailFiltration:=DisableMasterDetailFiltration;
//Set bulk insert flag and suppress master/detail filtration
FBulkInsert:=True;
DisableMasterDetailFiltration:=True;
if not ZMConnection.Connected then
ZMConnection.Connect;
//Delete previous records
Close; //This closes dataset and deletes all records from memory
{EmptyDataSet;} //This would be alternative to Close, but it has problems if DisableMasterDetailFiltration=True.
with FSdfDatasetImport do begin
Close;
FileName:=ZMConnection.DatabasePath{Full}+TableName+'.csv';
FirstLineAsSchema:=True;
FSdfDatasetImport.Delimiter:= DELIMITERS[FFieldDelimiter];
FSdfDatasetImport.FileMustExist:=False;
Open;
//Let object knows data source...
FSourceData:=sdSdfDataset;
FFieldCount:=FieldDefs.Count;
FRecordCount:=RecordCount;
end;
//Prepare ZMQueryDataset
with self do begin
Close;
//Decide what to do with FieldDefs and Fields.
ManageFields;
end;
Open;
try
//Insert data from the csv file.
InsertDataFromCSV;
finally
//Persistent save
if (FPersistentSave=True) then
begin
if (FTableName<>null) then SaveToTable
{MF begin}
// was: else ShowMessage('Dataset '+Name+' can not be saved because ' +'TableName property is not set');
else raise Exception.Create('Dataset '+Name+' can not be saved because ' +'TableName property is not set');
{MF end}
end;
end;
finally
//UnSet bulk inbsert flag and Unsuppress master/detail filtration
FBulkInsert:=False;
DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
end;
end;
procedure TZMQueryDataSet.DoCopyFromDataset(pDataset:TDataset);
var
n: Integer;
vFieldCount: Integer;
vFilter:String;
vFiltered:Boolean;
vDisableMasterDetailFiltration:Boolean;
begin
vFieldCount:=pDataSet.FieldDefs.Count;
if not ZMConnection.Connected then
ZMConnection.Connect;
//Delete previous records
Close; //This closes dataset and delets all records from memory
{EmptyDataSet;} //This would be alternative to Close, but it has problems if DisableMasterDetailFiltration=True.
//Let object knows data source...
FSourceData:=sdOtherDataset;
FOtherDatasetImport:=pDataset;
FFieldCount:=FOtherDatasetImport.FieldDefs.Count;
FRecordCount:=FOtherDatasetImport.RecordCount;
//Prepare ZMQueryDataset
with self do begin
Close;
//Decide what to do with FieldDefs and Fields
ManageFields;
end;
Open;
//Insert Fields Data.
try
//Remember whethere pDataSet was filtered.
vFilter:=pDataSet.Filter;
vFiltered:=pDataSet.Filtered;
if (pDataSet is TZMQueryDataSet) then vDisableMasterDetailFiltration:=(pDataSet as TZMQueryDataSet).DisableMasterDetailFiltration;
//Disable filter for the pDataSet
pDataSet.Filter:='';
pDataSet.Filtered:=False;
if (pDataSet is TZMQueryDataSet) then (pDataSet as TZMQueryDataSet).DisableMasterDetailFiltration:=True;
//iterate through pDataSet and copy values
pDataSet.First;
while not pDataSet.EOF do begin
Append;
for n:=0 to vFieldCount-1 do begin
if FieldDefs[n].DataType<>ftAutoInc then begin
Fields[n].Value:=pDataSet.Fields[n].Value;
end;
end;
Post;
pDataSet.Next;
end;
finally
//restore filter if existed
pDataSet.Filter:=vFilter;
pDataSet.Filtered:=vFiltered;
if (pDataSet is TZMQueryDataSet) then (pDataSet as TZMQueryDataSet).DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
end;
end;
procedure TZMQueryDataSet.DoCreatePersistentFieldsFromFieldDefs;
var
NewField: TField;
FieldDef: TFieldDef;
i: integer;
function FieldNameToPascalIdentifer(const AName: string): string;
var
i : integer;
begin
Result := '';
// FieldName is an ansistring
for i := 1 to Length(AName) do
if AName[i] in ['0'..'9','a'..'z','A'..'Z','_'] then
Result := Result + AName[i];
if (Length(Result) > 0) and (not (Result[1] in ['0'..'9'])) then
Exit;
if Assigned(FieldDef.FieldClass) then
begin
Result := FieldDef.FieldClass.ClassName + Result;
if Copy(Result, 1, 1) = 'T' then
Result := Copy(Result, 2, Length(Result) - 1);
end
else
Result := 'Field' + Result;
end;
function CreateFieldName(Owner: TComponent; const AName: string): string;
var
C: TComponent;
j:integer;
begin
for j := 0 to Owner.ComponentCount - 1 do
// for C in Owner do
begin
if CompareText(Owner.Components[j].Name, AName) = 0 then
// if CompareText(C.Name, AName) = 0 then
begin
{$ifdef VISUAL}
Result := FormEditingHook.CreateUniqueComponentName(NewField);
{$endif}
exit;
end;
end;
Result := AName;
end;
begin
//This procedure creates PERSISTENT Fields from predefined FieldDefs.
for I := 0 to Pred(fielddefs.Count) do
with FieldDefs.Items[I] do begin
FieldDef := Fielddefs.Items[I];
if DataType<>ftUnknown then begin
//Create new field and set it's unique name.
NewField:=CreateField(self.Owner); //Owner ---> this makes created field to be persistent and visible in object inspector.
{
NewField:=ZMCreateField(self.Owner,FieldDef); //Owner ---> this makes created field to be persistent and visible in object inspector.
}
NewField.Name := CreateFieldName(self.Owner, self.Name + FieldNameToPascalIdentifer(NewField.FieldName));
end;
//Set initial properties of the field.
NewField.FieldKind:=fkData;
NewField.SetFieldType(FieldDef.DataType);
NewField.Size:=FieldDef.Size;
{ TODO : Is there any possible way to set read-only property FieldNo??? }
{NewField.FieldNo:=FieldDef.FieldNo};
end;
end;
procedure TZMQueryDataSet.SetDynamicFieldsCreated(AValue: Boolean);
begin
if FDynamicFieldsCreated=AValue then exit;
if AValue then //// if AValue=True then edgarrod71@gmail.com
try
CreateDynamicFieldsFromFieldDefs;
{FDynamicFieldsCreated:=AValue;} //Removed to CreateDynamicFieldsFromFieldDefs procedure.
except
FDynamicFieldsCreated:=False;
Active:=False;
end
else //// if AValue=False then edgarrod71@gmail.com
try
{ TODO : To reconsider what action in case of SetDynamicFieldsCreated=False.
Currently set to do nothing.
Caution: if we clear dynamic fields, persistent fields will be deleted too. }
{
Active:=False;
Fields.Clear;
}
finally
FDynamicFieldsCreated:=AValue;
Active:=False;
//Deal with mutually exclusive properties.
FMemoryDataSetOpened:=False;
FTableLoaded:=False;
FQueryExecuted:=False;
end;
end;
procedure TZMQueryDataSet.SetMasterDetailFiltration(AValue: Boolean);
begin
if FMasterDetailFiltration=AValue then Exit;
FMasterDetailFiltration:=AValue;
FDisableMasterDetailFiltration := not AValue;
Filtered := AValue;
if Active then Refresh;
end;
procedure TZMQueryDataSet.SetMemoryDataSetOpened(AValue: Boolean);
begin
if FMemoryDataSetOpened=AValue then Exit;
if AValue then //// if (AValue=True) then edgarrod71@gmail.com
try
MemoryDataSetOpen;
{FMemoryDataSetOpened:=AValue;} //This is removed to MemoryDataSetOpen procedure.
except
FMemoryDataSetOpened:=False;
Active:=False;
end
else ///// if (AValue=False) then edgarrod71@gmail.com
try
Close; //This closes dataset and delets all records from memory
{EmptyDataSet;} //This would be alternative to Close, but it has problems if DisableMasterDetailFiltration=True.
finally
FMemoryDataSetOpened:=AValue;
Active:=False;
end;
end;
procedure TZMQueryDataSet.SetDisableMasterDetailFiltration(const AValue: Boolean);
begin
if FDisableMasterDetailFiltration=AValue then exit;
FDisableMasterDetailFiltration:=AValue; ///// edgarrod71@gmail.com
FMasterDetailFiltration := not AValue; //// FMasterDetailFiltration := FDisableMasterDetailFiltration = false
Filtered := not AValue; //// Filtered := FDisableMasterDetailFiltration = false
if Active then Refresh;
end;
procedure TZMQueryDataSet.SetMasterDataSetTo(const AValue: TList);
begin
if FMasterDataSetTo=AValue then exit;
FMasterDataSetTo:=AValue;
end;
procedure TZMQueryDataSet.SetMasterReferentialKeys(const AValue: TList);
begin
if FMasterReferentialKeys=AValue then exit;
FMasterReferentialKeys:=AValue;
end;
procedure TZMQueryDataSet.SetPersistentFieldsCreated(AValue: Boolean);
{ TODO : To solve problems with persistent fields }
begin
if FPersistentFieldsCreated=AValue then exit;
if AValue then //// if AValue=True then edgarrod71@gmail.com
try
//In design-time only, because, in run-time persistent fields should be streamed from .lfm?
if (csDesigning in ComponentState)
and not (csLoading in ComponentState)
and not (csReading in ComponentState)
then begin
raise Exception.Create('I am going to create persistent fields from fielddefs.');
//ShowMessage('I am going to create persistent fields from fielddefs.');
CreatePersistentFieldsFromFieldDefs;
end;
{FPersistentFieldsCreated:=AValue; //Removed to CreatePersistentFieldsFromFieldDefs procedure.}
{ TODO : Setting FPersistentFieldsCreated to True is temporarily disabled, because if PersistentFieldsCreated is True, then persistent fields will be loaded twice (once from stream and second time here) when project loading in design-time.... }
FPersistentFieldsCreated:=False; //POOR SOLUTION
except
FPersistentFieldsCreated:=False;
Active:=False;
end
else
/////if AValue=False then { TODO : To reconsider what to do on SetPersistentFieldsCreated=False. Currently set to do nothing. }
FPersistentFieldsCreated:=AValue;
end;
procedure TZMQueryDataSet.SetSlaveReferentialKeys(const AValue: TList);
begin
if FSlaveReferentialKeys=AValue then exit;
FSlaveReferentialKeys:=AValue;
end;
procedure TZMQueryDataSet.SetMasterFields(const AValue: TStrings);
begin
if FMasterFields=AValue then exit;
FMasterFields.Assign(AValue);
end;
procedure TZMQueryDataSet.SetMasterSource(const AValue: TDataSource);
begin
if FMasterSource=AValue then exit;
//Remember old master source
if Assigned (FMasterSource) then begin
FOldMasterSource:=FMasterSource;
end;
//Set new master data source
FMasterSource:=AValue;
UpdateMasterDataSetTo;
end;
procedure TZMQueryDataSet.UpdateMasterDataSetTo;
var
vAlreadyInList, vToAddNew,vToRemoveOld:Boolean;
begin
if Assigned (FMasterSource)
then vAlreadyInList:=(TObject(FMasterSource.DataSet) as TZMQueryDataSet).MasterDataSetTo.IndexOf(self)>=0
else vAlreadyInList:=False;
//Inspect how to update detail datasets list
if ((FOldMasterSource<>FMasterSource) and Assigned(FMasterSource))
then vToAddNew:=True else vToAddNew:=False;
if ((FOldMasterSource<>FMasterSource) and (vAlreadyInList=True) and Assigned(FOldMasterSource))
then vToRemoveOld:=True else vToRemoveOld:=False;
//Update detail datasets list
//Append dataset to the list of datasets for which the dataset is master dataset
if (vToAddNew=True) then begin
(TObject(FMasterSource.DataSet) as TZMQueryDataSet).MasterDataSetTo.Add(self);
end;
//Remove dataset from the list of datasets for which the dataset is master dataset
if (vToRemoveOld=True) then begin
(TObject(FOldMasterSource.DataSet) as TZMQueryDataSet).MasterDataSetTo.Remove(self);
end;
end;
procedure TZMQueryDataSet.SetParameters(const AValue: TParams);
begin
if FParameters=AValue then exit;
FParameters:=AValue;
end;
procedure TZMQueryDataSet.SetPersistentSave(const AValue: Boolean);
begin
if FPersistentSave=AValue then exit;
FPersistentSave:=AValue;
end;
procedure TZMQueryDataSet.SetTableLoaded(const AValue: Boolean);
begin
if FTableLoaded=AValue then exit;
if AValue then ////if AValue=True then edgarrod71@gmail.com
try
LoadFromTable;
{FTableLoaded:=AValue; } //This is removed inside LoadFromTable procedure.
except
FTableLoaded:=False;
Active:=False;
end
else ////if AValue=False then edgarrod71@gmail.com
try
Close; //This closes dataset and deletes all records from memory
{EmptyDataSet;} //This would be alternative to Close, but it has problems if DisableMasterDetailFiltration=True.
finally
FTableLoaded:=AValue;
Active:=False;
end;
end;
procedure TZMQueryDataSet.SetTableName(const AValue: String);
begin
if FTableName=AValue then exit;
FTableName:=AValue;
end;
procedure TZMQueryDataSet.SetTableSaved(const AValue: Boolean);
begin
if FTableSaved=AValue then exit;
if AValue then /////if AValue=True then edgarrod71@gmail.com
try
SaveToTable;
FTableSaved:=AValue;
finally
FTableSaved:=False;
end
else ////if AValue=False then
FTableSaved:=AValue;
end;
procedure TZMQueryDataSet.SetQueryExecuted(const AValue: Boolean);
begin
if FQueryExecuted=AValue then exit;
if AValue then /// if AValue=True then edgarrod71@gmail.com
try
if not ZMConnection.Connected then
ZMConnection.Connect; ////.Connected:=True; edgarrod71@gmail.com
QueryExecute;
{FQueryExecuted :=AValue;} //Moved to QueryExecute procedure.
except
FQueryExecuted :=False;
Active:=False;
end
else /// if AValue = false edgarrod71@gmail.com
try
EmptyDataSet; //Delete records from the dataset.
FJanSQLInstance.ReleaseRecordset(FRecordsetIndex);
finally
FQueryExecuted:=AValue;
Active:=False;
end;
end;
procedure TZMQueryDataSet.SetSQL(const AValue: TStrings);
begin
if FSQL=AValue then exit;
FSQL.Assign(AValue);
end;
procedure TZMQueryDataSet.PassQueryResult;
begin
FSourceData:=sdJanSQL;
FRecordCount:=FJanSQLInstance.RecordSets[FRecordsetIndex].recordcount;
FFieldCount:=FJanSQLInstance.RecordSets[FRecordsetIndex].fieldcount;
with self do begin
//Decide what to do with FieldDefs and Fields
ManageFields;
//OpenDataset
Open;
InsertDataFromJanSQL;
end;
end;
procedure TZMQueryDataSet.EmptyDataSet;
//This procedure deletes all records from dataset.
var
vFilter:String;
vFiltered:Boolean;
begin
with self do begin
//This is incredible slow in MemDataset, seems to be faster in TBufDataset!
try //// test edgarrod71@gmail.com I put Fields.Count inactive,
//// because if RecordCount>0 means that Fields were loaded!!!
if ({(Fields.Count>0) and }(RecordCount>0) and Active{=True)}) then begin
try
DisableControls; // edgarrod71@gmail.com
//Rememeber filter and disable it while deleting records.
vFilter:=Filter;
vFiltered:=Filtered;
Filter:='';
Filtered:=False;
//Delete records.
first;
repeat
Delete;
until EOF;
finally
//Reestablish filter if existed before deletion.
Filter:=vFilter;
Filtered:=vFiltered;
Refresh; //I'm not sure whether this is neccessary...
EnableControls;
end;
end;
except
{MF begin}
// was: ShowMessage('Error in EmptyDataset method, dataset: '+self.Name);
on e:Exception do begin
raise Exception.Create('Error in EmptyDataset method, dataset: '+self.Name);
end;
{MF end}
end;
end;
end;
procedure TZMQueryDataSet.ClearDataSet;
//This procedure deletes both fielddefs and fields, with all data...
begin
with self do begin
if Active{=True} then Close;
FieldDefs.Clear;
Fields.Clear;
end; {Why doesn't it delete all?}
end;
procedure TZMQueryDataSet.CopyFromDataset(pDataset: TDataSet);
//This procedure can copy any dataset data (and if neccessary schema too) to zmquerydataset
var
vDisableMasterDetailFiltration:Boolean;
vFilter:String;
vFiltered:Boolean;
begin
//First, see whether there are present persistent fields and whether they need initialization.
InitializePersistentFields;
with self do begin
try
DisableControls;
pDataSet.DisableControls;
//Remember filters
vDisableMasterDetailFiltration:=DisableMasterDetailFiltration;
vFilter:=Filter;
vFiltered:=Filtered;
//Set bulk inbsert flag and suppress master/detail filtration, remove filters
FBulkInsert:=True;
DisableMasterDetailFiltration:=True;
Filter:='';
Filtered:=False;
//Do copy from pDataSet
DoCopyFromDataset(pDataset);
finally
FBulkInsert:=False;
DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
Filter:=vFilter;
Filtered:=vFiltered;
EnableControls;
pDataset.EnableControls;
end;
end;
end;
procedure TZMQueryDataSet.CopyARowFromDataset(pDataset: TDataSet);
var
vFieldDef:TFieldDef;
vFieldCount:Integer;
i,n:Integer;
begin
vFieldCount:=pDataSet.FieldDefs.Count;
with self do begin
try
//Set bulk insert flag
FBulkInsert:=True;
ClearDataSet;
DisableControls;
//Create FieldDefs.
for i:=0 to vFieldCount-1 do begin
vFieldDef:=FieldDefs.AddFieldDef;
vFieldDef.Name:=pDataSet.FieldDefs[i].Name;
if pDataSet.FieldDefs[i].DataType=ftAutoInc then vFieldDef.DataType:=ftInteger
else vFieldDef.DataType:=pDataSet.FieldDefs[i].DataType;
vFieldDef.Size:=pDataSet.FieldDefs[i].Size;
vFieldDef.Required:=pDataSet.FieldDefs[i].Required;
end;
MaxIndexesCount:=(2*(FieldDefs.Count)+3);
CreateDataSet; //In case of TBufDataset ancestor.
Open;
//Insert current record from pDataSet.
Append;
for n:=0 to vFieldCount-1 do begin
Fields[n].Value:=pDataSet.Fields[n].Value;
end;
Post;
finally
//Remove bulk inbsert flag
FBulkInsert:=False;
EnableControls;
end;
end;
end;
procedure TZMQueryDataSet.UpdateFOldRecord;
begin
//For referential filtration
if Active and (FBulkInsert=False) then
TZMQueryDataSet(FOldRecord).CopyARowFromDataSet(self);
end;
function TZMQueryDataSet.FormatStringToFloat(pFloatString: string):Extended;
//Transform float value inside a string with adequate decimal separator.
var
// modified by edgarrod71@gmail.com
vFloatString, vLeftPart, vRightPart:String;
vDelimiterPos, I: SizeInt;
Dp, Tp, vDecSep: Char;
function StrToNumber(aStr:string): string;
var
P: Pchar;
begin
p := @aStr[1];
result := '';
repeat
if p^ in ['0'..'9','-'] then
result += p^;
inc(p);
until (p = #0) or (p = '');
end;
function GetDecSep(aStr: String): Char;
var
Pa, Pb: PChar;
begin
I := length(aStr);
Pa := @aStr[1];
Pb := @aStr[I];
if (Pa <= Pb) and not(Pb^ in [Dp, Tp]) then
repeat
dec(Pb)
until (Pa > Pb) or (Pb^ in [Dp, TP]);
vDelimiterPos := Pb - Pa + 1;
result := Pb^;
end;
begin
Result := 0.00;
if pFloatString <> '' then begin
Dp := SysUtils.DefaultFormatSettings.DecimalSeparator;
Tp := SysUtils.DefaultFormatSettings.ThousandSeparator;
vDecSep := GetDecSep(pFloatString);
if vDecSep <> Dp then
case vDecSep of
'.': vDecSep := ',';
',': vDecSep := '.';
end;
vLeftPart := StrToNumber(copy(pFloatString, 1, Pred(vDelimiterPos)));
vRightPart := copy(pFloatString, Succ(vDelimiterPos), I-Pred(vDelimiterPos));
vFloatString := vLeftPart + vDecSep + vRightPart;
result := StrToFloat(vFloatString);
end
end;
function TZMQueryDataSet.SortDataset(const pFieldName: String):Boolean;
var
i: Integer;
vIndexDefs: TIndexDefs;
vIndexName: String;
vIndexOptions: TIndexOptions;
vField: TField;
begin
Result := False;
vField := Fields.FindField(pFieldName);
//If invalid field name, exit.
if vField = nil then Exit;
//if invalid field type, exit.
if {(vField is TObjectField) or} (vField is TBlobField) or
{(vField is TAggregateField) or} (vField is TVariantField)
or (vField is TBinaryField) then Exit;
//Get IndexDefs and IndexName using RTTI
if IsPublishedProp(self, 'IndexDefs') then
vIndexDefs := GetObjectProp(self, 'IndexDefs') as TIndexDefs
else
Exit;
if IsPublishedProp(self, 'IndexName') then
vIndexName := GetStrProp(self, 'IndexName')
else
Exit;
//Ensure IndexDefs is up-to-date
IndexDefs.Update;
//If an ascending index is already in use,
//switch to a descending index
if vIndexName = pFieldName + '__IdxA'
then
begin
vIndexName := pFieldName + '__IdxD';
vIndexOptions := [ixDescending];
end
else
begin
vIndexName := pFieldName + '__IdxA';
vIndexOptions := [];
end;
//Look for existing index
for i := 0 to Pred(IndexDefs.Count) do
begin
if vIndexDefs[i].Name = vIndexName then
begin
Result := True;
Break
end; //if
end; // for
//If existing index not found, create one
if not Result then
begin
if vIndexName=pFieldName + '__IdxD' then
AddIndex(vIndexName, pFieldName, vIndexOptions, pFieldName)
else
AddIndex(vIndexName, pFieldName, vIndexOptions);
Result := True;
end; // if not
//Set the index
SetStrProp(self, 'IndexName', vIndexName);
end;
procedure TZMQueryDataSet.LoadFromTable;
begin
DisableControls;
//First, see whether there are present persistent fields and whether they need initialization.
InitializePersistentFields;
try
try
DoLoadFromTable;
//If everything goes well, ensure that corresponding property is set accordingly.
FTableLoaded:=True;
//Set mutually exclusive properties to False.
FQueryExecuted:=False;
FMemoryDataSetOpened:=False;
except
FTableLoaded:=False;
Active:=False;
end;
finally
//Refresh self
if Active then refresh;
FSdfDatasetImport.Close;
EnableControls;
end;
end;
procedure TZMQueryDataSet.LoadTableSchema;
begin
DisableControls;
//First, see whether there are present persistent fields and whether they need initialization.
InitializePersistentFields;
try
try
DoLoadTableSchema;
//If everything goes well, ensure that corresponding property is set accordingly.
FTableLoaded:=True;
//Set mutually exclusive properties to False.
FQueryExecuted:=False;
FMemoryDataSetOpened:=False;
except
FTableLoaded:=False;
Active:=False;
end;
finally
//Refresh self
if Active then refresh;
FSdfDatasetImport.Close;
EnableControls;
end;
end;
procedure TZMQueryDataSet.SaveToTable;overload;
var
vFiltered:Boolean;
vFilter:String;
vBookmark:TBookmark;
vDisableMasterDetailFiltration:Boolean;
begin
try
DisableControls;
vDisableMasterDetailFiltration:=DisableMasterDetailFiltration;
//Disable Master/Detail filtration
DisableMasterDetailFiltration:=True;
//Get bookmark
vBookmark:=GetBookmark;
//Get filter
if Filtered=True then vFiltered:=True else vFiltered:=False;
vFilter:=Filter;
//Temporary disable filters
Filtered:=False;
//Refresh in order to disable filters
DisableControls;
if active then Refresh;
if active then First;
EnableControls;
with FCSVExporterExport do begin
Dataset:=self;
FileName:=ZMConnection.DatabasePath{Full}+TableName+'.csv';
FromCurrent:=False;
FormatSettings.FieldDelimiter:= DELIMITERS[FFieldDelimiter];
FormatSettings.HeaderRow:=True;
// FormatSettings.QuoteStrings:=[qsAlways]; //=== ct9999 in FPC SVN 30449 NOT Exists ====
FormatSettings.BooleanFalse:='False';
FormatSettings.BooleanTrue:='True';
FormatSettings.DateFormat:='yyyy-mm-dd';
FormatSettings.DateTimeFormat:='yyyy-mm-dd hh:mm:ss';
//Set decimal separator.
FormatSettings.DecimalSeparator:=SysUtils.DefaultFormatSettings.DecimalSeparator;
Execute;
end;
//Restore filters.
Filter:=vFilter;
Filtered:=vFiltered;
//Goto bookmark
if ((BookmarkAvailable) and (BookmarkValid(vBookmark))) then
GotoBookmark(vBookmark);
finally
//Enable Master/Detail filtration
DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
//Refresh in order to enable filters
if Active then Refresh;
FreeBookmark(vBookmark);
EnableControls;
end;
end;
procedure TZMQueryDataSet.SaveToTable(pDecimaSeparator: Char);
var
vFiltered:Boolean;
vFilter:String;
vBookmark:TBookmark;
vDisableMasterDetailFiltration:Boolean;
begin
try
DisableControls;
vDisableMasterDetailFiltration:=DisableMasterDetailFiltration;
//Disable Master/Detail filtration
DisableMasterDetailFiltration:=True;
//Get bookmark
vBookmark:=GetBookmark;
//Get filter
if Filtered=True then vFiltered:=True else vFiltered:=False;
vFilter:=Filter;
//Temporary disable filters
if Active then Filtered:=False;
if Active then Refresh;
//Goto first record.
First;
with FCSVExporterExport do begin
Dataset:=self;
{FileName:=ZMConnection.DatabasePathFull+TableName+'.txt';}
FileName:=ZMConnection.DatabasePath{Full}+TableName+'.csv';
FromCurrent:=False;
FormatSettings.FieldDelimiter:= DELIMITERS[FFieldDelimiter];
FormatSettings.HeaderRow:=True;
// FormatSettings.QuoteStrings:=[qsAlways]; //=== ct9999 in FPC SVN 30449 NOT Exists ====
FormatSettings.BooleanFalse:='False';
FormatSettings.BooleanTrue:='True';
FormatSettings.DateFormat:='yyyy-mm-dd';
FormatSettings.DateTimeFormat:='yyyy-mm-dd hh:mm:ss';
FormatSettings.DecimalSeparator:=pDecimaSeparator;
Execute;
end;
//Restore filters.
Filter:=vFilter;
Filtered:=vFiltered;
//Goto bookmark
if ((BookmarkAvailable) and (BookmarkValid(vBookmark))) then
GotoBookmark(vBookmark);
finally
//Enable Master/Detail filtration
DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
if Active then Refresh;
FreeBookmark(vBookmark);
EnableControls;
end;
end;
procedure TZMQueryDataSet.CreateDynamicFieldsFromFieldDefs;
//This procedure is used to create Fields from FieldDefs, create the dataset and make it active.
begin
//Prepare ZMQueryDataset
with self do begin
FSourceData:=sdInternal;
FFieldCount:=FieldDefs.Count;
try
Close;
//Decide what to do with FieldDefs and Fields
ManageFields; //If matching persistent fields (match in FieldName and number) are already created, do nothing.
//If everything goes ok, set the property accordingly.
{FDynamicFieldsCreated:=True;}
except
FDynamicFieldsCreated:=False;
Active:=False;
end;
end;
end;
procedure TZMQueryDataSet.CreatePersistentFieldsFromFieldDefs;
//This procedure is used to create PERSISTENT Fields from FieldDefs.
{var strMsg:String;}
begin
with self do begin
FSourceData:=sdInternal;
FFieldCount:=FieldDefs.Count;
try
Close;
// Create PERSISTENT fields from FieldDefs
{ TODO : To investigate BindFields(False) and DefaultFields in ZMBufDataset and TBufDataset.
In Delphi, BindFields(False) disconnects fields object from underlying fields, but it seems that currently this does not work here?
Also, DefaultFields should be False in case of persistent fields and True in case of dynamic fields. However, it seems that sometimes it is False even if only dynamic fields are created.}
{
if InspectFields=ifDoNothing {This means that there are already created corresponding persistent fields.} then begin
ShowMessage('InspectFields=ifDoNothing');
if DefaultFields=False {DefaultFields=False means Persistent Fields exist} then begin
ShowMessage('DefaultFields=False');
Exit;
end;
end;
}
Fields.Clear;
//DefaultFields should be False in case of persistent fields?
SetDefaultFields(False);
DoCreatePersistentFieldsFromFieldDefs;
BindFields(True); //Connect persistent Fields objects to underlying Fields.
//If everything goes ok, set the property accordingly.
FPersistentFieldsCreated:=True;
//Deal mutually exclusive property
FDynamicFieldsCreated:=False;
except
{MF begin}
// was: ShowMessage('I can not create persistent fields!');
// was: FPersistentFieldsCreated:=False;
// was: Active:=False;
on e:Exception do begin
FPersistentFieldsCreated:=False;
Active:=False;
raise Exception.Create('I can not create persistent fields!');
end;
{MF end}
end;
end;
end;
procedure TZMQueryDataSet.MemOpen;
//This procedure creates dataset fields (if not created) and opens the dataset for insert/edit.
//To be used for activation of memory datasets that will not be filled by sql query,
//nor be loaded from stored tables.
begin
//First, see whether there are present persistent fields and whether they need initialization.
InitializePersistentFields;
FSourceData:=sdInternal;
FFieldCount:=FieldDefs.Count;
try
//First, deal with creating dataset anew...
ManageFields;
//Then, open the dataset
Active:=True;
//If everything goes OK, then set the property accordingly.
FMemoryDataSetOpened:=True;
//Set mutually exclusive properties to false.
FQueryExecuted:=False;
FTableLoaded:=False;
except
FMemoryDataSetOpened:=False;
Active:=False;
end;
end;
procedure TZMQueryDataSet.MemoryDataSetOpen;
begin
MemOpen;
end;
procedure TZMQueryDataSet.FieldsFromFieldDefs;
var
vFieldDefsCount: Integer;
//Here we create dynamic fields from predefined fielddefs.
begin
// with self do begin
close;
Fields.Clear;
vFieldDefsCount := FieldDefs.Count;
if (vFieldDefsCount > 0) then begin ////edgarrod71@gmail.com supposed Fields.Count is 0!!!
//Set MaxIndexes count if not manually set
if (MaxIndexesCount = Null) or (MaxIndexesCount < (2 * vFieldDefsCount + 3)) then
MaxIndexesCount:= 2 * vFieldDefsCount + 3;
//Set precision for float fields
SetFloatPrecision;
CreateDataset; //Creates Fields from FieldDefs /// Aquí es donde se bloquea... edgarrod71@gmail.com
end;
//Set display format for float fields
SetFloatDisplayFormat;
//Set property DynamicFieldsCreated to True
FDynamicFieldsCreated:=True;
FPersistentFieldsCreated:=False;
end;
procedure TZMQueryDataSet.FieldsFromScratch;
//Here we create both fielddefs and fields.
var
vFieldDef:TFieldDef;
vCurrentFieldSize, vMaxFieldSize, i, n:Integer;
begin
with self do begin
if Active{=True} then Close;
//Clears both fielddefs and fields....
Fields.Clear;
FieldDefs.Clear;
//Create new FieldDefs.
for n:=0 to FFieldCount-1 do begin
vFieldDef:=FieldDefs.AddFieldDef;
case FSourceData of
sdJanSQL:vFieldDef.Name:=FJanSQLInstance.recordsets[FRecordsetIndex].FieldNames[n];
sdSdfDataset:vFieldDef.Name:=FSdfDatasetImport.FieldDefs[n].Name;
sdOtherDataset:vFieldDef.Name:=FOtherDatasetImport.FieldDefs[n].Name;
end;
//Determine FieldDef properties
case FSourceData of
sdJanSQL:
begin
vFieldDef.DataType:=ftString;//TODO: In procedure FieldsFromScratch add other fielddefs DataType recognition, besides ftString.
vFieldDef.Required:=False;
vFieldDef.Precision:=0;
vFieldDef.Attributes:=[];
end;
sdSdfDataset:
begin
vFieldDef.DataType:=FSdfDatasetImport.FieldDefs[n].DataType;
vFieldDef.Required:=False;
vFieldDef.Precision:=0;
vFieldDef.Attributes:=[];
end;
sdOtherDataset:
begin
vFieldDef.DataType:=FOtherDatasetImport.FieldDefs[n].DataType;
vFieldDef.Required:=FOtherDatasetImport.FieldDefs[n].Required;
vFieldDef.Precision:=FOtherDatasetImport.FieldDefs[n].Precision;
vFieldDef.Attributes:=FOtherDatasetImport.FieldDefs[n].Attributes;
end;
end;
//Determine FieldDef.Size property!
vMaxFieldSize:=0;
vCurrentFieldSize:=0;
case FSourceData of
sdSdfDataset: vFieldDef.Size:=FSdfDatasetImport.Fields[n].Size;
sdJanSQL:
begin
for i:=0 to FRecordCount-1 do begin
vCurrentFieldSize:=Length(FJanSQLInstance.RecordSets[FRecordsetIndex].records[i].fields[n].value);
if vCurrentFieldSize>vMaxFieldSize then vMaxFieldSize:=vCurrentFieldSize;
end;
if vMaxFieldSize>0 then vFieldDef.Size:=vMaxFieldSize else vFieldDef.Size:=255;
end;
sdOtherDataset: vFieldDef.Size:=FOtherDatasetImport.Fields[n].Size;
end;
//Set MaxIndexes count
if ((MaxIndexesCount=Null)
or (MaxIndexesCount<(2*(FieldDefs.Count)+3)))
then MaxIndexesCount:=(2*(FieldDefs.Count)+3);
end;
//Set precision for float fields
SetFloatPrecision;
CreateDataSet;//Creates Fields from FieldDefs
//Set display format for float fields
SetFloatDisplayFormat;
//Set property DynamicFieldsCreated to True
FDynamicFieldsCreated:=True;
FPersistentFieldsCreated:=False;
end;
end;
procedure TZMQueryDataSet.EmptySdfDataSet;
begin
with FSdfDatasetImport do begin
if Active=False then Open;
while not EOF do begin
Delete;
end;
end;
end;
procedure TZMQueryDataSet.ClearSdfDataSet;
begin
with FSdfDatasetImport do begin
if Active=True then Close;
FieldDefs.Clear;
Fields.Clear;
end;
end;
procedure TZMQueryDataSet.dummyProc;
begin
end;
function makeMethod(data, code: Pointer): TMethod;
begin
result.data := data;
result.code := code;
end;
procedure TZMQueryDataSet.InsertDataFromCSV;
{type
TProc = procedure {(DataSet: TDataSet)} of object;
const
NullMethod: TMethod = (Code: nil; Data: nil);}
var
i:SizeInt;
vFieldString:string;
{ DS: TBufDataSet;
P: ^TRTLMethod;
tmpProc: TRTLMethod;//TProc; //TDataSetNotifyEvent;}
begin
if not Active then Open;
if not FSdfDatasetImport.Active then FSdfDatasetImport.Open;
DisableControls; //// Seem not to be working because the program enters in AfterScroll event!!!!
{ tmpProc := TDataSetnotifyEvent(@DoAfterScroll); //// edgarrod71@gmail.com WORKAROUND!!!!
}
{ tmpProc := @DoAfterScroll;
tmpProc := TRTLMethod(@DoAfterScroll);
SetMethodProp(Self, 'OnAfterScroll', NullMethod);
P^ := @DoAfterScroll;
tmpProc := TProc(@DoAfterScroll);
tmpProc := TProc(MakeMethod(Self, P));}
// DoAfterScroll := TDataSetNotifyEvent(MakeMethod(Self, @tmpProc));
{ TZMQueryDataSet(Self).DoAfterScroll := nil;
TMethod(DoAfterScroll).data := Pointer(Self);}
// DoAfterScroll := TDataSetNotifyEvent(@dummyProc);
FSdfDatasetImport.First;
FAutoIncValue := 0;
while not FSdfDatasetImport.EOF do begin
Append;
for i := 0 to Pred(FFieldCount) do begin
if FieldDefs[i].DataType <> ftAutoInc then begin
vFieldString := FSdfDatasetImport.Fields[i].AsString;
try
case Fields[i].DataType of //Fields of Float type require special transformation.
ftFloat:
Fields[i].Value := FormatStringToFloat(vFieldString); //Format value with appropriate decimal separator.
ftInteger: begin
if (vFieldString[1] in ['1'..'9','-']) then
Fields[i].Value := StrToInt(vFieldString)
else
Fields[i].AsString := FSdfDatasetImport.Fields[i].AsString;
end;
else begin //Other field types.
//Convert string to UTF8
{vFieldString:=AnsiToUTF8(vFieldString);}
vFieldString:=ConvertEncoding(vFieldString, GuessEncoding(vFieldString),EncodingUTF8);
Fields[i].Value:=vFieldString;
end;
end; {case}
except
Fields[i].Value:=FSdfDatasetImport.Fields[i].Value;
end;
end
else { BECAUSE IT IS FTAUTOINC } begin /// edgarrod71@gmail.com
if FSdfDatasetImport.Fields[i].AsInteger > FAutoIncValue then
FAutoIncValue := FSdfDatasetImport.Fields[i].AsInteger;
Fields[i].AsInteger := FSdfDatasetImport.Fields[i].AsInteger;
end; {try}
end;
Post;
FSdfDatasetImport.Next;
end;
//DoAfterScroll := TDataSetNotifyEvent(makeMethod(nil, @tmpProc));
// tmpProc := nil;
if Active then begin
Refresh;
First;
end;
EnableControls;
end;
procedure TZMQueryDataSet.InsertDataFromJanSQL;
var
i,n:integer;
vFieldString:string;
begin
if Active=False then Open;
with self do begin
FAutoIncValue := 0;
try
for i:=0 to Pred(FRecordCount) do begin
Append;
//Iterate columns
for n:=0 to pred(FFieldCount) do begin
if FieldDefs[n].DataType<>ftAutoInc then begin
vFieldString:=FJanSQLInstance.RecordSets[FRecordsetIndex].records[i].fields[n].value;
//Convert string to UTF8
{vFieldString:=AnsiToUTF8(vFieldString);}
vFieldString:=ConvertEncoding(vFieldString, GuessEncoding(vFieldString),EncodingUTF8);
//Float fields need special transformation
case Fields[n].DataType of /// edgarrod71@gmail.com
ftFloat: begin
try
//Format value with appropriate decimal separator.
Fields[n].Value:=FormatStringToFloat(vFieldString);
except
Fields[n].AsString:=vFieldString;
end;
end;
ftInteger: begin
if (vFieldString[1] in ['1'..'9','-']) then /// must start with a valid number
Fields[i].Value := StrToInt(vFieldString)
else
Fields[n].AsString:=vFieldString;
end;
//Other types of fields.
else Fields[n].Value := vFieldString;
end; {case}
end
else { BECAUSE IT IS FTAUTOINC } begin /// edgarrod71@gmail.com
if FJanSQLInstance.RecordSets[FRecordsetIndex].records[i].fields[n].value > FAutoIncValue then
FAutoIncValue := FJanSQLInstance.RecordSets[FRecordsetIndex].records[i].fields[n].value;
Fields[i].AsInteger := FJanSQLInstance.RecordSets[FRecordsetIndex].records[i].Fields[n].value;
end;
end;
Post;
end;
except
raise exception.Create('error en I='+IntToStr(i));
end;
if Active then begin
Refresh;
First;
end;
end;
end;
function TZMQueryDataSet.InspectFields:TInspectFields;
//This function compares old and new dataset and detects whether fielddefs and fields should be created or not.
//TInspectFields=(ifCreateFieldsFromFieldDefs, ifCreateFieldDefsAndFields, ifDoNothing, ifNewIsEmpty, ifOther);
var
vNewFieldNames, vOldFieldNames:String;
vNewFieldDefNames, vOldFieldDefNames:String;
i, vFieldDefsCount:Integer;
vFieldCountMatch:Boolean;
vFieldDefNamesMatch:Boolean;
vNewIsEmpty:Boolean;
vFieldType: TFieldType;
vDelim: Char;
begin
//Set default values
Result:=ifOther;
/// vFieldCountMatch:=False; edgarrod71@gmail.com
vFieldDefNamesMatch:=false;
vNewIsEmpty:=False;
vOldFieldNames:='';
vOldFieldDefNames:='';
vNewFieldNames:='';
vNewFieldDefNames:='';
vFieldDefsCount := FieldDefs.Count;
vDelim := DELIMITERS[FFieldDelimiter];
//Iterate through Old Dataset (assumption: There cannot be fields without fielddefs).
//FieldDefs
for i:=0 to pred(vFieldDefsCount) do begin
vOldFieldDefNames += FieldDefs[i].Name + vDelim;
vFieldType := FieldDefs[i].DataType;
if (vFieldType = ftAutoInc) and (FAutoIncIdx = -1) then
FAutoIncIdx := I; { Question (edgarrod71@gmail.com)::: how many AutoIncrement fields must syncronize with this? I suppose it must be only one! }
//Fields
if (Fields.Count>0) and (Fields.Count >= (i + 1)) then
vOldFieldNames += Fields[i].FieldName + vDelim;
end;
// end;
//Iterate through New Dataset
if boolean(FFieldCount) then begin //// FFieldCount>0
if FSourceData = sdInternal then
vNewFieldDefNames := vOldFieldDefNames //// avoids to enter in the next loop! edgarrod71@gmail.com
else
for i:=0 to pred(FFieldCount) do begin
case FSourceData of
sdJanSQL:vNewFieldDefNames += FJanSQLInstance.recordsets[FRecordsetIndex].FieldNames[i]+ vDelim;
sdSdfDataset:vNewFieldDefNames += FSdfDatasetImport.FieldDefs[i].Name+ vDelim;
sdOtherDataset:vNewFieldDefNames += FOtherDatasetImport.FieldDefs[i].Name+ vDelim;
//// sdInternal: vNewFieldDefNames += FieldDefs[i].Name+';'; //// Old Code, review it to eliminate
end;
end;
vNewFieldNames := vNewFieldDefNames; /// edgarrod71@gmail.com took it out from the loop...
end;
//Inspect whether number of columns is same in old and new dataset
vFieldCountMatch := FieldDefs.Count = FFieldCount; /// edgarrod71@gmail.com then vFieldCountMatch:=True;
//Inspect whether new dataset is empty (with no columns)
vNewIsEmpty := vNewFieldDefNames = ''; /// edgarrod71@gmail.com then vNewIsEmpty:=True;
//Inspect whether fielddef names match
vFieldDefNamesMatch := vNewFieldDefNames = vOldFieldDefNames; /// edgarrod71@gmail.com then vFieldDefNamesMatch:=True else vFieldDefNamesMatch:=False;
if vNewIsEmpty then /// Modified by edgarrod71@gmail.com
Result := ifNewIsEmpty
else {if vNewIsEmpty=false} begin
if vFieldCountMatch and vFieldDefNamesMatch then
if vOldFieldNames = vNewFieldNames then
Result := ifDoNothing
else
Result := ifCreateFieldsFromfieldDefs
else { and (vFieldCountMatch=False) or (vFieldDefNamesMatch=False) then }
Result := ifCreateFieldDefsAndFields;
end;
end;
procedure TZMQueryDataSet.DoFilterRecord({var} out Acceptable: Boolean);
var
i, vCount:Integer;
namDetail, namMaster: String;
DetailField, MasterField: TField;
begin
//inherited behavior
inherited DoFilterRecord(Acceptable);
//New behavior
if not Acceptable then exit;
//Filter detail dataset if all conditions are met.
if (not FBulkInsert)
and (not DisableMasterDetailFiltration)
and (Assigned(MasterFields))
and (Assigned(MasterSource))
and (FMasterDetailFiltration)
and (Active)
and (MasterSource.DataSet.Active) then
begin
vCount:=0;
Filtered:=True; //Ensure dataset is filtered
for i:=0 to MasterFields.Count-1 do begin
//try
namDetail := MasterFields.Names[i];
if namDetail <> '' then begin
// if Name=Value (Detail field=Master field) pair is provided...
namMaster := MasterFields.ValueFromIndex[i];
end else begin
// if single name is provided for both detail and master field
namMaster := FMasterFields[i];
namDetail := namMaster;
end;
DetailField := FieldByName(namDetail);
MasterField := MasterSource.Dataset.FieldByName(namMaster);
if VarSameValue(Detailfield.Value, Masterfield.Value) then
inc(vCount);
end;
Acceptable := (vCount=MasterFields.Count);
//Refresh slave datasets
if not ControlsDisabled then //// edgarrod71@gmail.com
DoAfterScroll;
end;
end;
procedure TZMQueryDataSet.DoOnNewRecord;
begin
inherited DoOnNewRecord;
{New behavior}
{ TODO : This is only temporary solution until bug(s) regarding ftAutoInc in TBufDataset is solved.
The bug is: when new dataset is created and opened, autoincrement fields are working correctly. But, if dataset is closed and reopened, autoincrement fields are not working anymore.
See bug report: http://bugs.freepascal.org/view.php?id=25628
Also, as currently implemented in TBufDataset, ftAutoInc can't be used for referential integrity in zmquerydataset.}
{ BUG CORRECTED!!!! edgarrod71@gmail.com } /// no estoy seguro si la segunda condición vale...
if (FAutoIncIdx > -1) {and (Fields[FAutoIncIdx].DataType = ftAutoInc)} then
Fields[FAutoIncIdx].AsInteger := Succ(FAutoIncValue);
end;
procedure TZMQueryDataSet.DoAfterScroll;
var
i:Integer;
begin
inherited DoAfterScroll;
{New behavior}
//For master/detail filtration
if Assigned (FMasterDatasetTo) then begin
for i:=0 to FMasterDatasetTo.Count-1 do begin
if ((TZMQueryDataSet(FMasterDatasetTo.Items[i]).Active) and (Active)
and (TZMQueryDataSet(FMasterDatasetTo.Items[i]).Fields.Count>0)
and (Fields.Count>0)
{and (TZMQueryDataSet(FMasterDatasetTo.Items[i]).FieldDefs.Count=TZMQueryDataSet(FMasterDatasetTo.Items[i]).Fields.Count)} ///// WHY THIS CHECK HERE?
{and (FieldDefs.Count=Fields.Count)} //// why to ask? supposed the dataset is open because it has FIELDS!!!
and (TZMQueryDataSet(FMasterDatasetTo.Items[i]).RecordCount>0)
and (RecordCount>0)
and (DisableMasterDetailFiltration=False)
and (FBulkInsert=False))
then begin
//Detail datasets must be refreshed in order master/detail filtration take effect.
if TZMQueryDataSet(FMasterDatasetTo.Items[i]).Active then TZMQueryDataSet(FMasterDatasetTo.Items[i]).Refresh;
if TZMQueryDataSet(FMasterDatasetTo.Items[i]).Active then TZMQueryDataSet(FMasterDatasetTo.Items[i]).First;
end;
end;
end;
end;
procedure TZMQueryDataSet.DoBeforeEdit;
begin
inherited DoBeforeEdit;
{New behavior}
//Save OldRecord
if FBulkInsert=False then UpdateFOldRecord;
end;
procedure TZMQueryDataSet.DoBeforeInsert;
var
I: integer;
begin
inherited DoBeforeInsert;
{New behavior}
//Save OldRecord
if FBulkInsert=False then UpdateFOldRecord;
end;
procedure TZMQueryDataSet.DoBeforeDelete;
var
SlaveDataSet:TZMQueryDataSet;
ReferentialKey:TZMReferentialKey;
ReferentialKind:TZMReferentialKind;
i:Integer;
vFilter:String;
vFiltered:Boolean;
vDoReferentialDelete:Boolean;
vSlaveBookmark:TBookmark;
vDisableMasterDetailFiltration:Boolean;
function InspectReferentialDeleteCondition: Boolean;
var
vDoReferentialDelete: Boolean;
vCount: Integer;
n: Integer;
begin
//Inspect whether referential conditions are met
vCount:=0;
vDoReferentialDelete:=False;
for n:=0 to ReferentialKey.JoinedFields.Count-1 do begin
try
//If MasterField=SlaveField pair is provided in JoinedFields item.
if SlaveDataSet.FieldByName(ReferentialKey.JoinedFields.Names[n]).AsString
=FOldRecord.FieldByName(ReferentialKey.JoinedFields.ValueFromIndex[n]).AsString
then Inc(vCount);
except
//If MasterField=SlaveField pair is not provided in JoinedFields item.
if SlaveDataSet.FieldByName(ReferentialKey.JoinedFields.Names[n]).AsString
=FOldRecord.FieldByName(ReferentialKey.JoinedFields[n]).AsString
then Inc(vCount);
end;
if vCount=ReferentialKey.JoinedFields.Count then vDoReferentialDelete:=True;
end;
Result:=vDoReferentialDelete;
end;
begin
inherited DoBeforeDelete;
{New behavior}
//Save OldRecord
if FBulkInsert=False then UpdateFOldRecord;
//Referential Delete
if Assigned(FMasterReferentialKeys) then begin
for i:=0 to FMasterReferentialKeys.Count-1 do begin
ReferentialKey:=TObject(FMasterReferentialKeys[i]) as TZMReferentialKey;
SlaveDataSet:=ReferentialKey.SlaveDataSet;
ReferentialKind:=ReferentialKey.ReferentialKind;
if ((SlaveDataSet.Active) and (Active)
and (ReferentialKey.Enabled=True)
and (SlaveDataSet.Fields.Count>0) and (SlaveDataSet.FieldDefs.Count=SlaveDataSet.Fields.Count)
and (FieldDefs.Count>0) and (FieldDefs.Count=Fields.Count)
and Assigned(ReferentialKey.JoinedFields)
and (rkDelete in ReferentialKind)
and (FBulkInsert=False)) then begin
try
//Signalize referential delete
FReferentialDeleteFired:=True;
SlaveDataSet.DisableControls;
vSlaveBookmark:=SlaveDataSet.GetBookmark;
//Enforce referential delete. self=MasterDataset
try
//Delete records in SlaveDataset
begin
DisableControls;
try
vFilter:=SlaveDataSet.Filter;
vFiltered:=SlaveDataSet.Filtered;
vDisableMasterDetailFiltration:=SlaveDataSet.DisableMasterDetailFiltration;
//Disable DoFilterRecord
SlaveDataSet.DisableMasterDetailFiltration:=True;
SlaveDataSet.Filtered:=False;
//Iterate through records in SlaveDataSet and update every record.
if SlaveDataSet.Active then SlaveDataSet.Refresh;
if Slavedataset.Active then SlaveDataSet.First;
while not SlaveDataSet.EOF do begin
vDoReferentialDelete:=InspectReferentialDeleteCondition;
//Do referential delete
if vDoReferentialDelete=True then
begin
SlaveDataSet.Delete;
end
else SlaveDataSet.Next;
end;
{ TODO : To investigate why this test to bookmark validity gives wrong result and crashes the application...}
{
if ((SlaveDataSet.BookmarkAvailable) and (SlaveDataSet.BookmarkValid(vSlaveBookmark))) then begin
SlaveDataSet.GotoBookmark(vSlaveBookmark);
end;
}
finally
//Enable DoFilterRecord
SlaveDataSet.DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
SlaveDataSet.Filter:=vFilter;
SlaveDataSet.Filtered:=vFiltered;
end;
EnableControls;
end;
finally
if SlaveDataSet.Active then SlaveDataSet.Refresh;
end;
finally
FReferentialDeleteFired:=False;
SlaveDataSet.FreeBookmark(vSlaveBookmark);
SlaveDataSet.EnableControls;
end;
end;
end;
end;
end;
procedure TZMQueryDataSet.DoBeforePost;
var
MasterDataSet:TZMQueryDataSet;
ReferentialKey:TZMReferentialKey;
i:Integer;
begin
inherited DoBeforePost;
{ if FAutoIncIdx > -1 then
FAutoIncValue := Fields[FAutoIncIdx].AsInteger;}
if (FAutoIncIdx > -1) and (state = dsInsert) then
FAutoIncValue := succ(FAutoIncValue);
{New behavior}
//Ensure that masterdatasets are not in edit state
if (Assigned(FSlaveReferentialKeys))
then begin
for i:=0 to FSlaveReferentialKeys.Count-1 do begin
ReferentialKey:=TObject(FSlaveReferentialKeys[i]) as TZMReferentialKey;
MasterDataSet:=ReferentialKey.MasterDataSet;
if (
(MasterDataSet.State=dsEdit)
and (MasterDataSet.Active) and (Active)
and (FBulkInsert=False)
and (ReferentialKey.Enabled=True)
and Assigned(ReferentialKey.JoinedFields)
)
then begin
MasterDataSet.Post;
end;
end;
end;
FDoReferentialUpdate := State = dsEdit;
{ if State=dsEdit then FDoReferentialUpdate:=True
else FDoReferentialUpdate:=False;}
end;
procedure TZMQueryDataSet.DoAfterInsert;
var
MasterDataSet:TZMQueryDataSet;
ReferentialKey:TZMReferentialKey;
ReferentialKind:TZMReferentialKind;
i,n:Integer;
begin
inherited DoAfterInsert;
//Referential Insert - self as SlaveDataset
if Assigned(FSlaveReferentialKeys) then begin
for i:=0 to FSlaveReferentialKeys.Count-1 do begin
ReferentialKey:=TObject(FSlaveReferentialKeys[i]) as TZMReferentialKey;
MasterDataSet:=ReferentialKey.MasterDataSet;
ReferentialKind:=ReferentialKey.ReferentialKind;
if ((MasterDataSet.Active) and (Active)
and (MasterDataSet.FieldDefs.Count>0) and (MasterDataSet.FieldDefs.Count=MasterDataSet.Fields.Count)
and (Fields.Count>0) and (FieldDefs.Count=Fields.Count)
and (ReferentialKey.Enabled=True)
and Assigned(ReferentialKey.JoinedFields)
and (rkInsert in ReferentialKind)
and (FBulkInsert=False))
then begin
try
//Signalize referential insert
FReferentialInsertFired:=True;
//Enforce referential insert for self as SlaveDataSet
DisableControls;
for n:=0 to ReferentialKey.JoinedFields.Count-1 do begin
try
//If MasterField=SlaveField pair is provided in JoinedFields item.
FieldByName(ReferentialKey.JoinedFields.Names[n]).Value:=MasterDataSet.FieldByName(ReferentialKey.JoinedFields.ValueFromIndex[n]).Value;
except
//If MasterField=SlaveField pair is not provided in JoinedFields item.
FieldByName(ReferentialKey.JoinedFields[n]).Value:=MasterDataSet.FieldByName(ReferentialKey.JoinedFields[n]).Value;
end;
end;
finally
FReferentialInsertFired:=False;
EnableControls;
end;
end;
end;
end;
//Refresh slave datasets
if not boolean(ControlsDisabled) then //// edgarrod71@gmail.com even in DisabledControls put it called Scroll!!!
DoAfterScroll;
end;
procedure TZMQueryDataSet.DoAfterPost;
var
{MasterDataSet:TZMQueryDataSet;}
SlaveDataSet:TZMQueryDataSet;
ReferentialKey:TZMReferentialKey;
ReferentialKind:TZMReferentialKind;
i,n:Integer;
vFilter:String;
vFiltered:Boolean;
vDisableMasterDetailFiltration:Boolean;
vDoReferentialUpdate:Boolean;
vSlaveBookmark:TBookmark;
function InspectReferentialUpdateCondition: Boolean;
var
vDoReferentialUpdate: Boolean;
vCount: Integer;
j:Integer;
begin
//Inspect whether referential conditions are met
vCount:=0;
vDoReferentialUpdate:=False;
for j:=0 to ReferentialKey.JoinedFields.Count-1 do begin
try
//If MasterField=SlaveField pair is provided in JoinedFields item.
if SlaveDataSet.FieldByName(ReferentialKey.JoinedFields.Names[j]).AsString
=FOldRecord.FieldByName(ReferentialKey.JoinedFields.ValueFromIndex[j]).AsString
then Inc(vCount);
except
//If MasterField=SlaveField pair is not provided in JoinedFields item.
if SlaveDataSet.FieldByName(ReferentialKey.JoinedFields.Names[j]).AsString
=FOldRecord.FieldByName(ReferentialKey.JoinedFields[j]).AsString
then Inc(vCount);
end;
if vCount=ReferentialKey.JoinedFields.Count then vDoReferentialUpdate:=True;
end;
Result:=vDoReferentialUpdate;
end;
begin
inherited DoAfterPost;
{New behavior}
//Persistent save
if (FPersistentSave{=True}) and (FBulkInsert=False) then
if (FTableName<>null) then SaveToTable
{MF begin}
// was: else ShowMessage('Dataset can not be saved because TableName property is not set');
else raise Exception.Create('Dataset can not be saved because TableName property is not set');
{MF end}
if FDoReferentialUpdate=False then exit;
//Referential Update; self as Master Dataset
if Assigned(FMasterReferentialKeys) then begin
for i:=0 to FMasterReferentialKeys.Count-1 do begin
ReferentialKey:=TObject(FMasterReferentialKeys[i]) as TZMReferentialKey;
SlaveDataSet:=ReferentialKey.SlaveDataSet;
ReferentialKind:=ReferentialKey.ReferentialKind;
if ((SlaveDataSet.Active) and (Active)
and (SlaveDataSet.FieldDefs.Count>0) and (SlaveDataSet.FieldDefs.Count=SlaveDataSet.Fields.Count)
and (Fields.Count>0) and (FieldDefs.Count=Fields.Count)
and (ReferentialKey.Enabled=True)
and Assigned(ReferentialKey.JoinedFields)
and (rkUpdate in ReferentialKind)
and (FBulkInsert=False))
then begin
try
//Signalize referential update
FReferentialUpdateFired:=True;
//Update records in SlaveDataset
SlaveDataSet.DisableControls;
vSlaveBookmark:=SlaveDataSet.GetBookmark;
begin
try
vFilter:=SlaveDataSet.Filter;
vFiltered:=SlaveDataSet.Filtered;
vDisableMasterDetailFiltration:=SlaveDataSet.DisableMasterDetailFiltration;
//Disable DoFilterRecord
SlaveDataSet.DisableMasterDetailFiltration:=True;
SlaveDataSet.Filtered:=False;
//Iterate through records in SlaveDataSet and update every record.
if SlaveDataSet.Active then SlaveDataSet.Refresh;
if SlaveDataSet.Active then SlaveDataSet.First;
while not SlaveDataSet.EOF do begin
vDoReferentialUpdate:=InspectReferentialUpdateCondition;
//Do referential update
if vDoReferentialUpdate=True then begin
SlaveDataSet.Edit;
//Enforce referential update for SlaveDataSet
for n:=0 to ReferentialKey.JoinedFields.Count-1 do begin
try
//If MasterField=SlaveField pair is provided in JoinedFields item.
SlaveDataSet.FieldByName(ReferentialKey.JoinedFields.Names[n]).Value
:=FieldByName(ReferentialKey.JoinedFields.ValueFromIndex[n]).Value;
except
//If MasterField=SlaveField pair is not provided in JoinedFields item.
SlaveDataSet.FieldByName(ReferentialKey.JoinedFields[n]).Value
:=FieldByName(ReferentialKey.JoinedFields[n]).Value;
end;
end;
SlaveDataSet.Post;
end;
SlaveDataSet.Next;
end;
try
if ((SlaveDataSet.BookmarkAvailable) and (SlaveDataSet.BookmarkValid(vSlaveBookmark))) then
SlaveDataSet.GotoBookmark(vSlaveBookmark);
except
end;
finally
//Enable DoFilterRecord
SlaveDataSet.DisableMasterDetailFiltration:=vDisableMasterDetailFiltration;
SlaveDataSet.Filter:=vFilter;
SlaveDataSet.Filtered:=vFiltered;
end;
end;
finally
FReferentialUpdateFired:=False;
if SlaveDataSet.Active then SlaveDataSet.Refresh;
SlaveDataSet.FreeBookmark(vSlaveBookmark);
SlaveDataSet.EnableControls;
end;
end;
end;
end;
//Refresh slave datasets
if not ControlsDisabled then
DoAfterScroll;
end;
procedure TZMQueryDataSet.DoAfterDelete;
begin
inherited DoAfterDelete;
{New behavior}
//Persistent save
if ((FPersistentSave{=True}) and (FBulkInsert=False)) then
begin
if (FTableName<>null) then SaveToTable
{MF begin}
// was: else ShowMessage('Dataset can not be saved because TableName property is not set');
else raise Exception.Create('Dataset can not be saved because TableName property is not set');
{MF end}
end;
//Refresh slave datasets
if not ControlsDisabled then //// edgarrod71@gmail.com
DoAfterScroll;
end;
procedure TZMQueryDataSet.InternalRefresh;
begin
//Do nothing. TBufDataSet's InternalRefresh does troubles.
//It seems that what in TDataSet's Refresh method is implemented is quite enough for ZMQueryDataset.
{
inherited InternalRefresh;
}
end;
procedure TZMQueryDataSet.DoAfterClose;
begin
inherited DoAfterClose;
//Deal with mutually exclusive properties
FTableLoaded := False;
FQueryExecuted := False;
FMemoryDataSetOpened := False;
//Reset autoincrement counter
FAutoIncValue:=0;
end;
procedure TZMQueryDataSet.QueryExecute;
begin
try
DisableControls;
//First, see whether there are present persistent fields and whether they need initialization.
InitializePersistentFields;
try
DoQueryExecute;
//If everything goes OK, then set the property accordingly.
FQueryExecuted := True;
//Set mutually exclusive properties to false.
FTableLoaded := False;
FMemoryDataSetOpened := False;
except
FQueryExecuted := False;
Active := False;
end;
finally
//Refresh self
if Active then refresh;
EnableControls
end;
end;
procedure TZMQueryDataSet.PrepareQuery;
{This is temporary simple solution of passing parameters to query SQL string before execution}
var
i:Integer;
begin
FOriginalSQL:='';
FPreparedSQL:='';
for i:=0 to FSQL.Count-1 do begin
FOriginalSQL:=FOriginalSQL+' '+FSQL.Strings[i];
end;
FPreparedSQL:=FOriginalSQL;
if (Assigned(Parameters) and (Parameters.Count>0)) then begin
for i:=0 to Parameters.Count-1 do begin
//Apply parameters by name
FPreparedSQL:=AnsiReplaceText(FPreparedSQL,':'+Parameters[i].Name,Parameters[i].Value);//There must be better way...
end;
end;
{ShowMessage('Prepared query:'+FPreparedSQL);}
end;
constructor TZMQueryDataSet.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{
//JanSQL instance
FJanSQLInstance:=TJanSQL.Create;
}
FAutoIncValue := 0; /// edgarrod71@gmail.com
FAutoIncIdx := -1; /// edgarrod71@gmail.com -1 means no AutoIncIdx; >0 means there is and position of it
FTableFile := nil;
FFieldsLoaded := false;
//SQL
FSQL := TStringList.Create;
//Master/detail filtration
FMasterFields:=TStringList.Create;
FMasterDataSetTo:=TList.Create;
//Referential integrity
FMasterReferentialKeys:=TList.Create;
FSlaveReferentialKeys:=TList.Create;
//Import/export
FSdfDatasetImport:=TSdfDataset.Create(nil);
FCSVExporterExport:=TCSVExporter.Create(nil);
//Parameters
FParameters:=TParams.Create;
//FOldRecord
FOldRecord:={$IFDEF ZMBufDataset} TZMBufDataSet{$ELSE}TBufDataSet{$ENDIF}.Create(nil);
end;
destructor TZMQueryDataSet.Destroy;
begin
if Assigned(FTableFile) then
FreeAndNil(FTableFile);
//Import/Export
if FSdfDatasetImport.Active{=True} then FSdfDatasetImport.Close;
FreeAndNil(FSdfDatasetImport);
FreeAndNil(FCSVExporterExport);
//FOldRecord
FreeAndNil(FOldRecord);
//Parameters
FreeAndNil(FParameters);
//SQL
FreeAndNil(FSQL);
//Master/detail
FreeAndNil(FMasterFields);
FreeAndNil(FMasterDataSetTo);
//Referential integrity
FreeAndNil(FMasterReferentialKeys);
FreeAndNil(FSlaveReferentialKeys);
//JanSQL
if FRecordsetIndex>0 then FJanSQLInstance.ReleaseRecordset(FRecordsetIndex);
if Assigned(FJanSQLInstance) then FreeAndNil(FJanSQLInstance);
//inherited
inherited Destroy;
end;
procedure TZMQueryDataSet.SetFloatDisplayFormat;
var
vFD: TCollectionItem;
begin
//If FloatDisplayFormat property is set, then take it...
if (Assigned(FZMConnection) and (FZMConnection.FloatDisplayFormat<>'')
and (FZMConnection.FloatDisplayFormat<>Null)) then begin
//Set display format for Float type
for vFD in FieldDefs do //// edgarrod71@gmail.com
if ((TFieldDef(vFD).DataType=ftFloat)
and ((Fields[vFD.Index] as TFloatField).DisplayFormat='')) //Manually set property value has precendance than property set in ZMConnection.
then begin
(Fields[vFD.Index] as TFloatField).DisplayFormat
:= FZMConnection.FloatDisplayFormat;
end;
end;
end;
procedure TZMQueryDataSet.SetFloatPrecision;
var
vFD: TCollectionItem;
begin
//If FloatPrecision property is set, then take it...
if (Assigned(FZMConnection) and (FZMConnection.FloatPrecision<>0)
and (FZMConnection.FloatPrecision <> Null)) then
//Set precision for Float type
for vFD in FieldDefs do /// edgarrod71@gmail.com
if (TFieldDef(vFD).DataType=ftFloat)
and (TFieldDef(vFD).Precision = 0) then //Manually set property value has precendance than property set in ZMConnection.
TFieldDef(vFD).Precision := FZMConnection.FloatPrecision;
end;
function TZMQueryDataSet.ZMInitializePersistentField(AOwner: TComponent; AFieldDef: TFieldDef; AOldPersistentField:TField): TField;
Var
TheNewPeristentField : TFieldClass;
vName:String;
begin
{TheNewPeristentField:=GetFieldClass(AFieldDef.DataType);
if TheNewPeristentField=Nil then
DatabaseErrorFmt(SUnknownFieldType,[FName]);
Result:=TheNewPeristentField.Create(AOwner); }
TheNewPeristentField:=GetFieldClass(AFieldDef.DataType);
{TheNewPeristentField:=AOldPersistentField.ClassType;}
Result:=TheNewPeristentField.Create(AOwner);
Try
//Copy all properties from old persistent field.
Result.Size:={AFieldDef.Size;}AOldPersistentField.Size;
Result.Required:={AFieldDef.Required;}AOldPersistentField.Required;
Result.FieldName:={AFieldDef.Name;}AOldPersistentField.Name;
Result.DisplayLabel:={AFieldDef.DisplayName;}AOldPersistentField.DisplayName;
Result.{SetFieldType(AFieldDef.DataType);}SetFieldType(AOldPersistentField.DataType);
Result.ReadOnly:= {(faReadOnly in AFieldDef.Attributes);}AOldPersistentField.ReadOnly;
//Other properties
Result.Required:=AOldPersistentField.Required;
{Result.DisplayName:=AOldPersistentField.DisplayName;}
Result.Alignment:=AOldPersistentField.Alignment;
Result.AttributeSet:=AOldPersistentField.AttributeSet;
Result.Calculated:=AOldPersistentField.Calculated;
Result.ConstraintErrorMessage:=AOldPersistentField.ConstraintErrorMessage;
Result.CustomConstraint:=AOldPersistentField.CustomConstraint;
{Result.DataSet:=AOldPersistentField.DataSet;}
Result.DefaultExpression:=AOldPersistentField.DefaultExpression;
Result.DisplayWidth:=AOldPersistentField.DisplayWidth;
Result.EditMask:=AOldPersistentField.EditMask;
Result.FieldKind:=AOldPersistentField.FieldKind;
Result.ImportedConstraint:=AOldPersistentField.ImportedConstraint;
Result.Index:=AOldPersistentField.Index;
Result.KeyFields:=AOldPersistentField.KeyFields;
Result.Lookup:=AOldPersistentField.Lookup;
Result.LookupCache:=AOldPersistentField.LookupCache;
Result.LookupDataSet:=AOldPersistentField.LookupDataSet;
Result.LookupKeyFields:=AOldPersistentField.LookupKeyFields;
Result.LookupResultField:=AOldPersistentField.LookupResultField;
{Result.IsBlob:=AOldPersistentField.IsBlob;}
Result.OnChange:=AOldPersistentField.OnChange;
Result.OnGetText:=AOldPersistentField.OnGetText;
Result.OnSetText:=AOldPersistentField.OnSetText;
Result.OnValidate:=AOldPersistentField.OnValidate;
Result.Origin:=AOldPersistentField.Origin;
Result.ProviderFlags:=AOldPersistentField.ProviderFlags;
{Result.Text:=AOldPersistentField.Text;}
Result.ValidChars:=AOldPersistentField.ValidChars;
Result.Tag:=AOldPersistentField.Tag;
Result.Visible:=AOldPersistentField.Visible;
Result.DesignInfo:=AOldPersistentField.DesignInfo;
{
Result.Dataset:=self;
}
If (Result is TFloatField) then
TFloatField(Result).Precision:={AFieldDef.Precision;}TFloatField(AOldPersistentField).Precision;
if (Result is TBCDField) then
TBCDField(Result).Precision:={AFieldDef.Precision;}TBCDField(AOldPersistentField).Precision;
if (Result is TFmtBCDField) then
TFmtBCDField(Result).Precision:={AFieldDef.Precision;}TFmtBCDField(AOldPersistentField).Precision;
//Set Name of the new persistent fields and delete old persistent field.
vName:=AOldPersistentField.Name;
FreeAndNil(AOldPersistentField);
Result.Name:=vName;
except
FreeAndNil(Result);
Raise;
end;
end;
procedure TZMQueryDataSet.InitializePersistentFields;
var
i:Integer;
vPersistentFields:Boolean;
vFieldNoPresent:Boolean;
vFieldsCount, vFieldDefsCount: Integer;
begin
//Initialize persistent fields
//First detetect whether persistent fields are loaded from .lfm
vPersistentFields:=False;
vFieldNoPresent:=True;
vFieldsCount := Fields.Count; //// remember to reassign to the
vFieldDefsCount := FieldDefs.Count; //// next IF
if boolean(vFieldDefsCount) and (vFieldsCount = vFieldDefsCount) then begin // if ((Fields.Count=FieldDefs.Count) and (FieldDefs.Count>0)) then begin
vPersistentFields:=True;
for i:=0 to pred(vFieldDefsCount) do begin
if FieldDefs[i].Name <> Fields[i].FieldName then vPersistentFields:=False;
if Fields[i].FieldNo=0 then vFieldNoPresent:=False;
end;
end;
/////// if ((vPersistentFields{=True}) and (vFieldNoPresent=False)) then vPersistentFieldsNeedInitialization:=True;
//If there are persistent fields and need recreation, then recreate them.
////// if (vPersistentFieldsNeedInitialization{=True}) then begin
if vPersistentFields{=True} and (vFieldNoPresent=False) then begin
SetDefaultFields(False);
for i := 0 to pred(vFieldDefsCount) do
ZMInitializePersistentField(self.Owner, FieldDefs[i], self.FindField(FieldDefs[i].Name));
BindFields(True);
end;
end;
procedure TZMQueryDataSet.ResetAutoInc(pStart: SizeInt);
begin
FAutoIncValue:=pStart;
end;
function TZMQueryDataSet.LoadTableFields: boolean; //// edgarrod71@gmail.com
var
S, fName, fField: string;
P: PChar;
procedure FillFieldDefs;
var
vFieldDef: TFieldDef;
vDelim: Char;
begin
if FieldDefs.Count > 0 then
FieldDefs.Clear;
vDelim := DELIMITERS[FFieldDelimiter];
P := @S[1];
if P <> '' then
repeat /// This gets the Fields from File and updates FieldDefs...
fField := '';
repeat
fField += P^;
inc(P);
until P^ in [#0, #10, #13, vDelim];
if P^ = vDelim then inc(P);
vFieldDef := FieldDefs.AddFieldDef;
vFieldDef.Name := fField;
vFieldDef.DataType:=ftString;
vFieldDef.Size := 1024;
vFieldDef.Required:=False;
vFieldDef.Precision:=0;
vFieldDef.Attributes:=[];
until (P^ in [#0, #10, #13]){ or (P = '')};
end;
begin
Result := false;
if FileName <> '' then
fName := FileName
else
if TableName <> '' then
fName := TableName + '.csv';
if fName <> '' then begin
if Assigned(FTableFile) then
FreeAndNil(FTableFile);
try
FTableFile := TFileStream.Create(fName, fmOpenReadWrite);
SetLength(S, FTableFile.Size);
FTableFile.Read(S[1], FTableFile.Size);
FillFieldDefs;
CreateDynamicFieldsFromFieldDefs; /// this ManageFields
finally
if Active then //// it must be closed, but not sure if here we can close it!!!
Close;
Result := true;
FFieldsLoaded := true;
end;
end;
end;
procedure TZMQueryDataSet.LoadLastRecord; //// edgarrod71@gmail.com
var
I: integer;
P: PChar;
begin
if not FFieldsLoaded then
LoadTableFields;
I := FTableFile.Seek(0, soFromEnd); /// se supone que FTableFile está abierto
/// result no va aquí, se supone que se cargue en un registro...
// Result := ''; AQUI VOY!!!
// P := @S[I];
repeat
// Result := (P-1)^ + Result; //// for this, file must have a #10 at the end of file
dec(P);
until ((P-1)^ in [#10, #13]);
end;
function TZMQueryDataSet.AddRecord(const Values: array of const;
pAutoIncPos: Integer): boolean; //// edgarrod71@gmail.com
var
i: integer;
value: string;
begin
Result := false;
if not FFieldsLoaded and Active then
try
AppendRecord(Values);
finally
result := true;
end
else begin
if not FFieldsLoaded then
LoadTableFields;
if (length(Values) = FieldDefs.Count) then begin { Inserta el último registro }
// TO-DO: if pAutoIncPos > -1 then check the position, the LastRecord and AutoInc-it!
// T := GetLastRecord; /// I must do something with this...
/// Hay que cargar el último registro!!! no recuerdo para qué
value := '';
for i := 0 to High(Values) do begin
case Values[i].vType of
vtWideString: value := WideString(Values[i].VWideString);
vtAnsiString: value := AnsiString(Values[i].vAnsiString);
vtExtended: value := floatToStr(Values[i].vExtended^);
vtInteger: value := IntToStr(Values[i].vInteger);
vtPChar: value := Values[i].vPChar^;
vtString: value := Values[i].vString^;
end;
if i <> High(Values) then
value += DELIMITERS[FFieldDelimiter]
else
value += #10;
Result := boolean(FTableFile.Write(PChar(value)^, length(value)));
end;
end
else
raise Exception.Create('Fields Count Differ from Table' + FTableFile.FileName + '!');
end;
end;
procedure TZMQueryDataSet.Insert; //// edgarrod71@gmail.com
begin
case State of
dsBrowse: inherited Insert;
dsEdit, dsInsert: Post;
dsInactive: begin
if FFieldsLoaded then
MemOpen;
inherited Insert;
end;
end;
end;
procedure TZMQueryDataSet.Post; //// edgarrod71@gmail.com
var
I, FC: integer;
AOC: array of TVarRec;
VR: ^TVarRec;
begin
if FFieldsLoaded then
try
FC := Fields.Count;
SetLength(AOC, FC);
VR := @AOC[0];
for I := 0 to FC - 1 do begin
VR[I].vType := vtWideString;
case VR[I].vType of /// edgarrod71@gmail.com Possible items (just in case)
vtWideString: WideString(VR[I].VWideString) := Fields[i].AsWideString;
vtInteger: VR[I].VInteger := Fields[I].AsInteger;
// vtAnsiString: AnsiString(VR[I].VAnsiString) := Fields[i].AsAnsiString;
vtString: String(VR[I].VString) := Fields[I].AsString;
end;
end;
AddRecord(AOC);
finally
Close;
end
else
if Active then
inherited post;
end;
initialization
RegisterClasses ( [{ ftUnknown} Tfield,
{ ftString} TStringField,
{ ftSmallint} TSmallIntField,
{ ftInteger} TLongintField,
{ ftWord} TWordField,
{ ftBoolean} TBooleanField,
{ ftFloat} TFloatField,
{ ftCurrency} TCurrencyField,
{ ftBCD} TBCDField,
{ ftDate} TDateField,
{ ftTime} TTimeField,
{ ftDateTime} TDateTimeField,
{ ftBytes} TBytesField,
{ ftVarBytes} TVarBytesField,
{ ftAutoInc} TAutoIncField,
{ ftBlob} TBlobField,
{ ftMemo} TMemoField,
{ ftGraphic} TGraphicField,
{ ftFmtMemo} TBlobField,
{ ftParadoxOle} TBlobField,
{ ftDBaseOle} TBlobField,
{ ftTypedBinary} TBlobField,
{ ftFixedChar} TStringField,
{ ftWideString} TWideStringField,
{ ftLargeint} TLargeIntField,
{ ftOraBlob} TBlobField,
{ ftOraClob} TMemoField,
{ ftVariant} TVariantField,
{ ftGuid} TGuidField,
{ ftFMTBcd} TFMTBCDField,
{ ftFixedWideString} TWideStringField,
{ ftWideMemo} TWideMemoField ]);
end.