123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 1999-2013 by Joost van der Sluis and other members of the
- Free Pascal development team
- BufDataset implementation
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- unit BufDataset;
- {$mode objfpc}
- {$h+}
- interface
- uses Classes,Sysutils,db,bufdataset_parser;
- type
- TCustomBufDataset = Class;
- TResolverErrorEvent = procedure(Sender: TObject; DataSet: TCustomBufDataset; E: EUpdateError;
- UpdateKind: TUpdateKind; var Response: TResolverResponse) of object;
- { TBlobBuffer }
- PBlobBuffer = ^TBlobBuffer;
- TBlobBuffer = record
- FieldNo : integer;
- OrgBufID: integer;
- Buffer : pointer;
- Size : PtrInt;
- end;
- PBufBlobField = ^TBufBlobField;
- TBufBlobField = record
- ConnBlobBuffer : array[0..11] of byte; // DB specific data is stored here
- BlobBuffer : PBlobBuffer;
- end;
- { TBufBlobStream }
- TBufBlobStream = class(TStream)
- private
- FField : TBlobField;
- FDataSet : TCustomBufDataset;
- FBlobBuffer : PBlobBuffer;
- FPosition : PtrInt;
- FModified : boolean;
- protected
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- public
- constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
- destructor Destroy; override;
- end;
- { TCustomBufDataset }
- PBufRecLinkItem = ^TBufRecLinkItem;
- TBufRecLinkItem = record
- prior : PBufRecLinkItem;
- next : PBufRecLinkItem;
- end;
- PBufBookmark = ^TBufBookmark;
- TBufBookmark = record
- BookmarkData : PBufRecLinkItem;
- BookmarkInt : integer; // was used by TArrayBufIndex
- BookmarkFlag : TBookmarkFlag;
- end;
- TRecUpdateBuffer = record
- UpdateKind : TUpdateKind;
- { BookMarkData:
- - Is -1 if the update has canceled out. For example: an appended record has been deleted again
- - If UpdateKind is ukInsert, it contains a bookmark to the newly created record
- - If UpdateKind is ukModify, it contains a bookmark to the record with the new data
- - If UpdateKind is ukDelete, it contains a bookmark to the deleted record (ie: the record is still there)
- }
- BookmarkData : TBufBookmark;
- { NextBookMarkData:
- - If UpdateKind is ukDelete, it contains a bookmark to the record just after the deleted record
- }
- NextBookmarkData : TBufBookmark;
- { OldValuesBuffer:
- - If UpdateKind is ukModify, it contains a record buffer which contains the old data
- - If UpdateKind is ukDelete, it contains a record buffer with the data of the deleted record
- }
- OldValuesBuffer : TRecordBuffer;
- end;
- TRecordsUpdateBuffer = array of TRecUpdateBuffer;
- TCompareFunc = function(subValue, aValue: pointer; options: TLocateOptions): int64;
- TDBCompareRec = record
- Comparefunc : TCompareFunc;
- Off1,Off2 : PtrInt;
- FieldInd1,
- FieldInd2 : longint;
- NullBOff1,
- NullBOff2 : PtrInt;
- Options : TLocateOptions;
- Desc : Boolean;
- end;
- TDBCompareStruct = array of TDBCompareRec;
- { TBufIndex }
- TBufIndex = class(TObject)
- private
- FDataset : TCustomBufDataset;
- protected
- function GetBookmarkSize: integer; virtual; abstract;
- function GetCurrentBuffer: Pointer; virtual; abstract;
- function GetCurrentRecord: TRecordBuffer; virtual; abstract;
- function GetIsInitialized: boolean; virtual; abstract;
- function GetSpareBuffer: TRecordBuffer; virtual; abstract;
- function GetSpareRecord: TRecordBuffer; virtual; abstract;
- function GetRecNo: Longint; virtual; abstract;
- procedure SetRecNo(ARecNo: Longint); virtual; abstract;
- public
- DBCompareStruct : TDBCompareStruct;
- Name : String;
- FieldsName : String;
- CaseinsFields : String;
- DescFields : String;
- Options : TIndexOptions;
- IndNr : integer;
- constructor Create(const ADataset : TCustomBufDataset); virtual;
- function ScrollBackward : TGetResult; virtual; abstract;
- function ScrollForward : TGetResult; virtual; abstract;
- function GetCurrent : TGetResult; virtual; abstract;
- function ScrollFirst : TGetResult; virtual; abstract;
- procedure ScrollLast; virtual; abstract;
- // Gets prior/next record relative to given bookmark; does not change current record
- function GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult; virtual;
- procedure SetToFirstRecord; virtual; abstract;
- procedure SetToLastRecord; virtual; abstract;
- procedure StoreCurrentRecord; virtual; abstract;
- procedure RestoreCurrentRecord; virtual; abstract;
- function CanScrollForward : Boolean; virtual; abstract;
- procedure DoScrollForward; virtual; abstract;
- procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); virtual; abstract;
- procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); virtual; abstract;
- procedure GotoBookmark(const ABookmark : PBufBookmark); virtual; abstract;
- function BookmarkValid(const ABookmark: PBufBookmark): boolean; virtual;
- function CompareBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : boolean; virtual;
- procedure InitialiseIndex; virtual; abstract;
- procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); virtual; abstract;
- procedure ReleaseSpareRecord; virtual; abstract;
- procedure BeginUpdate; virtual; abstract;
- // Adds a record to the end of the index as the new last record (spare record)
- // Normally only used in GetNextPacket
- procedure AddRecord; virtual; abstract;
- // Inserts a record before the current record, or if the record is sorted,
- // inserts it in the proper position
- procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); virtual; abstract;
- procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); virtual; abstract;
- procedure OrderCurrentRecord; virtual; abstract;
- procedure EndUpdate; virtual; abstract;
- property SpareRecord : TRecordBuffer read GetSpareRecord;
- property SpareBuffer : TRecordBuffer read GetSpareBuffer;
- property CurrentRecord : TRecordBuffer read GetCurrentRecord;
- property CurrentBuffer : Pointer read GetCurrentBuffer;
- property IsInitialized : boolean read GetIsInitialized;
- property BookmarkSize : integer read GetBookmarkSize;
- property RecNo : Longint read GetRecNo write SetRecNo;
- end;
-
- { TDoubleLinkedBufIndex }
- TDoubleLinkedBufIndex = class(TBufIndex)
- private
- FCursOnFirstRec : boolean;
- FStoredRecBuf : PBufRecLinkItem;
- FCurrentRecBuf : PBufRecLinkItem;
- protected
- function GetBookmarkSize: integer; override;
- function GetCurrentBuffer: Pointer; override;
- function GetCurrentRecord: TRecordBuffer; override;
- function GetIsInitialized: boolean; override;
- function GetSpareBuffer: TRecordBuffer; override;
- function GetSpareRecord: TRecordBuffer; override;
- function GetRecNo: Longint; override;
- procedure SetRecNo(ARecNo: Longint); override;
- public
- FLastRecBuf : PBufRecLinkItem;
- FFirstRecBuf : PBufRecLinkItem;
- FNeedScroll : Boolean;
- function ScrollBackward : TGetResult; override;
- function ScrollForward : TGetResult; override;
- function GetCurrent : TGetResult; override;
- function ScrollFirst : TGetResult; override;
- procedure ScrollLast; override;
- function GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult; override;
- procedure SetToFirstRecord; override;
- procedure SetToLastRecord; override;
- procedure StoreCurrentRecord; override;
- procedure RestoreCurrentRecord; override;
- function CanScrollForward : Boolean; override;
- procedure DoScrollForward; override;
- procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); override;
- procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
- procedure GotoBookmark(const ABookmark : PBufBookmark); override;
- procedure InitialiseIndex; override;
- procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
- procedure ReleaseSpareRecord; override;
- procedure BeginUpdate; override;
- procedure AddRecord; override;
- procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); override;
- procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); override;
- procedure OrderCurrentRecord; override;
- procedure EndUpdate; override;
- end;
- { TUniDirectionalBufIndex }
- TUniDirectionalBufIndex = class(TBufIndex)
- private
- FSPareBuffer: TRecordBuffer;
- protected
- function GetBookmarkSize: integer; override;
- function GetCurrentBuffer: Pointer; override;
- function GetCurrentRecord: TRecordBuffer; override;
- function GetIsInitialized: boolean; override;
- function GetSpareBuffer: TRecordBuffer; override;
- function GetSpareRecord: TRecordBuffer; override;
- function GetRecNo: Longint; override;
- procedure SetRecNo(ARecNo: Longint); override;
- public
- function ScrollBackward : TGetResult; override;
- function ScrollForward : TGetResult; override;
- function GetCurrent : TGetResult; override;
- function ScrollFirst : TGetResult; override;
- procedure ScrollLast; override;
- procedure SetToFirstRecord; override;
- procedure SetToLastRecord; override;
- procedure StoreCurrentRecord; override;
- procedure RestoreCurrentRecord; override;
- function CanScrollForward : Boolean; override;
- procedure DoScrollForward; override;
- procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); override;
- procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
- procedure GotoBookmark(const ABookmark : PBufBookmark); override;
- procedure InitialiseIndex; override;
- procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
- procedure ReleaseSpareRecord; override;
- procedure BeginUpdate; override;
- procedure AddRecord; override;
- procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); override;
- procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); override;
- procedure OrderCurrentRecord; override;
- procedure EndUpdate; override;
- end;
- { TArrayBufIndex }
- TArrayBufIndex = class(TBufIndex)
- private
- FStoredRecBuf : integer;
- FInitialBuffers,
- FGrowBuffer : integer;
- Function GetRecordFromBookmark(ABookmark: TBufBookmark) : integer;
- protected
- function GetBookmarkSize: integer; override;
- function GetCurrentBuffer: Pointer; override;
- function GetCurrentRecord: TRecordBuffer; override;
- function GetIsInitialized: boolean; override;
- function GetSpareBuffer: TRecordBuffer; override;
- function GetSpareRecord: TRecordBuffer; override;
- function GetRecNo: Longint; override;
- procedure SetRecNo(ARecNo: Longint); override;
- public
- FRecordArray : array of Pointer;
- FCurrentRecInd : integer;
- FLastRecInd : integer;
- FNeedScroll : Boolean;
- constructor Create(const ADataset: TCustomBufDataset); override;
- function ScrollBackward : TGetResult; override;
- function ScrollForward : TGetResult; override;
- function GetCurrent : TGetResult; override;
- function ScrollFirst : TGetResult; override;
- procedure ScrollLast; override;
- procedure SetToFirstRecord; override;
- procedure SetToLastRecord; override;
- procedure StoreCurrentRecord; override;
- procedure RestoreCurrentRecord; override;
- function CanScrollForward : Boolean; override;
- procedure DoScrollForward; override;
- procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); override;
- procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
- procedure GotoBookmark(const ABookmark : PBufBookmark); override;
- procedure InitialiseIndex; override;
- procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
- procedure ReleaseSpareRecord; override;
- procedure BeginUpdate; override;
- procedure AddRecord; override;
- procedure InsertRecordBeforeCurrentRecord(Const ARecord : TRecordBuffer); override;
- procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); override;
- procedure EndUpdate; override;
- end;
- { TBufDatasetReader }
- type
- TRowStateValue = (rsvOriginal, rsvDeleted, rsvInserted, rsvUpdated, rsvDetailUpdates);
- TRowState = set of TRowStateValue;
- type
- { TDataPacketReader }
- TDataPacketFormat = (dfBinary,dfXML,dfXMLUTF8,dfAny);
- TDatapacketReaderClass = class of TDatapacketReader;
- TDataPacketReader = class(TObject)
- FDataSet: TCustomBufDataset;
- FStream : TStream;
- protected
- class function RowStateToByte(const ARowState : TRowState) : byte;
- class function ByteToRowState(const AByte : Byte) : TRowState;
- procedure RestoreBlobField(AField: TField; ASource: pointer; ASize: integer);
- property DataSet: TCustomBufDataset read FDataSet;
- property Stream: TStream read FStream;
- public
- constructor Create(ADataSet: TCustomBufDataset; AStream : TStream); virtual;
- // Load a dataset from stream:
- // Load the field definitions from a stream.
- procedure LoadFieldDefs(var AnAutoIncValue : integer); virtual; abstract;
- // Is called before the records are loaded
- procedure InitLoadRecords; virtual; abstract;
- // Returns if there is at least one more record available in the stream
- function GetCurrentRecord : boolean; virtual; abstract;
- // Return the RowState of the current record, and the order of the update
- function GetRecordRowState(out AUpdOrder : Integer) : TRowState; virtual; abstract;
- // Store a record from stream in the current record buffer
- procedure RestoreRecord; virtual; abstract;
- // Move the stream to the next record
- procedure GotoNextRecord; virtual; abstract;
- // Store a dataset to stream:
- // Save the field definitions to a stream.
- procedure StoreFieldDefs(AnAutoIncValue : integer); virtual; abstract;
- // Save a record from the current record buffer to the stream
- procedure StoreRecord(ARowState : TRowState; AUpdOrder : integer = 0); virtual; abstract;
- // Is called after all records are stored
- procedure FinalizeStoreRecords; virtual; abstract;
- // Checks if the provided stream is of the right format for this class
- class function RecognizeStream(AStream : TStream) : boolean; virtual; abstract;
- end;
- { TFpcBinaryDatapacketReader }
- { Data layout:
- Header section:
- Identification: 13 bytes: 'BinBufDataSet'
- Version: 1 byte
- Columns section:
- Number of Fields: 2 bytes
- For each FieldDef: Name, DisplayName, Size: 2 bytes, DataType: 2 bytes, ReadOnlyAttr: 1 byte
- Parameter section:
- AutoInc Value: 4 bytes
- Rows section:
- Row header: each row begins with $fe: 1 byte
- row state: 1 byte (original, deleted, inserted, modified)
- update order: 4 bytes
- null bitmap: 1 byte per each 8 fields (if field is null corresponding bit is 1)
- Row data: variable length data are prefixed with 4 byte length indicator
- null fields are not stored (see: null bitmap)
- }
- TFpcBinaryDatapacketReader = class(TDataPacketReader)
- private
- const
- FpcBinaryIdent1 = 'BinBufDataset'; // Old version 1; support for transient period;
- FpcBinaryIdent2 = 'BinBufDataSet';
- StringFieldTypes = [ftString,ftFixedChar,ftWideString,ftFixedWideChar];
- BlobFieldTypes = [ftBlob,ftMemo,ftGraphic,ftWideMemo];
- VarLenFieldTypes = StringFieldTypes + BlobFieldTypes + [ftBytes,ftVarBytes];
- var
- FNullBitmapSize: integer;
- FNullBitmap: TBytes;
- protected
- var
- FVersion: byte;
- public
- constructor Create(ADataSet: TCustomBufDataset; AStream : TStream); override;
- procedure LoadFieldDefs(var AnAutoIncValue : integer); override;
- procedure StoreFieldDefs(AnAutoIncValue : integer); override;
- procedure InitLoadRecords; override;
- function GetCurrentRecord : boolean; override;
- function GetRecordRowState(out AUpdOrder : Integer) : TRowState; override;
- procedure RestoreRecord; override;
- procedure GotoNextRecord; override;
- procedure StoreRecord(ARowState : TRowState; AUpdOrder : integer = 0); override;
- procedure FinalizeStoreRecords; override;
- class function RecognizeStream(AStream : TStream) : boolean; override;
- end;
- TCustomBufDataset = class(TDBDataSet)
- private
- FFileName: string;
- FReadFromFile : boolean;
- FFileStream : TFileStream;
- FDatasetReader : TDataPacketReader;
- FIndexes : array of TBufIndex;
- FMaxIndexesCount: integer;
- FIndexesCount : integer;
- FCurrentIndex : TBufIndex;
- FFilterBuffer : TRecordBuffer;
- FBRecordCount : integer;
- FReadOnly : Boolean;
- FSavedState : TDatasetState;
- FPacketRecords : integer;
- FRecordSize : Integer;
- FNullmaskSize : byte;
- FOpen : Boolean;
- FUpdateBuffer : TRecordsUpdateBuffer;
- FCurrentUpdateBuffer : integer;
- FAutoIncValue : longint;
- FAutoIncField : TAutoIncField;
- FIndexDefs : TIndexDefs;
- FParser : TBufDatasetParser;
- FFieldBufPositions : array of longint;
- FAllPacketsFetched : boolean;
- FOnUpdateError : TResolverErrorEvent;
- FBlobBuffers : array of PBlobBuffer;
- FUpdateBlobBuffers: array of PBlobBuffer;
- procedure ProcessFieldsToCompareStruct(const AFields, ADescFields, ACInsFields: TList;
- const AIndexOptions: TIndexOptions; const ALocateOptions: TLocateOptions; out ACompareStruct: TDBCompareStruct);
- function BufferOffset: integer;
- function GetFieldSize(FieldDef : TFieldDef) : longint;
- procedure CalcRecordSize;
- function IntAllocRecordBuffer: TRecordBuffer;
- procedure IntLoadFieldDefsFromFile;
- procedure IntLoadRecordsFromFile;
- function GetCurrentBuffer: TRecordBuffer;
- procedure CurrentRecordToBuffer(Buffer: TRecordBuffer);
- function LoadBuffer(Buffer : TRecordBuffer): TGetResult;
- procedure FetchAll;
- function GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludePrior : boolean = false; AFindNext : boolean = false) : boolean;
- function GetRecordUpdateBufferCached(const ABookmark : TBufBookmark; IncludePrior : boolean = false) : boolean;
- function GetActiveRecordUpdateBuffer : boolean;
- procedure ParseFilter(const AFilter: string);
- function GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader;
- function GetIndexDefs : TIndexDefs;
- function GetIndexFieldNames: String;
- function GetIndexName: String;
- function GetBufUniDirectional: boolean;
- procedure SetIndexFieldNames(const AValue: String);
- procedure SetIndexName(AValue: String);
- procedure SetMaxIndexesCount(const AValue: Integer);
- procedure SetPacketRecords(aValue : integer);
- procedure SetBufUniDirectional(const AValue: boolean);
- // indexes handling
- procedure InitDefaultIndexes;
- procedure BuildIndex(var AIndex : TBufIndex);
- procedure BuildIndexes;
- procedure RemoveRecordFromIndexes(const ABookmark : TBufBookmark);
- protected
- // abstract & virtual methods of TDataset
- procedure UpdateIndexDefs; override;
- procedure SetRecNo(Value: Longint); override;
- function GetRecNo: Longint; override;
- function GetChangeCount: integer; virtual;
- function AllocRecordBuffer: TRecordBuffer; override;
- procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override;
- procedure ClearCalcFields(Buffer: TRecordBuffer); override;
- procedure InternalInitRecord(Buffer: TRecordBuffer); override;
- function GetCanModify: Boolean; override;
- function GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
- procedure DoBeforeClose; override;
- procedure InternalInitFieldDefs; override;
- procedure InternalOpen; override;
- procedure InternalClose; override;
- function GetRecordSize: Word; override;
- procedure InternalPost; override;
- procedure InternalCancel; Override;
- procedure InternalDelete; override;
- procedure InternalFirst; override;
- procedure InternalLast; override;
- procedure InternalSetToRecord(Buffer: TRecordBuffer); override;
- procedure InternalGotoBookmark(ABookmark: Pointer); override;
- procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
- procedure SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag); override;
- procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
- function GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag; override;
- function IsCursorOpen: Boolean; override;
- function GetRecordCount: Longint; override;
- procedure ApplyRecUpdate(UpdateKind : TUpdateKind); virtual;
- procedure SetOnUpdateError(const AValue: TResolverErrorEvent);
- procedure SetFilterText(const Value: String); override; {virtual;}
- procedure SetFiltered(Value: Boolean); override; {virtual;}
- procedure InternalRefresh; override;
- procedure DataEvent(Event: TDataEvent; Info: Ptrint); override;
- // virtual or methods, which can be used by descendants
- function GetNewBlobBuffer : PBlobBuffer;
- function GetNewWriteBlobBuffer : PBlobBuffer;
- procedure FreeBlobBuffer(var ABlobBuffer: PBlobBuffer);
- procedure InternalAddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string;
- const ACaseInsFields: string); virtual;
- procedure BeforeRefreshOpenCursor; virtual;
- procedure DoFilterRecord(out Acceptable: Boolean); virtual;
- procedure SetReadOnly(AValue: Boolean); virtual;
- function IsReadFromPacket : Boolean;
- function getnextpacket : integer;
- // abstracts, must be overidden by descendents
- function Fetch : boolean; virtual;
- function LoadField(FieldDef : TFieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; virtual;
- procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField); virtual; abstract;
- public
- constructor Create(AOwner: TComponent); override;
- function GetFieldData(Field: TField; Buffer: Pointer;
- NativeFormat: Boolean): Boolean; override;
- function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
- procedure SetFieldData(Field: TField; Buffer: Pointer;
- NativeFormat: Boolean); override;
- procedure SetFieldData(Field: TField; Buffer: Pointer); override;
- procedure ApplyUpdates; virtual; overload;
- procedure ApplyUpdates(MaxErrors: Integer); virtual; overload;
- procedure MergeChangeLog;
- procedure CancelUpdates; virtual;
- destructor Destroy; override;
- function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean; override;
- function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; override;
- function UpdateStatus: TUpdateStatus; override;
- function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
- procedure AddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string = '';
- const ACaseInsFields: string = ''); virtual;
- procedure ClearIndexes;
- procedure SetDatasetPacket(AReader : TDataPacketReader);
- procedure GetDatasetPacket(AWriter : TDataPacketReader);
- procedure LoadFromStream(AStream : TStream; Format: TDataPacketFormat = dfAny);
- procedure SaveToStream(AStream : TStream; Format: TDataPacketFormat = dfBinary);
- procedure LoadFromFile(AFileName: string = ''; Format: TDataPacketFormat = dfAny);
- procedure SaveToFile(AFileName: string = ''; Format: TDataPacketFormat = dfBinary);
- procedure CreateDataset;
- function BookmarkValid(ABookmark: TBookmark): Boolean; override;
- function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; override;
- property ChangeCount : Integer read GetChangeCount;
- property MaxIndexesCount : Integer read FMaxIndexesCount write SetMaxIndexesCount default 2;
- property ReadOnly : Boolean read FReadOnly write SetReadOnly default false;
- published
- property FileName : string read FFileName write FFileName;
- property PacketRecords : Integer read FPacketRecords write SetPacketRecords default 10;
- property OnUpdateError: TResolverErrorEvent read FOnUpdateError write SetOnUpdateError;
- property IndexDefs : TIndexDefs read GetIndexDefs;
- property IndexName : String read GetIndexName write SetIndexName;
- property IndexFieldNames : String read GetIndexFieldNames write SetIndexFieldNames;
- property UniDirectional: boolean read GetBufUniDirectional write SetBufUniDirectional default False;
- end;
- TBufDataset = class(TCustomBufDataset)
- published
- property MaxIndexesCount;
- // TDataset stuff
- property FieldDefs;
- Property Active;
- Property AutoCalcFields;
- Property Filter;
- Property Filtered;
- Property ReadOnly;
- Property AfterCancel;
- Property AfterClose;
- Property AfterDelete;
- Property AfterEdit;
- Property AfterInsert;
- Property AfterOpen;
- Property AfterPost;
- Property AfterScroll;
- Property BeforeCancel;
- Property BeforeClose;
- Property BeforeDelete;
- Property BeforeEdit;
- Property BeforeInsert;
- Property BeforeOpen;
- Property BeforePost;
- Property BeforeScroll;
- Property OnCalcFields;
- Property OnDeleteError;
- Property OnEditError;
- Property OnFilterRecord;
- Property OnNewRecord;
- Property OnPostError;
- end;
- procedure RegisterDatapacketReader(ADatapacketReaderClass : TDatapacketReaderClass; AFormat : TDataPacketFormat);
- implementation
- uses variants, dbconst, FmtBCD;
- Type TDatapacketReaderRegistration = record
- ReaderClass : TDatapacketReaderClass;
- Format : TDataPacketFormat;
- end;
- var RegisteredDatapacketReaders : Array of TDatapacketReaderRegistration;
- procedure RegisterDatapacketReader(ADatapacketReaderClass : TDatapacketReaderClass; AFormat : TDataPacketFormat);
- begin
- setlength(RegisteredDatapacketReaders,length(RegisteredDatapacketReaders)+1);
- with RegisteredDatapacketReaders[length(RegisteredDatapacketReaders)-1] do
- begin
- Readerclass := ADatapacketReaderClass;
- Format := AFormat;
- end;
- end;
- function GetRegisterDatapacketReader(AStream : TStream; AFormat : TDataPacketFormat; var ADataReaderClass : TDatapacketReaderRegistration) : boolean;
- var i : integer;
- begin
- Result := False;
- for i := 0 to length(RegisteredDatapacketReaders)-1 do if ((AFormat=dfAny) or (AFormat=RegisteredDatapacketReaders[i].Format)) then
- begin
- if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
- begin
- ADataReaderClass := RegisteredDatapacketReaders[i];
- Result := True;
- if (AStream <> nil) then AStream.Seek(0,soFromBeginning);
- break;
- end;
- AStream.Seek(0,soFromBeginning);
- end;
- end;
- function DBCompareText(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- if [loCaseInsensitive,loPartialKey]=options then
- Result := AnsiStrLIComp(pchar(subValue),pchar(aValue),length(pchar(subValue)))
- else if [loPartialKey] = options then
- Result := AnsiStrLComp(pchar(subValue),pchar(aValue),length(pchar(subValue)))
- else if [loCaseInsensitive] = options then
- Result := AnsiCompareText(pchar(subValue),pchar(aValue))
- else
- Result := AnsiCompareStr(pchar(subValue),pchar(aValue));
- end;
- function DBCompareWideText(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- if [loCaseInsensitive,loPartialKey]=options then
- Result := WideCompareText(pwidechar(subValue),LeftStr(pwidechar(aValue), Length(pwidechar(subValue))))
- else if [loPartialKey] = options then
- Result := WideCompareStr(pwidechar(subValue),LeftStr(pwidechar(aValue), Length(pwidechar(subValue))))
- else if [loCaseInsensitive] = options then
- Result := WideCompareText(pwidechar(subValue),pwidechar(aValue))
- else
- Result := WideCompareStr(pwidechar(subValue),pwidechar(aValue));
- end;
- function DBCompareByte(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- Result := PByte(subValue)^-PByte(aValue)^;
- end;
- function DBCompareSmallInt(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- Result := PSmallInt(subValue)^-PSmallInt(aValue)^;
- end;
- function DBCompareInt(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- Result := PInteger(subValue)^-PInteger(aValue)^;
- end;
- function DBCompareLargeInt(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- // A simple subtraction doesn't work, since it could be that the result
- // doesn't fit into a LargeInt
- if PLargeInt(subValue)^ < PLargeInt(aValue)^ then
- result := -1
- else if PLargeInt(subValue)^ > PLargeInt(aValue)^ then
- result := 1
- else
- result := 0;
- end;
- function DBCompareWord(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- Result := PWord(subValue)^-PWord(aValue)^;
- end;
- function DBCompareQWord(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- // A simple subtraction doesn't work, since it could be that the result
- // doesn't fit into a LargeInt
- if PQWord(subValue)^ < PQWord(aValue)^ then
- result := -1
- else if PQWord(subValue)^ > PQWord(aValue)^ then
- result := 1
- else
- result := 0;
- end;
- function DBCompareDouble(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- // A simple subtraction doesn't work, since it could be that the result
- // doesn't fit into a LargeInt
- if PDouble(subValue)^ < PDouble(aValue)^ then
- result := -1
- else if PDouble(subValue)^ > PDouble(aValue)^ then
- result := 1
- else
- result := 0;
- end;
- function DBCompareBCD(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
- begin
- result:=BCDCompare(PBCD(subValue)^, PBCD(aValue)^);
- end;
- procedure unSetFieldIsNull(NullMask : pbyte;x : longint); //inline;
- begin
- NullMask[x div 8] := (NullMask[x div 8]) and not (1 shl (x mod 8));
- end;
- procedure SetFieldIsNull(NullMask : pbyte;x : longint); //inline;
- begin
- NullMask[x div 8] := (NullMask[x div 8]) or (1 shl (x mod 8));
- end;
- function GetFieldIsNull(NullMask : pbyte;x : longint) : boolean; //inline;
- begin
- result := ord(NullMask[x div 8]) and (1 shl (x mod 8)) > 0
- end;
- function IndexCompareRecords(Rec1,Rec2 : pointer; ADBCompareRecs : TDBCompareStruct) : LargeInt;
- var IndexFieldNr : Integer;
- IsNull1, IsNull2 : boolean;
- begin
- for IndexFieldNr:=0 to length(ADBCompareRecs)-1 do with ADBCompareRecs[IndexFieldNr] do
- begin
- IsNull1:=GetFieldIsNull(rec1+NullBOff1,FieldInd1);
- IsNull2:=GetFieldIsNull(rec2+NullBOff2,FieldInd2);
- if IsNull1 and IsNull2 then
- result := 0
- else if IsNull1 then
- result := -1
- else if IsNull2 then
- result := 1
- else
- Result := Comparefunc(Rec1+Off1,Rec2+Off2,Options);
- if Result <> 0 then
- begin
- if Desc then
- Result := -Result;
- break;
- end;
- end;
- end;
- { ---------------------------------------------------------------------
- TCustomBufDataset
- ---------------------------------------------------------------------}
- constructor TCustomBufDataset.Create(AOwner : TComponent);
- begin
- Inherited Create(AOwner);
- FMaxIndexesCount:=2;
- FIndexesCount:=0;
- FIndexDefs := TIndexDefs.Create(Self);
- FAutoIncValue:=-1;
- SetLength(FUpdateBuffer,0);
- SetLength(FBlobBuffers,0);
- SetLength(FUpdateBlobBuffers,0);
- FParser := nil;
- FPacketRecords := 10;
- end;
- procedure TCustomBufDataset.SetPacketRecords(aValue : integer);
- begin
- if (aValue = -1) or (aValue > 0) then FPacketRecords := aValue
- else DatabaseError(SInvPacketRecordsValue);
- end;
- destructor TCustomBufDataset.Destroy;
- begin
- if Active then Close;
- SetLength(FUpdateBuffer,0);
- SetLength(FBlobBuffers,0);
- SetLength(FUpdateBlobBuffers,0);
- ClearIndexes;
- FreeAndNil(FIndexDefs);
- inherited destroy;
- end;
- procedure TCustomBufDataset.FetchAll;
- begin
- repeat
- until (getnextpacket < FPacketRecords) or (FPacketRecords = -1);
- end;
- {
- // Code to dump raw dataset data, including indexes information, useful for debugging
- procedure DumpRawMem(const Data: pointer; ALength: PtrInt);
- var
- b: integer;
- s1,s2: string;
- begin
- s1 := '';
- s2 := '';
- for b := 0 to ALength-1 do
- begin
- s1 := s1 + ' ' + hexStr(pbyte(Data)[b],2);
- if pchar(Data)[b] in ['a'..'z','A'..'Z','1'..'9',' '..'/',':'..'@'] then
- s2 := s2 + pchar(Data)[b]
- else
- s2 := s2 + '.';
- if length(s2)=16 then
- begin
- write(' ',s1,' ');
- writeln(s2);
- s1 := '';
- s2 := '';
- end;
- end;
- write(' ',s1,' ');
- writeln(s2);
- end;
- procedure DumpRecord(Dataset: TCustomBufDataset; RecBuf: PBufRecLinkItem; RawData: boolean = false);
- var ptr: pointer;
- NullMask: pointer;
- FieldData: pointer;
- NullMaskSize: integer;
- i: integer;
- begin
- if RawData then
- DumpRawMem(RecBuf,Dataset.RecordSize)
- else
- begin
- ptr := RecBuf;
- NullMask:= ptr + (sizeof(TBufRecLinkItem)*Dataset.MaxIndexesCount);
- NullMaskSize := 1+(Dataset.Fields.Count-1) div 8;
- FieldData:= ptr + (sizeof(TBufRecLinkItem)*Dataset.MaxIndexesCount) +NullMaskSize;
- write('record: $',hexstr(ptr),' nullmask: $');
- for i := 0 to NullMaskSize-1 do
- write(hexStr(byte((NullMask+i)^),2));
- write('=');
- for i := 0 to NullMaskSize-1 do
- write(binStr(byte((NullMask+i)^),8));
- writeln('%');
- for i := 0 to Dataset.MaxIndexesCount-1 do
- writeln(' ','Index ',inttostr(i),' Prior rec: ' + hexstr(pointer((ptr+(i*2)*sizeof(ptr))^)) + ' Next rec: ' + hexstr(pointer((ptr+((i*2)+1)*sizeof(ptr))^)));
- DumpRawMem(FieldData,Dataset.RecordSize-((sizeof(TBufRecLinkItem)*Dataset.MaxIndexesCount) +NullMaskSize));
- end;
- end;
- procedure DumpDataset(AIndex: TBufIndex;RawData: boolean = false);
- var RecBuf: PBufRecLinkItem;
- begin
- writeln('Dump records, order based on index ',AIndex.IndNr);
- writeln('Current record:',hexstr(AIndex.CurrentRecord));
- RecBuf:=(AIndex as TDoubleLinkedBufIndex).FFirstRecBuf;
- while RecBuf<>(AIndex as TDoubleLinkedBufIndex).FLastRecBuf do
- begin
- DumpRecord(AIndex.FDataset,RecBuf,RawData);
- RecBuf:=RecBuf[(AIndex as TDoubleLinkedBufIndex).IndNr].next;
- end;
- end;
- }
- procedure TCustomBufDataset.BuildIndex(var AIndex: TBufIndex);
- var PCurRecLinkItem : PBufRecLinkItem;
- p,l,q : PBufRecLinkItem;
- i,k,psize,qsize : integer;
- MergeAmount : integer;
- PlaceQRec : boolean;
- IndexFields : TList;
- DescIndexFields : TList;
- CInsIndexFields : TList;
- Index0,
- DblLinkIndex : TDoubleLinkedBufIndex;
- procedure PlaceNewRec(var e: PBufRecLinkItem; var esize: integer);
- begin
- if DblLinkIndex.FFirstRecBuf=nil then
- begin
- DblLinkIndex.FFirstRecBuf:=e;
- e[DblLinkIndex.IndNr].prior:=nil;
- l:=e;
- end
- else
- begin
- l[DblLinkIndex.IndNr].next:=e;
- e[DblLinkIndex.IndNr].prior:=l;
- l:=e;
- end;
- e := e[DblLinkIndex.IndNr].next;
- dec(esize);
- end;
- begin
- // Build the DBCompareStructure
- // One AS is enough, and makes debugging easier.
- DblLinkIndex:=(AIndex as TDoubleLinkedBufIndex);
- Index0:=(FIndexes[0] as TDoubleLinkedBufIndex);
- with DblLinkIndex do
- begin
- IndexFields := TList.Create;
- DescIndexFields := TList.Create;
- CInsIndexFields := TList.Create;
- try
- GetFieldList(IndexFields,FieldsName);
- GetFieldList(DescIndexFields,DescFields);
- GetFieldList(CInsIndexFields,CaseinsFields);
- if IndexFields.Count=0 then
- DatabaseError(SNoIndexFieldNameGiven);
- ProcessFieldsToCompareStruct(IndexFields, DescIndexFields, CInsIndexFields, Options, [], DBCompareStruct);
- finally
- CInsIndexFields.Free;
- DescIndexFields.Free;
- IndexFields.Free;
- end;
- end;
- // This simply copies the index...
- PCurRecLinkItem:=Index0.FFirstRecBuf;
- PCurRecLinkItem[DblLinkIndex.IndNr].next := PCurRecLinkItem[0].next;
- PCurRecLinkItem[DblLinkIndex.IndNr].prior := PCurRecLinkItem[0].prior;
- if PCurRecLinkItem <> Index0.FLastRecBuf then
- begin
- while PCurRecLinkItem^.next<>Index0.FLastRecBuf do
- begin
- PCurRecLinkItem:=PCurRecLinkItem^.next;
- PCurRecLinkItem[DblLinkIndex.IndNr].next := PCurRecLinkItem[0].next;
- PCurRecLinkItem[DblLinkIndex.IndNr].prior := PCurRecLinkItem[0].prior;
- end;
- end
- else
- // Empty dataset
- Exit;
- // Set FirstRecBuf and FCurrentRecBuf
- DblLinkIndex.FFirstRecBuf:=Index0.FFirstRecBuf;
- DblLinkIndex.FCurrentRecBuf:=DblLinkIndex.FFirstRecBuf;
- // Link in the FLastRecBuf that belongs to this index
- PCurRecLinkItem[DblLinkIndex.IndNr].next:=DblLinkIndex.FLastRecBuf;
- DblLinkIndex.FLastRecBuf[DblLinkIndex.IndNr].prior:=PCurRecLinkItem;
- // Mergesort. Used the algorithm as described here by Simon Tatham
- // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
- // The comments in the code are from this website.
- // In each pass, we are merging lists of size K into lists of size 2K.
- // (Initially K equals 1.)
- k:=1;
- repeat
- // So we start by pointing a temporary pointer p at the head of the list,
- // and also preparing an empty list L which we will add elements to the end
- // of as we finish dealing with them.
- p := DblLinkIndex.FFirstRecBuf;
- DblLinkIndex.FFirstRecBuf := nil;
- q := p;
- MergeAmount := 0;
- // Then:
- // * If p is null, terminate this pass.
- while p <> DblLinkIndex.FLastRecBuf do
- begin
- // * Otherwise, there is at least one element in the next pair of length-K
- // lists, so increment the number of merges performed in this pass.
- inc(MergeAmount);
- // * Point another temporary pointer, q, at the same place as p. Step q along
- // the list by K places, or until the end of the list, whichever comes
- // first. Let psize be the number of elements you managed to step q past.
- i:=0;
- while (i<k) and (q<>DblLinkIndex.FLastRecBuf) do
- begin
- inc(i);
- q := q[DblLinkIndex.IndNr].next;
- end;
- psize :=i;
- // * Let qsize equal K. Now we need to merge a list starting at p, of length
- // psize, with a list starting at q of length at most qsize.
- qsize:=k;
- // * So, as long as either the p-list is non-empty (psize > 0) or the q-list
- // is non-empty (qsize > 0 and q points to something non-null):
- while (psize>0) or ((qsize>0) and (q <> DblLinkIndex.FLastRecBuf)) do
- begin
- // * Choose which list to take the next element from. If either list
- // is empty, we must choose from the other one. (By assumption, at
- // least one is non-empty at this point.) If both lists are
- // non-empty, compare the first element of each and choose the lower
- // one. If the first elements compare equal, choose from the p-list.
- // (This ensures that any two elements which compare equal are never
- // swapped, so stability is guaranteed.)
- if (psize=0) then
- PlaceQRec := true
- else if (qsize=0) or (q = DblLinkIndex.FLastRecBuf) then
- PlaceQRec := False
- else if IndexCompareRecords(p,q,DblLinkIndex.DBCompareStruct) <= 0 then
- PlaceQRec := False
- else
- PlaceQRec := True;
-
- // * Remove that element, e, from the start of its list, by advancing
- // p or q to the next element along, and decrementing psize or qsize.
- // * Add e to the end of the list L we are building up.
- if PlaceQRec then
- PlaceNewRec(q,qsize)
- else
- PlaceNewRec(p,psize);
- end;
-
- // * Now we have advanced p until it is where q started out, and we have
- // advanced q until it is pointing at the next pair of length-K lists to
- // merge. So set p to the value of q, and go back to the start of this loop.
- p:=q;
- end;
- // As soon as a pass like this is performed and only needs to do one merge, the
- // algorithm terminates, and the output list L is sorted. Otherwise, double the
- // value of K, and go back to the beginning.
- l[DblLinkIndex.IndNr].next:=DblLinkIndex.FLastRecBuf;
- k:=k*2;
- until MergeAmount = 1;
- DblLinkIndex.FLastRecBuf[DblLinkIndex.IndNr].next:=DblLinkIndex.FFirstRecBuf;
- DblLinkIndex.FLastRecBuf[DblLinkIndex.IndNr].prior:=l;
- end;
- procedure TCustomBufDataset.BuildIndexes;
- var i: integer;
- begin
- for i:=1 to FIndexesCount-1 do
- if (i<>1) or (FIndexes[i]=FCurrentIndex) then
- BuildIndex(FIndexes[i]);
- end;
- procedure TCustomBufDataset.ClearIndexes;
- var
- i:integer;
- begin
- CheckInactive;
- For I:=0 to Length(FIndexes)-1 do
- FreeAndNil(Findexes[I]);
- SetLength(FIndexes,0);
- FIndexesCount:=0;
- end;
- procedure TCustomBufDataset.RemoveRecordFromIndexes(const ABookmark: TBufBookmark);
- var i: integer;
- begin
- for i:=0 to FIndexesCount-1 do
- if (i<>1) or (FIndexes[i]=FCurrentIndex) then
- FIndexes[i].RemoveRecordFromIndex(ABookmark);
- end;
- function TCustomBufDataset.GetIndexDefs : TIndexDefs;
- begin
- Result := FIndexDefs;
- end;
- procedure TCustomBufDataset.UpdateIndexDefs;
- var i : integer;
- begin
- FIndexDefs.Clear;
- for i := 0 to high(FIndexes) do with FIndexDefs.AddIndexDef do
- begin
- Name := FIndexes[i].Name;
- Fields := FIndexes[i].FieldsName;
- DescFields:= FIndexes[i].DescFields;
- CaseInsFields:=FIndexes[i].CaseinsFields;
- Options:=FIndexes[i].Options;
- end;
- end;
- function TCustomBufDataset.GetCanModify: Boolean;
- begin
- Result:=not (UniDirectional or ReadOnly);
- end;
- function TCustomBufDataset.BufferOffset: integer;
- begin
- // Returns the offset of data buffer in bufdataset record
- Result := sizeof(TBufRecLinkItem) * FMaxIndexesCount;
- end;
- function TCustomBufDataset.IntAllocRecordBuffer: TRecordBuffer;
- begin
- // Note: Only the internal buffers of TDataset provide bookmark information
- result := AllocMem(FRecordSize+BufferOffset);
- end;
- function TCustomBufDataset.AllocRecordBuffer: TRecordBuffer;
- begin
- result := AllocMem(FRecordSize + BookmarkSize + CalcFieldsSize);
- // The records are initialised, or else the fields of an empty, just-opened dataset
- // are not null
- InitRecord(result);
- end;
- procedure TCustomBufDataset.FreeRecordBuffer(var Buffer: TRecordBuffer);
- begin
- ReAllocMem(Buffer,0);
- end;
- procedure TCustomBufDataset.ClearCalcFields(Buffer: TRecordBuffer);
- begin
- if CalcFieldsSize > 0 then
- FillByte((Buffer+RecordSize)^,CalcFieldsSize,0);
- end;
- procedure TCustomBufDataset.InternalInitFieldDefs;
- begin
- if FileName<>'' then
- begin
- IntLoadFieldDefsFromFile;
- FreeAndNil(FDatasetReader);
- FreeAndNil(FFileStream);
- end;
- end;
- procedure TCustomBufDataset.InternalOpen;
- var IndexNr : integer;
- i : integer;
- begin
- if assigned(FDatasetReader) or (FileName<>'') then
- IntLoadFieldDefsFromFile;
- // This checks if the dataset is actually created (by calling CreateDataset,
- // or reading from a stream in some other way implemented by a descendent)
- // If there are less fields than FieldDefs we know for sure that the dataset
- // is not (correctly) created.
- // If there are constant expressions in the select statement (for PostgreSQL)
- // they are of type ftUnknown (in FieldDefs), and are not created (in Fields).
- // So Fields.Count < FieldDefs.Count in this case
- // See mantis #22030
- // if Fields.Count<FieldDefs.Count then
- if Fields.Count = 0 then
- DatabaseError(SErrNoDataset);
- // If there is a field with FieldNo=0 then the fields are not found to the
- // FieldDefs which is a sign that there is no dataset created. (Calculated and
- // lookup fields have FieldNo=-1)
- FAutoIncField:=nil;
- for i := 0 to Fields.Count-1 do
- if Fields[i].FieldNo=0 then
- DatabaseError(SErrNoDataset)
- else if (FAutoIncValue>-1) and (Fields[i] is TAutoIncField) and not assigned(FAutoIncField) then
- FAutoIncField := TAutoIncField(Fields[i]);
- InitDefaultIndexes;
- CalcRecordSize;
- FBRecordcount := 0;
- for IndexNr:=0 to FIndexesCount-1 do with FIndexes[IndexNr] do
- InitialiseSpareRecord(IntAllocRecordBuffer);
- FAllPacketsFetched := False;
- FOpen:=True;
- // parse filter expression
- ParseFilter(Filter);
- if assigned(FDatasetReader) then IntLoadRecordsFromFile;
- end;
- procedure TCustomBufDataset.DoBeforeClose;
- begin
- inherited DoBeforeClose;
- if FFileName<>'' then
- SaveToFile(FFileName);
- end;
- procedure TCustomBufDataset.InternalClose;
- var r : integer;
- iGetResult : TGetResult;
- pc : TRecordBuffer;
- begin
- FOpen:=False;
- if FIndexesCount>0 then with FIndexes[0] do if IsInitialized then
- begin
- iGetResult:=ScrollFirst;
- while iGetResult = grOK do
- begin
- pc := pointer(CurrentRecord);
- iGetResult:=ScrollForward;
- FreeRecordBuffer(pc);
- end;
- end;
- for r := 0 to FIndexesCount-1 do with FIndexes[r] do if IsInitialized then
- begin
- pc := SpareRecord;
- ReleaseSpareRecord;
- FreeRecordBuffer(pc);
- end;
- if Length(FUpdateBuffer) > 0 then
- begin
- for r := 0 to length(FUpdateBuffer)-1 do with FUpdateBuffer[r] do
- begin
- if assigned(OldValuesBuffer) then
- FreeRecordBuffer(OldValuesBuffer);
- if (UpdateKind = ukDelete) and assigned(BookmarkData.BookmarkData) then
- FreeRecordBuffer(TRecordBuffer(BookmarkData.BookmarkData));
- end;
- end;
- SetLength(FUpdateBuffer,0);
-
- for r := 0 to High(FBlobBuffers) do
- FreeBlobBuffer(FBlobBuffers[r]);
- for r := 0 to High(FUpdateBlobBuffers) do
- FreeBlobBuffer(FUpdateBlobBuffers[r]);
- SetLength(FBlobBuffers,0);
- SetLength(FUpdateBlobBuffers,0);
- SetLength(FFieldBufPositions,0);
- if FAutoIncValue>-1 then FAutoIncValue:=1;
- if assigned(FParser) then FreeAndNil(FParser);
- FReadFromFile:=false;
- end;
- procedure TCustomBufDataset.InternalFirst;
- begin
- with FCurrentIndex do
- // if FCurrentRecBuf = FLastRecBuf then the dataset is just opened and empty
- // in which case InternalFirst should do nothing (bug 7211)
- SetToFirstRecord;
- end;
- procedure TCustomBufDataset.InternalLast;
- begin
- FetchAll;
- with FCurrentIndex do
- SetToLastRecord;
- end;
- { TBufIndex }
- constructor TBufIndex.Create(const ADataset: TCustomBufDataset);
- begin
- inherited create;
- FDataset := ADataset;
- end;
- function TBufIndex.BookmarkValid(const ABookmark: PBufBookmark): boolean;
- begin
- Result := assigned(ABookmark) and assigned(ABookmark^.BookmarkData);
- end;
- function TBufIndex.CompareBookmarks(const ABookmark1, ABookmark2: PBufBookmark): boolean;
- begin
- Result := (ABookmark1^.BookmarkData=ABookmark2^.BookmarkData);
- end;
- function TBufIndex.GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult;
- begin
- Result := grError;
- end;
- { TDoubleLinkedBufIndex }
- function TDoubleLinkedBufIndex.GetBookmarkSize: integer;
- begin
- Result:=sizeof(TBufBookmark);
- end;
- function TDoubleLinkedBufIndex.GetCurrentBuffer: Pointer;
- begin
- Result := pointer(FCurrentRecBuf) + FDataset.BufferOffset;
- end;
- function TDoubleLinkedBufIndex.GetCurrentRecord: TRecordBuffer;
- begin
- Result := TRecordBuffer(FCurrentRecBuf);
- end;
- function TDoubleLinkedBufIndex.GetIsInitialized: boolean;
- begin
- Result := (FFirstRecBuf<>nil);
- end;
- function TDoubleLinkedBufIndex.GetSpareBuffer: TRecordBuffer;
- begin
- Result := pointer(FLastRecBuf) + FDataset.BufferOffset;
- end;
- function TDoubleLinkedBufIndex.GetSpareRecord: TRecordBuffer;
- begin
- Result := TRecordBuffer(FLastRecBuf);
- end;
- function TDoubleLinkedBufIndex.ScrollBackward: TGetResult;
- begin
- if not assigned(FCurrentRecBuf[IndNr].prior) then
- begin
- Result := grBOF;
- end
- else
- begin
- Result := grOK;
- FCurrentRecBuf := FCurrentRecBuf[IndNr].prior;
- end;
- end;
- function TDoubleLinkedBufIndex.ScrollForward: TGetResult;
- begin
- if (FCurrentRecBuf = FLastRecBuf) or // just opened
- (FCurrentRecBuf[IndNr].next = FLastRecBuf) then
- result := grEOF
- else
- begin
- FCurrentRecBuf := FCurrentRecBuf[IndNr].next;
- Result := grOK;
- end;
- end;
- function TDoubleLinkedBufIndex.GetCurrent: TGetResult;
- begin
- if FFirstRecBuf = FLastRecBuf then
- Result := grError
- else
- begin
- Result := grOK;
- if FCurrentRecBuf = FLastRecBuf then
- FCurrentRecBuf:=FLastRecBuf[IndNr].prior;
- end;
- end;
- function TDoubleLinkedBufIndex.ScrollFirst: TGetResult;
- begin
- FCurrentRecBuf:=FFirstRecBuf;
- if (FCurrentRecBuf = FLastRecBuf) then
- result := grEOF
- else
- result := grOK;
- end;
- procedure TDoubleLinkedBufIndex.ScrollLast;
- begin
- FCurrentRecBuf:=FLastRecBuf;
- end;
- function TDoubleLinkedBufIndex.GetRecord(ABookmark: PBufBookmark; GetMode: TGetMode): TGetResult;
- var ARecord : PBufRecLinkItem;
- begin
- Result := grOK;
- case GetMode of
- gmPrior:
- begin
- if assigned(ABookmark^.BookmarkData) then
- ARecord := ABookmark^.BookmarkData[IndNr].prior
- else
- ARecord := nil;
- if not assigned(ARecord) then
- Result := grBOF;
- end;
- gmNext:
- begin
- if assigned(ABookmark^.BookmarkData) then
- ARecord := ABookmark^.BookmarkData[IndNr].next
- else
- ARecord := FFirstRecBuf;
- end;
- else
- Result := grError;
- end;
- if ARecord = FLastRecBuf then
- Result := grEOF;
- // store into BookmarkData pointer to prior/next record
- ABookmark^.BookmarkData:=ARecord;
- end;
- procedure TDoubleLinkedBufIndex.SetToFirstRecord;
- begin
- FLastRecBuf[IndNr].next:=FFirstRecBuf;
- FCurrentRecBuf := FLastRecBuf;
- end;
- procedure TDoubleLinkedBufIndex.SetToLastRecord;
- begin
- if FLastRecBuf <> FFirstRecBuf then FCurrentRecBuf := FLastRecBuf;
- end;
- procedure TDoubleLinkedBufIndex.StoreCurrentRecord;
- begin
- FStoredRecBuf:=FCurrentRecBuf;
- end;
- procedure TDoubleLinkedBufIndex.RestoreCurrentRecord;
- begin
- FCurrentRecBuf:=FStoredRecBuf;
- end;
- procedure TDoubleLinkedBufIndex.DoScrollForward;
- begin
- FCurrentRecBuf := FCurrentRecBuf[IndNr].next;
- end;
- procedure TDoubleLinkedBufIndex.StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark);
- begin
- ABookmark^.BookmarkData:=FCurrentRecBuf;
- end;
- procedure TDoubleLinkedBufIndex.StoreSpareRecIntoBookmark(
- const ABookmark: PBufBookmark);
- begin
- ABookmark^.BookmarkData:=FLastRecBuf;
- end;
- procedure TDoubleLinkedBufIndex.GotoBookmark(const ABookmark : PBufBookmark);
- begin
- FCurrentRecBuf := ABookmark^.BookmarkData;
- end;
- procedure TDoubleLinkedBufIndex.InitialiseIndex;
- begin
- // Do nothing
- end;
- function TDoubleLinkedBufIndex.CanScrollForward: Boolean;
- begin
- if (FCurrentRecBuf[IndNr].next = FLastRecBuf) then
- Result := False
- else
- Result := True;
- end;
- procedure TDoubleLinkedBufIndex.InitialiseSpareRecord(const ASpareRecord : TRecordBuffer);
- begin
- FFirstRecBuf := pointer(ASpareRecord);
- FLastRecBuf := FFirstRecBuf;
- FLastRecBuf[IndNr].prior:=nil;
- FLastRecBuf[IndNr].next:=FLastRecBuf;
- FCurrentRecBuf := FLastRecBuf;
- end;
- procedure TDoubleLinkedBufIndex.ReleaseSpareRecord;
- begin
- FFirstRecBuf:= nil;
- end;
- function TDoubleLinkedBufIndex.GetRecNo: integer;
- var ARecord : PBufRecLinkItem;
- begin
- ARecord := FCurrentRecBuf;
- Result := 1;
- while ARecord <> FFirstRecBuf do
- begin
- inc(Result);
- ARecord := ARecord[IndNr].prior;
- end;
- end;
- procedure TDoubleLinkedBufIndex.SetRecNo(ARecNo: Longint);
- var ARecord : PBufRecLinkItem;
- begin
- ARecord := FFirstRecBuf;
- while (ARecNo > 1) and (ARecord <> FLastRecBuf) do
- begin
- dec(ARecNo);
- ARecord := ARecord[IndNr].next;
- end;
- FCurrentRecBuf := ARecord;
- end;
- procedure TDoubleLinkedBufIndex.BeginUpdate;
- begin
- if FCurrentRecBuf = FLastRecBuf then
- FCursOnFirstRec := True
- else
- FCursOnFirstRec := False;
- end;
- procedure TDoubleLinkedBufIndex.AddRecord;
- var ARecord: TRecordBuffer;
- begin
- ARecord := FDataset.IntAllocRecordBuffer;
- FLastRecBuf[IndNr].next := pointer(ARecord);
- FLastRecBuf[IndNr].next[IndNr].prior := FLastRecBuf;
- FLastRecBuf := FLastRecBuf[IndNr].next;
- end;
- procedure TDoubleLinkedBufIndex.InsertRecordBeforeCurrentRecord(const ARecord: TRecordBuffer);
- var ANewRecord : PBufRecLinkItem;
- begin
- ANewRecord:=PBufRecLinkItem(ARecord);
- ANewRecord[IndNr].prior:=FCurrentRecBuf[IndNr].prior;
- ANewRecord[IndNr].Next:=FCurrentRecBuf;
- if FCurrentRecBuf=FFirstRecBuf then
- begin
- FFirstRecBuf:=ANewRecord;
- ANewRecord[IndNr].prior:=nil;
- end
- else
- ANewRecord[IndNr].Prior[IndNr].next:=ANewRecord;
- ANewRecord[IndNr].next[IndNr].prior:=ANewRecord;
- end;
- procedure TDoubleLinkedBufIndex.RemoveRecordFromIndex(const ABookmark : TBufBookmark);
- var ARecord : PBufRecLinkItem;
- begin
- ARecord := ABookmark.BookmarkData;
- if ARecord = FCurrentRecBuf then DoScrollForward;
- if ARecord <> FFirstRecBuf then
- ARecord[IndNr].prior[IndNr].next := ARecord[IndNr].next
- else
- begin
- FFirstRecBuf := ARecord[IndNr].next;
- FLastRecBuf[IndNr].next := FFirstRecBuf;
- end;
- ARecord[IndNr].next[IndNr].prior := ARecord[IndNr].prior;
- end;
- procedure TDoubleLinkedBufIndex.OrderCurrentRecord;
- var ARecord: PBufRecLinkItem;
- ABookmark: TBufBookmark;
- begin
- // all records except current are already sorted
- // check prior records
- ARecord := FCurrentRecBuf;
- repeat
- ARecord := ARecord[IndNr].prior;
- until not assigned(ARecord) or (IndexCompareRecords(ARecord, FCurrentRecBuf, DBCompareStruct) <= 0);
- if assigned(ARecord) then
- ARecord := ARecord[IndNr].next
- else
- ARecord := FFirstRecBuf;
- if ARecord = FCurrentRecBuf then
- begin
- // prior record is less equal than current
- // check next records
- repeat
- ARecord := ARecord[IndNr].next;
- until (ARecord=FLastRecBuf) or (IndexCompareRecords(ARecord, FCurrentRecBuf, DBCompareStruct) >= 0);
- if ARecord = FCurrentRecBuf[IndNr].next then
- Exit; // current record is on proper position
- end;
- StoreCurrentRecIntoBookmark(@ABookmark);
- RemoveRecordFromIndex(ABookmark);
- FCurrentRecBuf := ARecord;
- InsertRecordBeforeCurrentRecord(TRecordBuffer(ABookmark.BookmarkData));
- GotoBookmark(@ABookmark);
- end;
- procedure TDoubleLinkedBufIndex.EndUpdate;
- begin
- FLastRecBuf[IndNr].next := FFirstRecBuf;
- if FCursOnFirstRec then FCurrentRecBuf:=FLastRecBuf;
- end;
- procedure TCustomBufDataset.CurrentRecordToBuffer(Buffer: TRecordBuffer);
- var ABookMark : PBufBookmark;
- begin
- with FCurrentIndex do
- begin
- move(CurrentBuffer^,buffer^,FRecordSize);
- ABookMark:=PBufBookmark(Buffer + FRecordSize);
- ABookmark^.BookmarkFlag:=bfCurrent;
- StoreCurrentRecIntoBookmark(ABookMark);
- end;
- GetCalcFields(Buffer);
- end;
- procedure TCustomBufDataset.SetBufUniDirectional(const AValue: boolean);
- begin
- CheckInactive;
- if (AValue<>IsUniDirectional) then
- begin
- SetUniDirectional(AValue);
- ClearIndexes;
- FPacketRecords := 1; // temporary
- end;
- end;
- procedure TCustomBufDataset.SetReadOnly(AValue: Boolean);
- begin
- FReadOnly:=AValue;
- end;
- function TCustomBufDataset.GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
- var Acceptable : Boolean;
- SavedState : TDataSetState;
- begin
- Result := grOK;
- with FCurrentIndex do
- repeat
- Acceptable := True;
- case GetMode of
- gmPrior : Result := ScrollBackward;
- gmCurrent : Result := GetCurrent;
- gmNext : begin
- if not CanScrollForward and (getnextpacket = 0) then
- Result := grEOF
- else
- begin
- Result := grOK;
- DoScrollForward;
- end;
- end;
- end;
- if Result = grOK then
- begin
- CurrentRecordToBuffer(Buffer);
- if Filtered then
- begin
- FFilterBuffer := Buffer;
- SavedState := SetTempState(dsFilter);
- DoFilterRecord(Acceptable);
- if (GetMode = gmCurrent) and not Acceptable then
- begin
- Acceptable := True;
- Result := grError;
- end;
- RestoreState(SavedState);
- end;
- end
- else if (Result = grError) and DoCheck then
- DatabaseError('No record');
- until Acceptable;
- end;
- function TCustomBufDataset.GetActiveRecordUpdateBuffer : boolean;
- var ABookmark : TBufBookmark;
- begin
- GetBookmarkData(ActiveBuffer,@ABookmark);
- result := GetRecordUpdateBufferCached(ABookmark);
- end;
- procedure TCustomBufDataset.ProcessFieldsToCompareStruct(const AFields, ADescFields, ACInsFields: TList;
- const AIndexOptions: TIndexOptions; const ALocateOptions: TLocateOptions; out ACompareStruct: TDBCompareStruct);
- var i: integer;
- AField: TField;
- ACompareRec: TDBCompareRec;
- begin
- SetLength(ACompareStruct, AFields.Count);
- for i:=0 to high(ACompareStruct) do
- begin
- AField := TField(AFields[i]);
- case AField.DataType of
- ftString, ftFixedChar : ACompareRec.Comparefunc := @DBCompareText;
- ftWideString, ftFixedWideChar: ACompareRec.Comparefunc := @DBCompareWideText;
- ftSmallint : ACompareRec.Comparefunc := @DBCompareSmallInt;
- ftInteger, ftBCD, ftAutoInc : ACompareRec.Comparefunc :=
- @DBCompareInt;
- ftWord : ACompareRec.Comparefunc := @DBCompareWord;
- ftBoolean : ACompareRec.Comparefunc := @DBCompareByte;
- ftFloat, ftCurrency : ACompareRec.Comparefunc := @DBCompareDouble;
- ftDateTime, ftDate, ftTime : ACompareRec.Comparefunc :=
- @DBCompareDouble;
- ftLargeint : ACompareRec.Comparefunc := @DBCompareLargeInt;
- ftFmtBCD : ACompareRec.Comparefunc := @DBCompareBCD;
- else
- DatabaseErrorFmt(SErrIndexBasedOnInvField, [AField.FieldName,Fieldtypenames[AField.DataType]]);
- end;
- ACompareRec.Off1:=BufferOffset + FFieldBufPositions[AField.FieldNo-1];
- ACompareRec.Off2:=ACompareRec.Off1;
- ACompareRec.FieldInd1:=AField.FieldNo-1;
- ACompareRec.FieldInd2:=ACompareRec.FieldInd1;
- ACompareRec.NullBOff1:=BufferOffset;
- ACompareRec.NullBOff2:=ACompareRec.NullBOff1;
- ACompareRec.Desc := ixDescending in AIndexOptions;
- if assigned(ADescFields) then
- ACompareRec.Desc := ACompareRec.Desc or (ADescFields.IndexOf(AField)>-1);
- ACompareRec.Options := ALocateOptions;
- if assigned(ACInsFields) and (ACInsFields.IndexOf(AField)>-1) then
- ACompareRec.Options := ACompareRec.Options + [loCaseInsensitive];
- ACompareStruct[i] := ACompareRec;
- end;
- end;
- procedure TCustomBufDataset.InitDefaultIndexes;
- begin
- if FIndexesCount=0 then
- begin
- InternalAddIndex('DEFAULT_ORDER','',[],'','');
- FCurrentIndex:=FIndexes[0];
- if not IsUniDirectional then
- InternalAddIndex('','',[],'','');
- BookmarkSize := FCurrentIndex.BookmarkSize;
- end;
- end;
- procedure TCustomBufDataset.AddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string = '';
- const ACaseInsFields: string = '');
- begin
- CheckBiDirectional;
- if AFields='' then DatabaseError(SNoIndexFieldNameGiven);
- if FIndexesCount=0 then
- InitDefaultIndexes;
- if Active and (FIndexesCount=FMaxIndexesCount) then
- DatabaseError(SMaxIndexes);
- // If not all packets are fetched, you can not sort properly.
- if not Active then
- FPacketRecords:=-1;
- InternalAddIndex(AName,AFields,AOptions,ADescFields,ACaseInsFields);
- end;
- procedure TCustomBufDataset.InternalAddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string;
- const ACaseInsFields: string);
- var StoreIndNr : Integer;
- begin
- if Active then FetchAll;
- if FIndexesCount>0 then
- StoreIndNr:=FCurrentIndex.IndNr
- else
- StoreIndNr:=0;
- inc(FIndexesCount);
- setlength(FIndexes,FIndexesCount); // This invalidates the currentindex! -> not anymore
- FCurrentIndex:=FIndexes[StoreIndNr];
- if IsUniDirectional then
- FIndexes[FIndexesCount-1] := TUniDirectionalBufIndex.Create(self)
- else
- FIndexes[FIndexesCount-1] := TDoubleLinkedBufIndex.Create(self);
- // FIndexes[FIndexesCount-1] := TArrayBufIndex.Create(self);
- with FIndexes[FIndexesCount-1] do
- begin
- InitialiseIndex;
- IndNr:=FIndexesCount-1;
- Name:=AName;
- FieldsName:=AFields;
- DescFields:=ADescFields;
- CaseinsFields:=ACaseInsFields;
- Options:=AOptions;
- end;
- if Active then
- begin
- FIndexes[FIndexesCount-1].InitialiseSpareRecord(IntAllocRecordBuffer);
- BuildIndex(FIndexes[FIndexesCount-1]);
- end
- else if FIndexesCount>FMaxIndexesCount then
- FMaxIndexesCount := FIndexesCount;
- FIndexDefs.Updated:=false;
- end;
- procedure TCustomBufDataset.SetIndexFieldNames(const AValue: String);
- begin
- if AValue<>'' then
- begin
- if FIndexesCount=0 then
- InitDefaultIndexes;
- FIndexes[1].FieldsName:=AValue;
- FCurrentIndex:=FIndexes[1];
- if Active then
- begin
- FetchAll;
- BuildIndex(FIndexes[1]);
- Resync([rmCenter]);
- end;
- FIndexDefs.Updated:=false;
- end
- else
- SetIndexName('');
- end;
- procedure TCustomBufDataset.SetIndexName(AValue: String);
- var i : integer;
- begin
- if AValue='' then AValue := 'DEFAULT_ORDER';
- for i := 0 to FIndexesCount-1 do
- if SameText(FIndexes[i].Name,AValue) then
- begin
- (FIndexes[i] as TDoubleLinkedBufIndex).FCurrentRecBuf:=(FCurrentIndex as TDoubleLinkedBufIndex).FCurrentRecBuf;
- FCurrentIndex:=FIndexes[i];
- if Active then Resync([rmCenter]);
- exit;
- end;
- end;
- procedure TCustomBufDataset.SetMaxIndexesCount(const AValue: Integer);
- begin
- CheckInactive;
- if AValue > 1 then
- FMaxIndexesCount:=AValue
- else
- DatabaseError(SMinIndexes);
- end;
- procedure TCustomBufDataset.InternalSetToRecord(Buffer: TRecordBuffer);
- begin
- FCurrentIndex.GotoBookmark(PBufBookmark(Buffer+FRecordSize));
- end;
- procedure TCustomBufDataset.SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
- begin
- PBufBookmark(Buffer + FRecordSize)^ := PBufBookmark(Data)^;
- end;
- procedure TCustomBufDataset.SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag);
- begin
- PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
- end;
- procedure TCustomBufDataset.GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
- begin
- PBufBookmark(Data)^ := PBufBookmark(Buffer + FRecordSize)^;
- end;
- function TCustomBufDataset.GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag;
- begin
- Result := PBufBookmark(Buffer + FRecordSize)^.BookmarkFlag;
- end;
- procedure TCustomBufDataset.InternalGotoBookmark(ABookmark: Pointer);
- begin
- // note that ABookMark should be a PBufBookmark. But this way it can also be
- // a pointer to a TBufRecLinkItem
- FCurrentIndex.GotoBookmark(ABookmark);
- end;
- function TCustomBufDataset.getnextpacket : integer;
- var i : integer;
- pb : TRecordBuffer;
- begin
- if FAllPacketsFetched then
- begin
- result := 0;
- exit;
- end;
- FCurrentIndex.BeginUpdate;
- i := 0;
- pb := FIndexes[0].SpareBuffer;
- while ((i < FPacketRecords) or (FPacketRecords = -1)) and (LoadBuffer(pb) = grOk) do
- begin
- with FIndexes[0] do
- begin
- AddRecord;
- pb := SpareBuffer;
- end;
- inc(i);
- end;
- FCurrentIndex.EndUpdate;
- FBRecordCount := FBRecordCount + i;
- result := i;
- end;
- function TCustomBufDataset.GetFieldSize(FieldDef : TFieldDef) : longint;
- begin
- case FieldDef.DataType of
- ftUnknown : result := 0;
- ftString,
- ftGuid,
- ftFixedChar: result := FieldDef.Size + 1;
- ftFixedWideChar,
- ftWideString:result := (FieldDef.Size + 1)*2;
- ftSmallint,
- ftInteger,
- ftAutoInc,
- ftword : result := sizeof(longint);
- ftBoolean : result := sizeof(wordbool);
- ftBCD : result := sizeof(currency);
- ftFmtBCD : result := sizeof(TBCD);
- ftFloat,
- ftCurrency : result := sizeof(double);
- ftLargeInt : result := sizeof(largeint);
- ftTime,
- ftDate,
- ftDateTime : result := sizeof(TDateTime);
- ftBytes : result := FieldDef.Size;
- ftVarBytes : result := FieldDef.Size + 2;
- ftVariant : result := sizeof(variant);
- ftBlob,
- ftMemo,
- ftGraphic,
- ftFmtMemo,
- ftParadoxOle,
- ftDBaseOle,
- ftTypedBinary,
- ftOraBlob,
- ftOraClob,
- ftWideMemo : result := sizeof(TBufBlobField)
- else
- DatabaseErrorFmt(SUnsupportedFieldType,[Fieldtypenames[FieldDef.DataType]]);
- end;
- {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
- result:=Align(result,4);
- {$ENDIF}
- end;
- function TCustomBufDataset.GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludePrior : boolean = false; AFindNext : boolean = false): boolean;
- var x : integer;
- StartBuf : integer;
- begin
- if AFindNext then
- StartBuf := FCurrentUpdateBuffer + 1
- else
- StartBuf := 0;
- Result := False;
- for x := StartBuf to high(FUpdateBuffer) do
- if FCurrentIndex.CompareBookmarks(@FUpdateBuffer[x].BookmarkData,@ABookmark) or
- (IncludePrior and (FUpdateBuffer[x].UpdateKind=ukDelete) and FCurrentIndex.CompareBookmarks(@FUpdateBuffer[x].NextBookmarkData,@ABookmark)) then
- begin
- FCurrentUpdateBuffer := x;
- Result := True;
- break;
- end;
- end;
- function TCustomBufDataset.GetRecordUpdateBufferCached(const ABookmark: TBufBookmark;
- IncludePrior: boolean): boolean;
- begin
- // if the current update buffer matches, immediately return true
- if (FCurrentUpdateBuffer < length(FUpdateBuffer)) and (
- FCurrentIndex.CompareBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData,@ABookmark) or
- (IncludePrior
- and (FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind=ukDelete)
- and FCurrentIndex.CompareBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData,@ABookmark))) then
- begin
- Result := True;
- end
- else
- Result := GetRecordUpdateBuffer(ABookmark,IncludePrior);
- end;
- function TCustomBufDataset.LoadBuffer(Buffer : TRecordBuffer): TGetResult;
- var NullMask : pbyte;
- x : longint;
- CreateBlobField : boolean;
- BufBlob : PBufBlobField;
- begin
- if not Fetch then
- begin
- Result := grEOF;
- FAllPacketsFetched := True;
- // This code has to be placed elsewhere. At least it should also run when
- // the datapacket is loaded from file ... see IntLoadRecordsFromFile
- BuildIndexes;
- Exit;
- end;
- NullMask := pointer(buffer);
- fillchar(Nullmask^,FNullmaskSize,0);
- inc(buffer,FNullmaskSize);
- for x := 0 to FieldDefs.Count-1 do
- begin
- if not LoadField(FieldDefs[x],buffer,CreateBlobField) then
- SetFieldIsNull(NullMask,x)
- else if CreateBlobField then
- begin
- BufBlob := PBufBlobField(Buffer);
- BufBlob^.BlobBuffer := GetNewBlobBuffer;
- LoadBlobIntoBuffer(FieldDefs[x],BufBlob);
- end;
- inc(buffer,GetFieldSize(FieldDefs[x]));
- end;
- Result := grOK;
- end;
- function TCustomBufDataset.GetCurrentBuffer: TRecordBuffer;
- begin
- case State of
- dsFilter: Result := FFilterBuffer;
- dsCalcFields: Result := CalcBuffer;
- else Result := ActiveBuffer;
- end;
- end;
- function TCustomBufDataset.GetFieldData(Field: TField; Buffer: Pointer;
- NativeFormat: Boolean): Boolean;
- begin
- Result := GetFieldData(Field, Buffer);
- end;
- function TCustomBufDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
- var CurrBuff : TRecordBuffer;
- begin
- Result := False;
- if State = dsOldValue then
- begin
- if FSavedState = dsInsert then
- CurrBuff := nil // old values = null
- else if GetActiveRecordUpdateBuffer then
- CurrBuff := FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer
- else
- // There is no UpdateBuffer for ActiveRecord, so there are no explicit old values available
- // then we can assume, that old values = current values
- CurrBuff := FCurrentIndex.CurrentBuffer;
- end
- else
- CurrBuff := GetCurrentBuffer;
- if not assigned(CurrBuff) then Exit;
- If Field.FieldNo > 0 then // If =-1, then calculated/lookup field or =0 unbound field
- begin
- if GetFieldIsNull(pbyte(CurrBuff),Field.FieldNo-1) then
- Exit;
- if assigned(buffer) then
- begin
- inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
- Move(CurrBuff^, Buffer^, GetFieldSize(FieldDefs[Field.FieldNo-1]));
- end;
- Result := True;
- end
- else
- begin
- Inc(CurrBuff, GetRecordSize + Field.Offset);
- Result := Boolean(CurrBuff^);
- if result and assigned(Buffer) then
- begin
- inc(CurrBuff);
- Move(CurrBuff^, Buffer^, Field.DataSize);
- end;
- end;
- end;
- procedure TCustomBufDataset.SetFieldData(Field: TField; Buffer: Pointer;
- NativeFormat: Boolean);
- begin
- SetFieldData(Field,Buffer);
- end;
- procedure TCustomBufDataset.SetFieldData(Field: TField; Buffer: Pointer);
- var CurrBuff : pointer;
- NullMask : pbyte;
- begin
- if not (State in dsWriteModes) then
- DatabaseError(SNotEditing, Self);
- CurrBuff := GetCurrentBuffer;
- If Field.FieldNo > 0 then // If =-1, then calculated/lookup field or =0 unbound field
- begin
- if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
- DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);
- if State in [dsEdit, dsInsert, dsNewValue] then
- Field.Validate(Buffer);
- NullMask := CurrBuff;
- inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]);
- if assigned(buffer) then
- begin
- Move(Buffer^, CurrBuff^, GetFieldSize(FieldDefs[Field.FieldNo-1]));
- unSetFieldIsNull(NullMask,Field.FieldNo-1);
- end
- else
- SetFieldIsNull(NullMask,Field.FieldNo-1);
- end
- else
- begin
- Inc(CurrBuff, GetRecordSize + Field.Offset);
- Boolean(CurrBuff^) := Buffer <> nil;
- inc(CurrBuff);
- if assigned(Buffer) then
- Move(Buffer^, CurrBuff^, Field.DataSize);
- end;
- if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
- DataEvent(deFieldChange, Ptrint(Field));
- end;
- procedure TCustomBufDataset.InternalDelete;
- var RemRec : pointer;
- RemRecBookmrk : TBufBookmark;
- begin
- InternalSetToRecord(ActiveBuffer);
- // Remove the record from all active indexes
- FCurrentIndex.StoreCurrentRecIntoBookmark(@RemRecBookmrk);
- RemRec := FCurrentIndex.CurrentBuffer;
- RemoveRecordFromIndexes(RemRecBookmrk);
- if not GetActiveRecordUpdateBuffer then
- begin
- FCurrentUpdateBuffer := length(FUpdateBuffer);
- SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
- FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := IntAllocRecordBuffer;
- move(RemRec^, FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
- end
- else
- begin
- if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind <> ukModify then
- begin
- FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := nil; //this 'disables' the updatebuffer
- // Do NOT release record buffer (pointed to by RemRecBookmrk.BookmarkData) here
- // - When record is inserted and deleted (and memory released) and again inserted then the same memory block can be returned
- // which leads to confusion, because we get the same BookmarkData for distinct records
- // - In CancelUpdates when records are restored, it is expected that deleted records still exist in memory
- // There also could be record(s) in the update buffer that is linked to this record.
- end;
- end;
- FCurrentIndex.StoreCurrentRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData);
- FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData := RemRecBookmrk;
- FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukDelete;
- dec(FBRecordCount);
- end;
- procedure TCustomBufDataset.ApplyRecUpdate(UpdateKind : TUpdateKind);
- begin
- raise EDatabaseError.Create(SApplyRecNotSupported);
- end;
- procedure TCustomBufDataset.CancelUpdates;
- var StoreRecBM : TBufBookmark;
- procedure CancelUpdBuffer(var AUpdBuffer : TRecUpdateBuffer);
- var
- TmpBuf : TRecordBuffer;
- StoreUpdBuf : integer;
- Bm : TBufBookmark;
- begin
- with AUpdBuffer do
- begin
- if Not assigned(BookmarkData.BookmarkData) then
- exit;// this is used to exclude buffers which are already handled
- Case UpdateKind of
- ukModify:
- begin
- FCurrentIndex.GotoBookmark(@BookmarkData);
- move(TRecordBuffer(OldValuesBuffer)^,TRecordBuffer(FCurrentIndex.CurrentBuffer)^,FRecordSize);
- FreeRecordBuffer(OldValuesBuffer);
- end;
- ukDelete:
- if (assigned(OldValuesBuffer)) then
- begin
- FCurrentIndex.GotoBookmark(@NextBookmarkData);
- FCurrentIndex.InsertRecordBeforeCurrentRecord(TRecordBuffer(BookmarkData.BookmarkData));
- FCurrentIndex.ScrollBackward;
- move(TRecordBuffer(OldValuesBuffer)^,TRecordBuffer(FCurrentIndex.CurrentBuffer)^,FRecordSize);
- {for x := length(FUpdateBuffer)-1 downto 0 do
- begin
- if (FUpdateBuffer[x].UpdateKind=ukDelete) and FCurrentIndex.CompareBookmarks(@FUpdateBuffer[x].NextBookmarkData,@BookmarkData) then
- CancelUpdBuffer(FUpdateBuffer[x]);
- end;}
- FreeRecordBuffer(OldValuesBuffer);
- inc(FBRecordCount);
- end ;
- ukInsert:
- begin
- // Process all update buffers linked to this record before this record is removed
- StoreUpdBuf:=FCurrentUpdateBuffer;
- Bm := BookmarkData;
- BookmarkData.BookmarkData:=nil; // Avoid infinite recursion...
- if GetRecordUpdateBuffer(Bm,True,False) then
- begin
- repeat
- if (FCurrentUpdateBuffer<>StoreUpdBuf) then
- CancelUpdBuffer(FUpdateBuffer[FCurrentUpdateBuffer]);
- until not GetRecordUpdateBuffer(Bm,True,True);
- end;
- FCurrentUpdateBuffer:=StoreUpdBuf;
- FCurrentIndex.GotoBookmark(@Bm);
- TmpBuf:=FCurrentIndex.CurrentRecord;
- // resync won't work if the currentbuffer is freed...
- if FCurrentIndex.CompareBookmarks(@Bm,@StoreRecBM) then with FCurrentIndex do
- begin
- GotoBookmark(@StoreRecBM);
- if ScrollForward = grEOF then
- if ScrollBackward = grBOF then
- ScrollLast; // last record will be removed from index, so move to spare record
- StoreCurrentRecIntoBookmark(@StoreRecBM);
- end;
- RemoveRecordFromIndexes(Bm);
- FreeRecordBuffer(TmpBuf);
- dec(FBRecordCount);
- end;
- end;
- BookmarkData.BookmarkData:=nil;
- end;
- end;
- var r : Integer;
- begin
- CheckBrowseMode;
- if Length(FUpdateBuffer) > 0 then
- begin
- FCurrentIndex.StoreCurrentRecIntoBookmark(@StoreRecBM);
- for r := Length(FUpdateBuffer) - 1 downto 0 do
- CancelUpdBuffer(FUpdateBuffer[r]);
- SetLength(FUpdateBuffer,0);
-
- FCurrentIndex.GotoBookmark(@StoreRecBM);
-
- Resync([]);
- end;
- end;
- procedure TCustomBufDataset.SetOnUpdateError(const AValue: TResolverErrorEvent);
- begin
- FOnUpdateError := AValue;
- end;
- procedure TCustomBufDataset.ApplyUpdates; // For backward compatibility
- begin
- ApplyUpdates(0);
- end;
- procedure TCustomBufDataset.ApplyUpdates(MaxErrors: Integer);
- var r : Integer;
- FailedCount : integer;
- Response : TResolverResponse;
- StoreCurrRec : TBufBookmark;
- AUpdateErr : EUpdateError;
- begin
- CheckBrowseMode;
- FCurrentIndex.StoreCurrentRecIntoBookmark(@StoreCurrRec);
- r := 0;
- FailedCount := 0;
- Response := rrApply;
- DisableControls;
- try
- while (r < Length(FUpdateBuffer)) and (Response <> rrAbort) do
- begin
- // If the record is first inserted and afterwards deleted, do nothing
- if not ((FUpdateBuffer[r].UpdateKind=ukDelete) and not (assigned(FUpdateBuffer[r].OldValuesBuffer))) then
- begin
- FCurrentIndex.GotoBookmark(@FUpdateBuffer[r].BookmarkData);
- // Synchronise the Currentbuffer to the ActiveBuffer
- CurrentRecordToBuffer(ActiveBuffer);
- Response := rrApply;
- try
- ApplyRecUpdate(FUpdateBuffer[r].UpdateKind);
- except
- on E: EDatabaseError do
- begin
- Inc(FailedCount);
- if FailedCount > word(MaxErrors) then Response := rrAbort
- else Response := rrSkip;
- if assigned(FOnUpdateError) then
- begin
- AUpdateErr := PSGetUpdateException(Exception(AcquireExceptionObject), nil);
- FOnUpdateError(Self,Self,AUpdateErr,FUpdateBuffer[r].UpdateKind,Response);
- AUpdateErr.Free;
- if Response in [rrApply, rrIgnore] then dec(FailedCount);
- if Response = rrApply then dec(r);
- end
- else if Response = rrAbort then
- Raise EUpdateError.Create(SOnUpdateError,E.Message,0,0,Exception(AcquireExceptionObject));
- end
- else
- raise;
- end;
- if Response in [rrApply, rrIgnore] then
- begin
- FreeRecordBuffer(FUpdateBuffer[r].OldValuesBuffer);
- if FUpdateBuffer[r].UpdateKind = ukDelete then
- FreeRecordBuffer( TRecordBuffer(FUpdateBuffer[r].BookmarkData.BookmarkData));
- FUpdateBuffer[r].BookmarkData.BookmarkData := nil;
- end
- end;
- inc(r);
- end;
- finally
- if FailedCount = 0 then
- MergeChangeLog;
- InternalGotoBookmark(@StoreCurrRec);
- Resync([]);
- EnableControls;
- end;
- end;
- procedure TCustomBufDataset.MergeChangeLog;
- var r : Integer;
- begin
- for r:=0 to length(FUpdateBuffer)-1 do
- if assigned(FUpdateBuffer[r].OldValuesBuffer) then
- FreeMem(FUpdateBuffer[r].OldValuesBuffer);
- SetLength(FUpdateBuffer,0);
- if assigned(FUpdateBlobBuffers) then for r:=0 to length(FUpdateBlobBuffers)-1 do
- if assigned(FUpdateBlobBuffers[r]) then
- begin
- // update blob buffer is already referenced from record buffer (see InternalPost)
- if FUpdateBlobBuffers[r]^.OrgBufID >= 0 then
- begin
- FreeBlobBuffer(FBlobBuffers[FUpdateBlobBuffers[r]^.OrgBufID]);
- FBlobBuffers[FUpdateBlobBuffers[r]^.OrgBufID] := FUpdateBlobBuffers[r];
- end
- else
- begin
- setlength(FBlobBuffers,length(FBlobBuffers)+1);
- FUpdateBlobBuffers[r]^.OrgBufID := high(FBlobBuffers);
- FBlobBuffers[high(FBlobBuffers)] := FUpdateBlobBuffers[r];
- end;
- end;
- SetLength(FUpdateBlobBuffers,0);
- end;
- procedure TCustomBufDataset.InternalCancel;
- Var i : integer;
- begin
- if assigned(FUpdateBlobBuffers) then for i:=0 to length(FUpdateBlobBuffers)-1 do
- if assigned(FUpdateBlobBuffers[i]) and (FUpdateBlobBuffers[i]^.FieldNo>0) then
- FreeBlobBuffer(FUpdateBlobBuffers[i]);
- end;
- procedure TCustomBufDataset.InternalPost;
- Var ABuff : TRecordBuffer;
- i : integer;
- bufblob : TBufBlobField;
- NullMask : pbyte;
- ABookmark : PBufBookmark;
- begin
- inherited InternalPost;
- if assigned(FUpdateBlobBuffers) then for i:=0 to length(FUpdateBlobBuffers)-1 do
- if assigned(FUpdateBlobBuffers[i]) and (FUpdateBlobBuffers[i]^.FieldNo>0) then
- begin
- bufblob.BlobBuffer := FUpdateBlobBuffers[i];
- NullMask := PByte(ActiveBuffer);
- if bufblob.BlobBuffer^.Size = 0 then
- SetFieldIsNull(NullMask, bufblob.BlobBuffer^.FieldNo-1)
- else
- unSetFieldIsNull(NullMask, bufblob.BlobBuffer^.FieldNo-1);
- bufblob.BlobBuffer^.FieldNo := -1;
- end;
- if State = dsInsert then
- begin
- if assigned(FAutoIncField) then
- begin
- FAutoIncField.AsInteger := FAutoIncValue;
- inc(FAutoIncValue);
- end;
- // The active buffer is the newly created TDataset record,
- // from which the bookmark is set to the record where the new record should be
- // inserted
- ABookmark := PBufBookmark(ActiveBuffer + FRecordSize);
- // Create the new record buffer
- ABuff := IntAllocRecordBuffer;
- // Add new record to all active indexes
- for i := 0 to FIndexesCount-1 do
- if (i<>1) or (FIndexes[i]=FCurrentIndex) then
- begin
- if ABookmark^.BookmarkFlag = bfEOF then
- // append (at end)
- FIndexes[i].ScrollLast
- else
- // insert (before current record)
- FIndexes[i].GotoBookmark(ABookmark);
- FIndexes[i].InsertRecordBeforeCurrentRecord(ABuff);
- // newly inserted record becomes current record
- FIndexes[i].ScrollBackward;
- end;
- // Link the newly created record buffer to the newly created TDataset record
- FCurrentIndex.StoreCurrentRecIntoBookmark(ABookmark);
- ABookmark^.BookmarkFlag := bfInserted;
- inc(FBRecordCount);
- end
- else
- InternalSetToRecord(ActiveBuffer);
- // If there is no updatebuffer already, add one
- if not GetActiveRecordUpdateBuffer then
- begin
- // Add a new updatebuffer
- FCurrentUpdateBuffer := length(FUpdateBuffer);
- SetLength(FUpdateBuffer,FCurrentUpdateBuffer+1);
- // Store a bookmark of the current record into the updatebuffer's bookmark
- FCurrentIndex.StoreCurrentRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
- if State = dsEdit then
- begin
- // Create an oldvalues buffer with the old values of the record
- FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := IntAllocRecordBuffer;
- with FCurrentIndex do
- // Move only the real data
- move(CurrentBuffer^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
- FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukModify;
- end
- else
- begin
- FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukInsert;
- FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := nil;
- end;
- end;
- move(ActiveBuffer^,FCurrentIndex.CurrentBuffer^,FRecordSize);
- // new data are now in current record so reorder current record if needed
- for i := 1 to FIndexesCount-1 do
- if (i<>1) or (FIndexes[i]=FCurrentIndex) then
- FIndexes[i].OrderCurrentRecord;
- end;
- procedure TCustomBufDataset.CalcRecordSize;
- var x : longint;
- begin
- FNullmaskSize := (FieldDefs.Count+7) div 8;
- {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
- FNullmaskSize:=Align(FNullmaskSize,4);
- {$ENDIF}
- FRecordSize := FNullmaskSize;
- SetLength(FFieldBufPositions,FieldDefs.count);
- for x := 0 to FieldDefs.count-1 do
- begin
- FFieldBufPositions[x] := FRecordSize;
- inc(FRecordSize, GetFieldSize(FieldDefs[x]));
- end;
- end;
- function TCustomBufDataset.GetIndexFieldNames: String;
- begin
- if (FIndexesCount=0) or (FCurrentIndex<>FIndexes[1]) then
- result := ''
- else
- result := FCurrentIndex.FieldsName;
- end;
- function TCustomBufDataset.GetIndexName: String;
- begin
- if FIndexesCount>0 then
- result := FCurrentIndex.Name
- else
- result := '';
- end;
- function TCustomBufDataset.GetBufUniDirectional: boolean;
- begin
- result := IsUniDirectional;
- end;
- function TCustomBufDataset.GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader;
- var APacketReader: TDataPacketReader;
- APacketReaderReg: TDatapacketReaderRegistration;
- begin
- if GetRegisterDatapacketReader(AStream, format, APacketReaderReg) then
- APacketReader := APacketReaderReg.ReaderClass.Create(Self, AStream)
- else if TFpcBinaryDatapacketReader.RecognizeStream(AStream) then
- begin
- AStream.Seek(0, soFromBeginning);
- APacketReader := TFpcBinaryDatapacketReader.Create(Self, AStream)
- end
- else
- DatabaseError(SStreamNotRecognised);
- Result:=APacketReader;
- end;
- function TCustomBufDataset.GetRecordSize : Word;
- begin
- result := FRecordSize + BookmarkSize;
- end;
- function TCustomBufDataset.GetChangeCount: integer;
- begin
- result := length(FUpdateBuffer);
- end;
- procedure TCustomBufDataset.InternalInitRecord(Buffer: TRecordBuffer);
- begin
- FillChar(Buffer^, FRecordSize, #0);
- fillchar(Buffer^,FNullmaskSize,255);
- end;
- procedure TCustomBufDataset.SetRecNo(Value: Longint);
- var ABookmark : TBufBookmark;
- begin
- CheckBrowseMode;
- if Value > RecordCount then
- repeat until (getnextpacket < FPacketRecords) or (Value <= RecordCount) or (FPacketRecords = -1);
- if (Value > RecordCount) or (Value < 1) then
- begin
- DatabaseError(SNoSuchRecord, Self);
- exit;
- end;
- FCurrentIndex.RecNo:=Value;
- FCurrentIndex.StoreCurrentRecIntoBookmark(@ABookmark);
- GotoBookmark(@ABookmark);
- end;
- function TCustomBufDataset.GetRecNo: Longint;
- begin
- if IsUniDirectional then
- Result := -1
- else if (FBRecordCount = 0) or (State = dsInsert) then
- Result := 0
- else
- begin
- InternalSetToRecord(ActiveBuffer);
- Result := FCurrentIndex.RecNo;
- end;
- end;
- function TCustomBufDataset.IsCursorOpen: Boolean;
- begin
- Result := FOpen;
- end;
- function TCustomBufDataset.GetRecordCount: Longint;
- begin
- Result := FBRecordCount;
- end;
- function TCustomBufDataset.UpdateStatus: TUpdateStatus;
- begin
- Result:=usUnmodified;
- if GetActiveRecordUpdateBuffer then
- case FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind of
- ukModify : Result := usModified;
- ukInsert : Result := usInserted;
- ukDelete : Result := usDeleted;
- end;
- end;
- function TCustomBufDataset.GetNewBlobBuffer : PBlobBuffer;
- var ABlobBuffer : PBlobBuffer;
- begin
- setlength(FBlobBuffers,length(FBlobBuffers)+1);
- new(ABlobBuffer);
- fillbyte(ABlobBuffer^,sizeof(ABlobBuffer^),0);
- ABlobBuffer^.OrgBufID := high(FBlobBuffers);
- FBlobBuffers[high(FBlobBuffers)] := ABlobBuffer;
- result := ABlobBuffer;
- end;
- function TCustomBufDataset.GetNewWriteBlobBuffer : PBlobBuffer;
- var ABlobBuffer : PBlobBuffer;
- begin
- setlength(FUpdateBlobBuffers,length(FUpdateBlobBuffers)+1);
- new(ABlobBuffer);
- fillbyte(ABlobBuffer^,sizeof(ABlobBuffer^),0);
- FUpdateBlobBuffers[high(FUpdateBlobBuffers)] := ABlobBuffer;
- result := ABlobBuffer;
- end;
- procedure TCustomBufDataset.FreeBlobBuffer(var ABlobBuffer: PBlobBuffer);
- begin
- if not Assigned(ABlobBuffer) then Exit;
- FreeMem(ABlobBuffer^.Buffer, ABlobBuffer^.Size);
- Dispose(ABlobBuffer);
- ABlobBuffer := Nil;
- end;
- { TBufBlobStream }
- function TBufBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
- begin
- Case Origin of
- soFromBeginning : FPosition:=Offset;
- soFromEnd : FPosition:=FBlobBuffer^.Size+Offset;
- soFromCurrent : FPosition:=FPosition+Offset;
- end;
- Result:=FPosition;
- end;
- function TBufBlobStream.Read(var Buffer; Count: Longint): Longint;
- var ptr : pointer;
- begin
- if FPosition + Count > FBlobBuffer^.Size then
- Count := FBlobBuffer^.Size-FPosition;
- ptr := FBlobBuffer^.Buffer+FPosition;
- move(ptr^, Buffer, Count);
- inc(FPosition, Count);
- result := Count;
- end;
- function TBufBlobStream.Write(const Buffer; Count: Longint): Longint;
- var ptr : pointer;
- begin
- ReAllocMem(FBlobBuffer^.Buffer, FPosition+Count);
- ptr := FBlobBuffer^.Buffer+FPosition;
- move(buffer, ptr^, Count);
- inc(FBlobBuffer^.Size, Count);
- inc(FPosition, Count);
- FModified := True;
- Result := Count;
- end;
- constructor TBufBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
- var bufblob : TBufBlobField;
- CurrBuff : TRecordBuffer;
- begin
- FField := Field;
- FDataSet := Field.DataSet as TCustomBufDataset;
- with FDataSet do
- if Mode = bmRead then
- begin
- if not Field.GetData(@bufblob) then
- DatabaseError(SFieldIsNull);
- if not assigned(bufblob.BlobBuffer) then
- begin
- bufblob.BlobBuffer := GetNewBlobBuffer;
- LoadBlobIntoBuffer(FieldDefs[Field.FieldNo-1], @bufblob);
- end;
- FBlobBuffer := bufblob.BlobBuffer;
- end
- else if Mode=bmWrite then
- begin
- FBlobBuffer := GetNewWriteBlobBuffer;
- FBlobBuffer^.FieldNo := Field.FieldNo;
- if Field.GetData(@bufblob) and assigned(bufblob.BlobBuffer) then
- FBlobBuffer^.OrgBufID := bufblob.BlobBuffer^.OrgBufID
- else
- FBlobBuffer^.OrgBufID := -1;
- bufblob.BlobBuffer := FBlobBuffer;
- // redirect pointer in current record buffer to new write blob buffer
- CurrBuff := GetCurrentBuffer;
- inc(CurrBuff, FDataSet.FFieldBufPositions[Field.FieldNo-1]);
- Move(bufblob, CurrBuff^, FDataSet.GetFieldSize(FDataSet.FieldDefs[Field.FieldNo-1]));
- FModified := True;
- end;
- end;
- destructor TBufBlobStream.Destroy;
- begin
- if FModified then
- begin
- // if TBufBlobStream was requested, but no data was written, then Size = 0;
- // used by TBlobField.Clear, so in this case set Field to null in InternalPost
- //FField.Modified := True; // should be set to True, but TBlobField.Modified is never reset
- if not (FDataSet.State in [dsFilter, dsCalcFields, dsNewValue]) then
- FDataSet.DataEvent(deFieldChange, PtrInt(FField));
- end;
- inherited Destroy;
- end;
- function TCustomBufDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
- var bufblob : TBufBlobField;
- begin
- result := nil;
- if Mode = bmRead then
- begin
- if not Field.GetData(@bufblob) then
- exit;
- result := TBufBlobStream.Create(Field as TBlobField, bmRead);
- end
- else if Mode = bmWrite then
- begin
- if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then
- DatabaseErrorFmt(SNotEditing,[Name],self);
- if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then
- DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName]);
- result := TBufBlobStream.Create(Field as TBlobField, bmWrite);
- end;
- end;
- procedure TCustomBufDataset.SetDatasetPacket(AReader: TDataPacketReader);
- begin
- FDatasetReader := AReader;
- try
- Open;
- finally
- FDatasetReader := nil;
- end;
- end;
- procedure TCustomBufDataset.GetDatasetPacket(AWriter: TDataPacketReader);
- procedure StoreUpdateBuffer(AUpdBuffer : TRecUpdateBuffer; var ARowState: TRowState);
- var AThisRowState : TRowState;
- AStoreUpdBuf : Integer;
- begin
- if AUpdBuffer.UpdateKind = ukModify then
- begin
- AThisRowState := [rsvOriginal];
- ARowState:=[rsvUpdated];
- end
- else if AUpdBuffer.UpdateKind = ukDelete then
- begin
- AStoreUpdBuf:=FCurrentUpdateBuffer;
- if GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,False) then
- begin
- repeat
- if FCurrentIndex.CompareBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData, @AUpdBuffer.BookmarkData) then
- StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
- until not GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,True)
- end;
- FCurrentUpdateBuffer:=AStoreUpdBuf;
- AThisRowState := [rsvDeleted];
- end
- else // ie: UpdateKind = ukInsert
- ARowState := [rsvInserted];
- FFilterBuffer:=AUpdBuffer.OldValuesBuffer;
- // OldValuesBuffer is nil if the record is either inserted or inserted and then deleted
- if assigned(FFilterBuffer) then
- FDatasetReader.StoreRecord(AThisRowState,FCurrentUpdateBuffer);
- end;
- procedure HandleUpdateBuffersFromRecord(AFirstCall : boolean;ARecBookmark : TBufBookmark; var ARowState: TRowState);
- var StoreUpdBuf1,StoreUpdBuf2 : Integer;
- begin
- if AFirstCall then ARowState:=[];
- if GetRecordUpdateBuffer(ARecBookmark,True,not AFirstCall) then
- begin
- if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind=ukDelete then
- begin
- StoreUpdBuf1:=FCurrentUpdateBuffer;
- HandleUpdateBuffersFromRecord(False,ARecBookmark,ARowState);
- StoreUpdBuf2:=FCurrentUpdateBuffer;
- FCurrentUpdateBuffer:=StoreUpdBuf1;
- StoreUpdateBuffer(FUpdateBuffer[StoreUpdBuf1], ARowState);
- FCurrentUpdateBuffer:=StoreUpdBuf2;
- end
- else
- begin
- StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
- HandleUpdateBuffersFromRecord(False,ARecBookmark,ARowState);
- end;
- end
- end;
- var ScrollResult : TGetResult;
- SavedState : TDataSetState;
- ABookMark : PBufBookmark;
- ATBookmark : TBufBookmark;
- RowState : TRowState;
- begin
- FDatasetReader := AWriter;
- try
- // CheckActive;
- ABookMark:=@ATBookmark;
- FDatasetReader.StoreFieldDefs(FAutoIncValue);
- SavedState:=SetTempState(dsFilter);
- ScrollResult:=FCurrentIndex.ScrollFirst;
- while ScrollResult=grOK do
- begin
- RowState:=[];
- FCurrentIndex.StoreCurrentRecIntoBookmark(ABookmark);
- HandleUpdateBuffersFromRecord(True,ABookmark^,RowState);
- FFilterBuffer:=FCurrentIndex.CurrentBuffer;
- if RowState=[] then
- FDatasetReader.StoreRecord([])
- else
- FDatasetReader.StoreRecord(RowState,FCurrentUpdateBuffer);
- ScrollResult:=FCurrentIndex.ScrollForward;
- if ScrollResult<>grOK then
- begin
- if getnextpacket>0 then
- ScrollResult := FCurrentIndex.ScrollForward;
- end;
- end;
- // There could be an update buffer linked to the last (spare) record
- FCurrentIndex.StoreSpareRecIntoBookmark(ABookmark);
- HandleUpdateBuffersFromRecord(True,ABookmark^,RowState);
- RestoreState(SavedState);
- FDatasetReader.FinalizeStoreRecords;
- finally
- FDatasetReader := nil;
- end;
- end;
- procedure TCustomBufDataset.LoadFromStream(AStream: TStream; Format: TDataPacketFormat);
- var APacketReader : TDataPacketReader;
- begin
- CheckBiDirectional;
- APacketReader:=GetPacketReader(Format, AStream);
- try
- SetDatasetPacket(APacketReader);
- finally
- APacketReader.Free;
- end;
- end;
- procedure TCustomBufDataset.SaveToStream(AStream: TStream; Format: TDataPacketFormat);
- var APacketReaderReg : TDatapacketReaderRegistration;
- APacketWriter : TDataPacketReader;
- begin
- CheckBiDirectional;
- if GetRegisterDatapacketReader(Nil,format,APacketReaderReg) then
- APacketWriter := APacketReaderReg.ReaderClass.Create(Self, AStream)
- else if Format = dfBinary then
- APacketWriter := TFpcBinaryDatapacketReader.Create(Self, AStream)
- else
- DatabaseError(SNoReaderClassRegistered);
- try
- GetDatasetPacket(APacketWriter);
- finally
- APacketWriter.Free;
- end;
- end;
- procedure TCustomBufDataset.LoadFromFile(AFileName: string; Format: TDataPacketFormat);
- var AFileStream : TFileStream;
- begin
- if AFileName='' then AFileName := FFileName;
- AFileStream := TFileStream.Create(AFileName,fmOpenRead);
- try
- LoadFromStream(AFileStream, Format);
- finally
- AFileStream.Free;
- end;
- end;
- procedure TCustomBufDataset.SaveToFile(AFileName: string;
- Format: TDataPacketFormat);
- var AFileStream : TFileStream;
- begin
- if AFileName='' then AFileName := FFileName;
- AFileStream := TFileStream.Create(AFileName,fmCreate);
- try
- SaveToStream(AFileStream, Format);
- finally
- AFileStream.Free;
- end;
- end;
- procedure TCustomBufDataset.CreateDataset;
- var AStoreFileName: string;
- begin
- CheckInactive;
- if ((FieldCount=0) or (FieldDefs.Count=0)) then
- begin
- if (FieldDefs.Count>0) then
- CreateFields
- else if (Fields.Count>0) then
- begin
- InitFieldDefsFromFields;
- BindFields(True);
- end
- else
- raise Exception.Create(SErrNoFieldsDefined);
- FAutoIncValue:=1;
- end;
- // When a FileName is set, do not read from this file
- AStoreFileName:=FFileName;
- FFileName := '';
- try
- Open;
- finally
- FFileName:=AStoreFileName;
- end;
- end;
- function TCustomBufDataset.BookmarkValid(ABookmark: TBookmark): Boolean;
- begin
- Result:=assigned(FCurrentIndex) and FCurrentIndex.BookmarkValid(pointer(ABookmark));
- end;
- function TCustomBufDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark
- ): Longint;
- begin
- if Assigned(FCurrentIndex) and FCurrentIndex.CompareBookmarks(pointer(Bookmark1),pointer(Bookmark2)) then
- Result := 0
- else
- Result := -1;
- end;
- procedure TCustomBufDataset.IntLoadFieldDefsFromFile;
- begin
- FReadFromFile := True;
- if not assigned(FDatasetReader) then
- begin
- FFileStream := TFileStream.Create(FileName, fmOpenRead);
- FDatasetReader := GetPacketReader(dfAny, FFileStream);
- end;
- FieldDefs.Clear;
- FDatasetReader.LoadFieldDefs(FAutoIncValue);
- if DefaultFields then
- CreateFields
- else
- BindFields(true);
- end;
- procedure TCustomBufDataset.IntLoadRecordsFromFile;
- var SavedState : TDataSetState;
- AddRecordBuffer : boolean;
- ARowState : TRowState;
- AUpdOrder : integer;
- x : integer;
- begin
- CheckBiDirectional;
- FDatasetReader.InitLoadRecords;
- SavedState:=SetTempState(dsFilter);
- while FDatasetReader.GetCurrentRecord do
- begin
- ARowState := FDatasetReader.GetRecordRowState(AUpdOrder);
- if rsvOriginal in ARowState then
- begin
- if length(FUpdateBuffer) < (AUpdOrder+1) then
- SetLength(FUpdateBuffer,AUpdOrder+1);
- FCurrentUpdateBuffer:=AUpdOrder;
- FFilterBuffer:=IntAllocRecordBuffer;
- fillchar(FFilterBuffer^,FNullmaskSize,0);
- FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := FFilterBuffer;
- FDatasetReader.RestoreRecord;
- FDatasetReader.GotoNextRecord;
- if not FDatasetReader.GetCurrentRecord then
- DatabaseError(SStreamNotRecognised);
- ARowState := FDatasetReader.GetRecordRowState(AUpdOrder);
- if rsvUpdated in ARowState then
- FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukModify
- else
- DatabaseError(SStreamNotRecognised);
- FFilterBuffer:=FIndexes[0].SpareBuffer;
- FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
- fillchar(FFilterBuffer^,FNullmaskSize,0);
- FDatasetReader.RestoreRecord;
- FIndexes[0].AddRecord;
- inc(FBRecordCount);
- AddRecordBuffer:=False;
- end
- else if rsvDeleted in ARowState then
- begin
- if length(FUpdateBuffer) < (AUpdOrder+1) then
- SetLength(FUpdateBuffer,AUpdOrder+1);
- FCurrentUpdateBuffer:=AUpdOrder;
- FFilterBuffer:=IntAllocRecordBuffer;
- fillchar(FFilterBuffer^,FNullmaskSize,0);
- FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := FFilterBuffer;
- FDatasetReader.RestoreRecord;
- FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukDelete;
- FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
- FIndexes[0].AddRecord;
- FIndexes[0].RemoveRecordFromIndex(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
- FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData);
- for x := FCurrentUpdateBuffer+1 to length(FUpdateBuffer)-1 do
- if Findexes[0].CompareBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData,@FUpdateBuffer[x].NextBookmarkData) then
- FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[x].NextBookmarkData);
- AddRecordBuffer:=False;
- end
- else
- AddRecordBuffer:=True;
- if AddRecordBuffer then
- begin
- FFilterBuffer:=FIndexes[0].SpareBuffer;
- fillchar(FFilterBuffer^,FNullmaskSize,0);
- FDatasetReader.RestoreRecord;
- if rsvInserted in ARowState then
- begin
- if length(FUpdateBuffer) < (AUpdOrder+1) then
- SetLength(FUpdateBuffer,AUpdOrder+1);
- FCurrentUpdateBuffer:=AUpdOrder;
- FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukInsert;
- FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
- end;
- FIndexes[0].AddRecord;
- inc(FBRecordCount);
- end;
- FDatasetReader.GotoNextRecord;
- end;
- RestoreState(SavedState);
- FIndexes[0].SetToFirstRecord;
- FAllPacketsFetched:=True;
- if assigned(FFileStream) then
- begin
- FreeAndNil(FFileStream);
- FreeAndNil(FDatasetReader);
- end;
- // rebuild indexes
- BuildIndexes;
- end;
- procedure TCustomBufDataset.DoFilterRecord(out Acceptable: Boolean);
- begin
- Acceptable := true;
- // check user filter
- if Assigned(OnFilterRecord) then
- OnFilterRecord(Self, Acceptable);
- // check filtertext
- if Acceptable and (Length(Filter) > 0) then
- Acceptable := Boolean((FParser.ExtractFromBuffer(GetCurrentBuffer))^);
- end;
- procedure TCustomBufDataset.SetFilterText(const Value: String);
- begin
- if Value = Filter then
- exit;
- // parse
- ParseFilter(Value);
- // call dataset method
- inherited;
- // refilter dataset if filtered
- if IsCursorOpen and Filtered then Resync([]);
- end;
- procedure TCustomBufDataset.SetFiltered(Value: Boolean); {override;}
- begin
- if Value = Filtered then
- exit;
- // pass on to ancestor
- inherited;
- // only refresh if active
- if IsCursorOpen then
- Resync([]);
- end;
- procedure TCustomBufDataset.InternalRefresh;
- var StoreDefaultFields: boolean;
- begin
- if length(FUpdateBuffer)>0 then
- DatabaseError(SErrApplyUpdBeforeRefresh);
- StoreDefaultFields:=DefaultFields;
- SetDefaultFields(False);
- FreeFieldBuffers;
- ClearBuffers;
- InternalClose;
- BeforeRefreshOpenCursor;
- InternalOpen;
- SetDefaultFields(StoreDefaultFields);
- end;
- procedure TCustomBufDataset.BeforeRefreshOpenCursor;
- begin
- // Do nothing
- end;
- procedure TCustomBufDataset.DataEvent(Event: TDataEvent; Info: Ptrint);
- begin
- if Event = deUpdateState then
- // Save DataSet.State set by DataSet.SetState (filter out State set by DataSet.SetTempState)
- FSavedState := State;
- inherited;
- end;
- function TCustomBufDataset.Fetch: boolean;
- begin
- // Empty procedure to make it possible to use TCustomBufDataset as a memory dataset
- Result := False;
- end;
- function TCustomBufDataset.LoadField(FieldDef: TFieldDef; buffer: pointer; out
- CreateBlob: boolean): boolean;
- begin
- // Empty procedure to make it possible to use TCustomBufDataset as a memory dataset
- CreateBlob := False;
- Result := False;
- end;
- function TCustomBufDataset.IsReadFromPacket: Boolean;
- begin
- Result := (FDatasetReader<>nil) or (FFileName<>'') or FReadFromFile;
- end;
- procedure TCustomBufDataset.ParseFilter(const AFilter: string);
- begin
- // parser created?
- if Length(AFilter) > 0 then
- begin
- if (FParser = nil) and IsCursorOpen then
- begin
- FParser := TBufDatasetParser.Create(Self);
- end;
- // is there a parser now?
- if FParser <> nil then
- begin
- // set options
- FParser.PartialMatch := not (foNoPartialCompare in FilterOptions);
- FParser.CaseInsensitive := foCaseInsensitive in FilterOptions;
- // parse expression
- FParser.ParseExpression(AFilter);
- end;
- end;
- end;
- function TCustomBufDataset.Locate(const KeyFields: string;
- const KeyValues: Variant; Options: TLocateOptions): boolean;
- var SearchFields : TList;
- DBCompareStruct : TDBCompareStruct;
- ABookmark : TBufBookmark;
- SavedState : TDataSetState;
- FilterRecord : TRecordBuffer;
- FilterAcceptable: boolean;
- begin
- // Call inherited to make sure the dataset is bi-directional
- Result := inherited;
- CheckActive;
- if IsEmpty then exit;
- // Build the DBCompare structure
- SearchFields := TList.Create;
- try
- GetFieldList(SearchFields,KeyFields);
- if SearchFields.Count=0 then exit;
- ProcessFieldsToCompareStruct(SearchFields, nil, nil, [], Options, DBCompareStruct);
- finally
- SearchFields.Free;
- end;
- // Set the filter buffer
- SavedState:=SetTempState(dsFilter);
- FilterRecord:=IntAllocRecordBuffer;
- FFilterBuffer:=FilterRecord + BufferOffset;
- SetFieldValues(KeyFields,KeyValues);
- // Iterate through the records until a match is found
- ABookmark.BookmarkData:=nil;
- while true do
- begin
- // try get next record
- if FCurrentIndex.GetRecord(@ABookmark, gmNext) <> grOK then
- // for grEOF ABookmark points to SpareRecord, which is used for storing next record(s)
- if getnextpacket = 0 then
- break;
- if IndexCompareRecords(FilterRecord, ABookmark.BookmarkData, DBCompareStruct) = 0 then
- begin
- if Filtered then
- begin
- FFilterBuffer:=pointer(ABookmark.BookmarkData) + BufferOffset;
- // The dataset state is still dsFilter at this point, so we don't have to set it.
- DoFilterRecord(FilterAcceptable);
- if FilterAcceptable then
- begin
- Result := True;
- break;
- end;
- end
- else
- begin
- Result := True;
- break;
- end;
- end;
- end;
- RestoreState(SavedState);
- FreeRecordBuffer(FilterRecord);
- // If a match is found, jump to the found record
- if Result then
- begin
- ABookmark.BookmarkFlag := bfCurrent;
- GotoBookmark(@ABookmark);
- end;
- end;
- function TCustomBufDataset.Lookup(const KeyFields: string;
- const KeyValues: Variant; const ResultFields: string): Variant;
- var
- bm:TBookmark;
- begin
- result:=Null;
- bm:=GetBookmark;
- DisableControls;
- try
- if Locate(KeyFields,KeyValues,[]) then
- begin
- // CalculateFields(ActiveBuffer); // not needed, done by Locate more than once
- result:=FieldValues[ResultFields];
- end;
- GotoBookmark(bm);
- FreeBookmark(bm);
- finally
- EnableControls;
- end;
- end;
- { TArrayBufIndex }
- function TArrayBufIndex.GetBookmarkSize: integer;
- begin
- Result:=Sizeof(TBufBookmark);
- end;
- function TArrayBufIndex.GetCurrentBuffer: Pointer;
- begin
- Result:=TRecordBuffer(FRecordArray[FCurrentRecInd]);
- end;
- function TArrayBufIndex.GetCurrentRecord: TRecordBuffer;
- begin
- Result:=GetCurrentBuffer;
- end;
- function TArrayBufIndex.GetIsInitialized: boolean;
- begin
- Result:=Length(FRecordArray)>0;
- end;
- function TArrayBufIndex.GetSpareBuffer: TRecordBuffer;
- begin
- if FLastRecInd>-1 then
- Result:= TRecordBuffer(FRecordArray[FLastRecInd])
- else
- Result := nil;
- end;
- function TArrayBufIndex.GetSpareRecord: TRecordBuffer;
- begin
- Result := GetSpareBuffer;
- end;
- constructor TArrayBufIndex.Create(const ADataset: TCustomBufDataset);
- begin
- Inherited create(ADataset);
- FInitialBuffers:=10000;
- FGrowBuffer:=1000;
- end;
- function TArrayBufIndex.ScrollBackward: TGetResult;
- begin
- if FCurrentRecInd>0 then
- begin
- dec(FCurrentRecInd);
- Result := grOK;
- end
- else
- Result := grBOF;
- end;
- function TArrayBufIndex.ScrollForward: TGetResult;
- begin
- if FCurrentRecInd = FLastRecInd-1 then
- result := grEOF
- else
- begin
- Result:=grOK;
- inc(FCurrentRecInd);
- end;
- end;
- function TArrayBufIndex.GetCurrent: TGetResult;
- begin
- if FLastRecInd=0 then
- Result := grError
- else
- begin
- Result := grOK;
- if FCurrentRecInd = FLastRecInd then
- dec(FCurrentRecInd);
- end;
- end;
- function TArrayBufIndex.ScrollFirst: TGetResult;
- begin
- FCurrentRecInd:=0;
- if (FCurrentRecInd = FLastRecInd) then
- result := grEOF
- else
- result := grOk;
- end;
- procedure TArrayBufIndex.ScrollLast;
- begin
- FCurrentRecInd:=FLastRecInd;
- end;
- procedure TArrayBufIndex.SetToFirstRecord;
- begin
- // if FCurrentRecBuf = FLastRecBuf then the dataset is just opened and empty
- // in which case InternalFirst should do nothing (bug 7211)
- if FCurrentRecInd <> FLastRecInd then
- FCurrentRecInd := -1;
- end;
- procedure TArrayBufIndex.SetToLastRecord;
- begin
- if FLastRecInd <> 0 then FCurrentRecInd := FLastRecInd;
- end;
- procedure TArrayBufIndex.StoreCurrentRecord;
- begin
- FStoredRecBuf := FCurrentRecInd;
- end;
- procedure TArrayBufIndex.RestoreCurrentRecord;
- begin
- FCurrentRecInd := FStoredRecBuf;
- end;
- function TArrayBufIndex.CanScrollForward: Boolean;
- begin
- Result := (FCurrentRecInd < FLastRecInd-1);
- end;
- procedure TArrayBufIndex.DoScrollForward;
- begin
- inc(FCurrentRecInd);
- end;
- procedure TArrayBufIndex.StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark);
- begin
- with ABookmark^ do
- begin
- BookmarkInt := FCurrentRecInd;
- BookmarkData := FRecordArray[FCurrentRecInd];
- end;
- end;
- procedure TArrayBufIndex.StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark
- );
- begin
- with ABookmark^ do
- begin
- BookmarkInt := FLastRecInd;
- BookmarkData := FRecordArray[FLastRecInd];
- end;
- end;
- function TArrayBufIndex.GetRecordFromBookmark(ABookmark: TBufBookmark): integer;
- begin
- // ABookmark.BookMarkBuf is nil if SetRecNo calls GotoBookmark
- if (ABookmark.BookmarkData<>nil) and (FRecordArray[ABookmark.BookmarkInt]<>ABookmark.BookmarkData) then
- begin
- // Start searching two records before the expected record
- if ABookmark.BookmarkInt > 2 then
- Result := ABookmark.BookmarkInt-2
- else
- Result := 0;
- while (Result<FLastRecInd) do
- begin
- if (FRecordArray[Result] = ABookmark.BookmarkData) then exit;
- inc(Result);
- end;
- Result:=0;
- while (Result<ABookmark.BookmarkInt) do
- begin
- if (FRecordArray[Result] = ABookmark.BookmarkData) then exit;
- inc(Result);
- end;
- DatabaseError(SInvalidBookmark)
- end
- else
- Result := ABookmark.BookmarkInt;
- end;
- procedure TArrayBufIndex.GotoBookmark(const ABookmark : PBufBookmark);
- begin
- FCurrentRecInd:=GetRecordFromBookmark(ABookmark^);
- end;
- procedure TArrayBufIndex.InitialiseIndex;
- begin
- // FRecordArray:=nil;
- setlength(FRecordArray,FInitialBuffers);
- FCurrentRecInd:=-1;
- FLastRecInd:=-1;
- end;
- procedure TArrayBufIndex.InitialiseSpareRecord(const ASpareRecord: TRecordBuffer);
- begin
- FLastRecInd := 0;
- // FCurrentRecInd := 0;
- FRecordArray[0] := ASpareRecord;
- end;
- procedure TArrayBufIndex.ReleaseSpareRecord;
- begin
- SetLength(FRecordArray,FInitialBuffers);
- end;
- function TArrayBufIndex.GetRecNo: integer;
- begin
- Result := FCurrentRecInd+1;
- end;
- procedure TArrayBufIndex.SetRecNo(ARecNo: Longint);
- begin
- FCurrentRecInd := ARecNo-1;
- end;
- procedure TArrayBufIndex.InsertRecordBeforeCurrentRecord(const ARecord: TRecordBuffer);
- begin
- inc(FLastRecInd);
- if FLastRecInd >= length(FRecordArray) then
- SetLength(FRecordArray,length(FRecordArray)+FGrowBuffer);
- Move(FRecordArray[FCurrentRecInd],FRecordArray[FCurrentRecInd+1],sizeof(Pointer)*(FLastRecInd-FCurrentRecInd));
- FRecordArray[FCurrentRecInd]:=ARecord;
- inc(FCurrentRecInd);
- end;
- procedure TArrayBufIndex.RemoveRecordFromIndex(const ABookmark : TBufBookmark);
- var ARecordInd : integer;
- begin
- ARecordInd:=GetRecordFromBookmark(ABookmark);
- Move(FRecordArray[ARecordInd+1],FRecordArray[ARecordInd],sizeof(Pointer)*(FLastRecInd-ARecordInd));
- dec(FLastRecInd);
- end;
- procedure TArrayBufIndex.BeginUpdate;
- begin
- // inherited BeginUpdate;
- end;
- procedure TArrayBufIndex.AddRecord;
- var ARecord: TRecordBuffer;
- begin
- ARecord := FDataset.IntAllocRecordBuffer;
- inc(FLastRecInd);
- if FLastRecInd >= length(FRecordArray) then
- SetLength(FRecordArray,length(FRecordArray)+FGrowBuffer);
- FRecordArray[FLastRecInd]:=ARecord;
- end;
- procedure TArrayBufIndex.EndUpdate;
- begin
- // inherited EndUpdate;
- end;
- { TDataPacketReader }
- class function TDataPacketReader.RowStateToByte(const ARowState: TRowState
- ): byte;
- var RowStateInt : Byte;
- begin
- RowStateInt:=0;
- if rsvOriginal in ARowState then RowStateInt := RowStateInt+1;
- if rsvDeleted in ARowState then RowStateInt := RowStateInt+2;
- if rsvInserted in ARowState then RowStateInt := RowStateInt+4;
- if rsvUpdated in ARowState then RowStateInt := RowStateInt+8;
- Result := RowStateInt;
- end;
- class function TDataPacketReader.ByteToRowState(const AByte: Byte): TRowState;
- begin
- result := [];
- if (AByte and 1)=1 then Result := Result+[rsvOriginal];
- if (AByte and 2)=2 then Result := Result+[rsvDeleted];
- if (AByte and 4)=4 then Result := Result+[rsvInserted];
- if (AByte and 8)=8 then Result := Result+[rsvUpdated];
- end;
- procedure TDataPacketReader.RestoreBlobField(AField: TField; ASource: pointer; ASize: integer);
- var
- ABufBlobField: TBufBlobField;
- begin
- ABufBlobField.BlobBuffer:=FDataSet.GetNewBlobBuffer;
- ABufBlobField.BlobBuffer^.Size:=ASize;
- ReAllocMem(ABufBlobField.BlobBuffer^.Buffer, ASize);
- move(ASource^, ABufBlobField.BlobBuffer^.Buffer^, ASize);
- AField.SetData(@ABufBlobField);
- end;
- constructor TDataPacketReader.Create(ADataSet: TCustomBufDataset; AStream: TStream);
- begin
- FDataSet := ADataSet;
- FStream := AStream;
- end;
- { TFpcBinaryDatapacketReader }
- constructor TFpcBinaryDatapacketReader.Create(ADataSet: TCustomBufDataset; AStream: TStream);
- begin
- inherited;
- FVersion := 20; // default version 2.0
- end;
- procedure TFpcBinaryDatapacketReader.LoadFieldDefs(var AnAutoIncValue: integer);
- var FldCount : word;
- i : integer;
- s : string;
- begin
- // Identify version
- SetLength(s, 13);
- if (Stream.Read(s[1], 13) = 13) then
- case s of
- FpcBinaryIdent1:
- FVersion := 10;
- FpcBinaryIdent2:
- FVersion := Stream.ReadByte;
- else
- DatabaseError(SStreamNotRecognised);
- end;
- // Read FieldDefs
- FldCount := Stream.ReadWord;
- DataSet.FieldDefs.Clear;
- for i := 0 to FldCount - 1 do with DataSet.FieldDefs.AddFieldDef do
- begin
- Name := Stream.ReadAnsiString;
- Displayname := Stream.ReadAnsiString;
- Size := Stream.ReadWord;
- DataType := TFieldType(Stream.ReadWord);
- if Stream.ReadByte = 1 then
- Attributes := Attributes + [faReadonly];
- end;
- Stream.ReadBuffer(i,sizeof(i));
- AnAutoIncValue := i;
- FNullBitmapSize := (FldCount + 7) div 8;
- SetLength(FNullBitmap, FNullBitmapSize);
- end;
- procedure TFpcBinaryDatapacketReader.StoreFieldDefs(AnAutoIncValue: integer);
- var i : integer;
- begin
- Stream.Write(FpcBinaryIdent2[1], length(FpcBinaryIdent2));
- Stream.WriteByte(FVersion);
- Stream.WriteWord(DataSet.FieldDefs.Count);
- for i := 0 to DataSet.FieldDefs.Count - 1 do with DataSet.FieldDefs[i] do
- begin
- Stream.WriteAnsiString(Name);
- Stream.WriteAnsiString(DisplayName);
- Stream.WriteWord(Size);
- Stream.WriteWord(ord(DataType));
- if faReadonly in Attributes then
- Stream.WriteByte(1)
- else
- Stream.WriteByte(0);
- end;
- i := AnAutoIncValue;
- Stream.WriteBuffer(i,sizeof(i));
- FNullBitmapSize := (DataSet.FieldDefs.Count + 7) div 8;
- SetLength(FNullBitmap, FNullBitmapSize);
- end;
- procedure TFpcBinaryDatapacketReader.InitLoadRecords;
- begin
- // Do nothing
- end;
- function TFpcBinaryDatapacketReader.GetCurrentRecord: boolean;
- var Buf : byte;
- begin
- Result := (Stream.Read(Buf,1)=1) and (Buf=$fe);
- end;
- function TFpcBinaryDatapacketReader.GetRecordRowState(out AUpdOrder : Integer) : TRowState;
- var Buf : byte;
- begin
- Stream.Read(Buf,1);
- Result := ByteToRowState(Buf);
- if Result<>[] then
- Stream.ReadBuffer(AUpdOrder,sizeof(integer))
- else
- AUpdOrder := 0;
- end;
- procedure TFpcBinaryDatapacketReader.GotoNextRecord;
- begin
- // Do Nothing
- end;
- procedure TFpcBinaryDatapacketReader.RestoreRecord;
- var
- AField: TField;
- i: integer;
- L: cardinal;
- B: TBytes;
- begin
- with DataSet do
- case FVersion of
- 10:
- Stream.ReadBuffer(GetCurrentBuffer^, FRecordSize); // Ugly because private members of ADataset are used...
- 20:
- begin
- // Restore field's Null bitmap
- Stream.ReadBuffer(FNullBitmap[0], FNullBitmapSize);
- // Restore field's data
- for i:=0 to FieldDefs.Count-1 do
- begin
- AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
- if AField=nil then continue;
- if GetFieldIsNull(PByte(FNullBitmap), i) then
- AField.SetData(nil)
- else if AField.DataType in StringFieldTypes then
- AField.AsString := Stream.ReadAnsiString
- else
- begin
- if AField.DataType in VarLenFieldTypes then
- L := Stream.ReadDWord
- else
- L := AField.DataSize;
- SetLength(B, L);
- if L > 0 then
- Stream.ReadBuffer(B[0], L);
- if AField.DataType in BlobFieldTypes then
- RestoreBlobField(AField, @B[0], L)
- else
- AField.SetData(@B[0], False); // set it to the FilterBuffer
- end;
- end;
- end;
- end;
- end;
- procedure TFpcBinaryDatapacketReader.StoreRecord(ARowState: TRowState; AUpdOrder : integer);
- var
- AField: TField;
- i: integer;
- L: cardinal;
- B: TBytes;
- begin
- // Record header
- Stream.WriteByte($fe);
- Stream.WriteByte(RowStateToByte(ARowState));
- if ARowState<>[] then
- Stream.WriteBuffer(AUpdOrder,sizeof(integer));
- // Record data
- with DataSet do
- case FVersion of
- 10:
- Stream.WriteBuffer(GetCurrentBuffer^, FRecordSize); // Old 1.0 version
- 20:
- begin
- // store fields Null bitmap
- FillByte(FNullBitmap[0], FNullBitmapSize, 0);
- for i:=0 to FieldDefs.Count-1 do
- begin
- AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
- if assigned(AField) and AField.IsNull then
- SetFieldIsNull(PByte(FNullBitmap), i);
- end;
- Stream.WriteBuffer(FNullBitmap[0], FNullBitmapSize);
- for i:=0 to FieldDefs.Count-1 do
- begin
- AField := Fields.FieldByNumber(FieldDefs[i].FieldNo);
- if not assigned(AField) or AField.IsNull then continue;
- if AField.DataType in StringFieldTypes then
- Stream.WriteAnsiString(AField.AsString)
- else
- begin
- B := AField.AsBytes;
- L := length(B);
- if AField.DataType in VarLenFieldTypes then
- Stream.WriteDWord(L);
- if L > 0 then
- Stream.WriteBuffer(B[0], L);
- end;
- end;
- end;
- end;
- end;
- procedure TFpcBinaryDatapacketReader.FinalizeStoreRecords;
- begin
- // Do nothing
- end;
- class function TFpcBinaryDatapacketReader.RecognizeStream(AStream: TStream): boolean;
- var s : string;
- begin
- SetLength(s, 13);
- if (AStream.Read(s[1], 13) = 13) then
- case s of
- FpcBinaryIdent1,
- FpcBinaryIdent2:
- Result := True;
- else
- Result := False;
- end;
- end;
- { TUniDirectionalBufIndex }
- function TUniDirectionalBufIndex.GetBookmarkSize: integer;
- begin
- // In principle there are no bookmarks, and the size should be 0.
- // But there is quite some code in TCustomBufDataset that relies on
- // an existing bookmark of the TBufBookmark type.
- // This code could be moved to the TBufIndex but that would make things
- // more complicated and probably slower. So use a 'fake' bookmark of
- // size TBufBookmark.
- // When there are other TBufIndexes which also need special bookmark code
- // this can be adapted.
- Result:=sizeof(TBufBookmark);
- end;
- function TUniDirectionalBufIndex.GetCurrentBuffer: Pointer;
- begin
- result := FSPareBuffer;
- end;
- function TUniDirectionalBufIndex.GetCurrentRecord: TRecordBuffer;
- begin
- // Result:=inherited GetCurrentRecord;
- end;
- function TUniDirectionalBufIndex.GetIsInitialized: boolean;
- begin
- Result := Assigned(FSPareBuffer);
- end;
- function TUniDirectionalBufIndex.GetSpareBuffer: TRecordBuffer;
- begin
- result := FSPareBuffer;
- end;
- function TUniDirectionalBufIndex.GetSpareRecord: TRecordBuffer;
- begin
- result := FSPareBuffer;
- end;
- function TUniDirectionalBufIndex.ScrollBackward: TGetResult;
- begin
- result := grError;
- end;
- function TUniDirectionalBufIndex.ScrollForward: TGetResult;
- begin
- result := grOk;
- end;
- function TUniDirectionalBufIndex.GetCurrent: TGetResult;
- begin
- result := grOk;
- end;
- function TUniDirectionalBufIndex.ScrollFirst: TGetResult;
- begin
- Result:=grError;
- end;
- procedure TUniDirectionalBufIndex.ScrollLast;
- begin
- DatabaseError(SUniDirectional);
- end;
- procedure TUniDirectionalBufIndex.SetToFirstRecord;
- begin
- // for UniDirectional datasets should be [Internal]First valid method call
- // do nothing
- end;
- procedure TUniDirectionalBufIndex.SetToLastRecord;
- begin
- DatabaseError(SUniDirectional);
- end;
- procedure TUniDirectionalBufIndex.StoreCurrentRecord;
- begin
- DatabaseError(SUniDirectional);
- end;
- procedure TUniDirectionalBufIndex.RestoreCurrentRecord;
- begin
- DatabaseError(SUniDirectional);
- end;
- function TUniDirectionalBufIndex.CanScrollForward: Boolean;
- begin
- // should return true if next record is already fetched
- result := false;
- end;
- procedure TUniDirectionalBufIndex.DoScrollForward;
- begin
- // do nothing
- end;
- procedure TUniDirectionalBufIndex.StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark);
- begin
- // do nothing
- end;
- procedure TUniDirectionalBufIndex.StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark);
- begin
- // do nothing
- end;
- procedure TUniDirectionalBufIndex.GotoBookmark(const ABookmark: PBufBookmark);
- begin
- DatabaseError(SUniDirectional);
- end;
- procedure TUniDirectionalBufIndex.InitialiseIndex;
- begin
- // do nothing
- end;
- procedure TUniDirectionalBufIndex.InitialiseSpareRecord(const ASpareRecord: TRecordBuffer);
- begin
- FSPareBuffer:=ASpareRecord;
- end;
- procedure TUniDirectionalBufIndex.ReleaseSpareRecord;
- begin
- FSPareBuffer:=nil;
- end;
- function TUniDirectionalBufIndex.GetRecNo: Longint;
- begin
- Result := -1;
- end;
- procedure TUniDirectionalBufIndex.SetRecNo(ARecNo: Longint);
- begin
- DatabaseError(SUniDirectional);
- end;
- procedure TUniDirectionalBufIndex.BeginUpdate;
- begin
- // Do nothing
- end;
- procedure TUniDirectionalBufIndex.AddRecord;
- begin
- // Do nothing
- end;
- procedure TUniDirectionalBufIndex.InsertRecordBeforeCurrentRecord(const ARecord: TRecordBuffer);
- begin
- // Do nothing
- end;
- procedure TUniDirectionalBufIndex.RemoveRecordFromIndex(const ABookmark: TBufBookmark);
- begin
- DatabaseError(SUniDirectional);
- end;
- procedure TUniDirectionalBufIndex.OrderCurrentRecord;
- begin
- // Do nothing
- end;
- procedure TUniDirectionalBufIndex.EndUpdate;
- begin
- // Do nothing
- end;
- initialization
- setlength(RegisteredDatapacketReaders,0);
- finalization
- setlength(RegisteredDatapacketReaders,0);
- end.
|