
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6122 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2723 lines
100 KiB
ObjectPascal
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.
|