dbf_idxfile.pas 112 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071
  1. unit dbf_idxfile;
  2. interface
  3. {$I Dbf_Common.inc}
  4. uses
  5. {$ifdef WIN32}
  6. Windows,
  7. {$else}
  8. {$ifdef KYLIX}
  9. Libc,
  10. {$endif}
  11. Types, Dbf_Wtil,
  12. {$endif}
  13. SysUtils,
  14. Classes,
  15. Db,
  16. Dbf_PgFile,
  17. {$ifdef USE_CACHE}
  18. Dbf_PgcFile,
  19. {$endif}
  20. Dbf_Parser,
  21. Dbf_PrsDef,
  22. Dbf_Cursor,
  23. Dbf_Common;
  24. {$ifdef _DEBUG}
  25. {$define TDBF_INDEX_CHECK}
  26. {$endif}
  27. {$ifdef _ASSERTS}
  28. {$define TDBF_INDEX_CHECK}
  29. {$endif}
  30. const
  31. MaxIndexes = 47;
  32. type
  33. TIndexPage = class;
  34. TIndexTag = class;
  35. TIndexUpdateMode = (umAll, umCurrent);
  36. TLocaleError = (leNone, leUnknown, leTableIndexMismatch, leNotAvailable);
  37. TLocaleSolution = (lsNotOpen, lsNoEdit, lsBinary);
  38. TIndexUniqueType = (iuNormal, iuUnique, iuDistinct);
  39. TIndexModifyMode = (mmNormal, mmDeleteRecall);
  40. TDbfLocaleErrorEvent = procedure(var Error: TLocaleError; var Solution: TLocaleSolution) of object;
  41. TDbfCompareKeyEvent = function(Key: PChar): Integer of object;
  42. TDbfCompareKeysEvent = function(Key1, Key2: PChar): Integer of object;
  43. PDouble = ^Double;
  44. PInteger = ^Integer;
  45. //===========================================================================
  46. TDbfIndexDef = class;
  47. TDbfIndexDef = class(TCollectionItem)
  48. protected
  49. FIndexName: string;
  50. FExpression: string;
  51. FOptions: TIndexOptions;
  52. FTemporary: Boolean; // added at runtime
  53. procedure SetIndexName(NewName: string);
  54. procedure SetExpression(NewField: string);
  55. public
  56. constructor Create(Collection: TCollection); override;
  57. destructor Destroy; override;
  58. procedure Assign(Source: TPersistent); override;
  59. property Temporary: Boolean read FTemporary write FTemporary;
  60. property Name: string read FIndexName write SetIndexName;
  61. property Expression: string read FExpression write SetExpression;
  62. published
  63. property IndexFile: string read FIndexName write SetIndexName;
  64. property SortField: string read FExpression write SetExpression;
  65. property Options: TIndexOptions read FOptions write FOptions;
  66. end;
  67. //===========================================================================
  68. TIndexFile = class;
  69. TIndexPageClass = class of TIndexPage;
  70. TIndexPage = class(TObject)
  71. protected
  72. FIndexFile: TIndexFile;
  73. FLowerPage: TIndexPage;
  74. FUpperPage: TIndexPage;
  75. FPageBuffer: Pointer;
  76. FEntry: Pointer;
  77. FEntryNo: Integer;
  78. FLockCount: Integer;
  79. FModified: Boolean;
  80. FPageNo: Integer;
  81. FWeight: Integer;
  82. // bracket props
  83. FLowBracket: Integer; // = FLowIndex if FPageNo = FLowPage
  84. FLowIndex: Integer;
  85. FLowPage: Integer;
  86. FHighBracket: Integer; // = FHighIndex if FPageNo = FHighPage
  87. FHighIndex: Integer;
  88. FHighPage: Integer;
  89. procedure LocalInsert(RecNo: Integer; Buffer: PChar; LowerPageNo: Integer);
  90. procedure LocalDelete;
  91. procedure Delete;
  92. procedure SyncLowerPage;
  93. procedure WritePage;
  94. procedure Split;
  95. procedure LockPage;
  96. procedure UnlockPage;
  97. function RecurPrev: Boolean;
  98. function RecurNext: Boolean;
  99. procedure RecurFirst;
  100. procedure RecurLast;
  101. procedure SetEntry(RecNo: Integer; key: PChar; LowerPageNo: Integer);
  102. procedure SetEntryNo(value: Integer);
  103. procedure SetPageNo(NewPageNo: Integer);
  104. procedure SetLowPage(NewPage: Integer);
  105. procedure SetHighPage(NewPage: Integer);
  106. procedure SetUpperPage(NewPage: TIndexPage);
  107. procedure UpdateBounds(IsInnerNode: Boolean);
  108. protected
  109. function GetEntry(AEntryNo: Integer): Pointer; virtual; abstract;
  110. function GetLowerPageNo: Integer; virtual; abstract;
  111. function GetKeyData: PChar; virtual; abstract;
  112. function GetNumEntries: Integer; virtual; abstract;
  113. function GetKeyDataFromEntry(AEntry: Integer): PChar; virtual; abstract;
  114. function GetRecNo: Integer; virtual; abstract;
  115. function GetIsInnerNode: Boolean; virtual; abstract;
  116. procedure IncNumEntries; virtual; abstract;
  117. procedure SetNumEntries(NewNum: Integer); virtual; abstract;
  118. procedure SetRecLowerPageNo(NewRecNo, NewPageNo: Integer); virtual; abstract;
  119. procedure SetRecLowerPageNoOfEntry(AEntry, NewRecNo, NewPageNo: Integer); virtual; abstract;
  120. {$ifdef TDBF_UPDATE_FIRST_LAST_NODE}
  121. procedure SetPrevBlock(NewBlock: Integer); virtual;
  122. {$endif}
  123. public
  124. constructor Create(Parent: TIndexFile);
  125. destructor Destroy; override;
  126. function FindNearest(ARecNo: Integer): Integer;
  127. function PhysicalRecNo: Integer;
  128. function MatchKey: Integer;
  129. procedure GotoInsertEntry;
  130. procedure Clear;
  131. procedure GetNewPage;
  132. procedure Modified;
  133. procedure RecalcWeight;
  134. procedure UpdateWeight;
  135. procedure Flush;
  136. property Key: PChar read GetKeyData;
  137. property Entry: Pointer read FEntry;
  138. property EntryNo: Integer read FEntryNo write SetEntryNo;
  139. property IndexFile: TIndexFile read FIndexFile;
  140. property UpperPage: TIndexPage read FUpperPage write SetUpperPage;
  141. property LowerPage: TIndexPage read FLowerPage;
  142. // property LowerPageNo: Integer read GetLowerPageNo; // never used
  143. property PageBuffer: Pointer read FPageBuffer;
  144. property PageNo: Integer read FPageNo write SetPageNo;
  145. property Weight: Integer read FWeight;
  146. property NumEntries: Integer read GetNumEntries;
  147. property HighBracket: Integer read FHighBracket write FHighBracket;
  148. property HighIndex: Integer read FHighIndex;
  149. property HighPage: Integer read FHighPage write SetHighPage;
  150. property LowBracket: Integer read FLowBracket write FLowBracket;
  151. property LowIndex: Integer read FLowIndex;
  152. property LowPage: Integer read FLowPage write SetLowPage;
  153. end;
  154. //===========================================================================
  155. TIndexTag = class(TObject)
  156. private
  157. FTag: Pointer;
  158. protected
  159. function GetHeaderPageNo: Integer; virtual; abstract;
  160. function GetTagName: string; virtual; abstract;
  161. function GetKeyFormat: Byte; virtual; abstract;
  162. function GetForwardTag1: Byte; virtual; abstract;
  163. function GetForwardTag2: Byte; virtual; abstract;
  164. function GetBackwardTag: Byte; virtual; abstract;
  165. function GetReserved: Byte; virtual; abstract;
  166. function GetKeyType: Char; virtual; abstract;
  167. procedure SetHeaderPageNo(NewPageNo: Integer); virtual; abstract;
  168. procedure SetTagName(NewName: string); virtual; abstract;
  169. procedure SetKeyFormat(NewFormat: Byte); virtual; abstract;
  170. procedure SetForwardTag1(NewTag: Byte); virtual; abstract;
  171. procedure SetForwardTag2(NewTag: Byte); virtual; abstract;
  172. procedure SetBackwardTag(NewTag: Byte); virtual; abstract;
  173. procedure SetReserved(NewReserved: Byte); virtual; abstract;
  174. procedure SetKeyType(NewType: Char); virtual; abstract;
  175. public
  176. property HeaderPageNo: Integer read GetHeaderPageNo write SetHeaderPageNo;
  177. property TagName: string read GetTagName write SetTagName;
  178. property KeyFormat: Byte read GetKeyFormat write SetKeyFormat;
  179. property ForwardTag1: Byte read GetForwardTag1 write SetForwardTag1;
  180. property ForwardTag2: Byte read GetForwardTag2 write SetForwardTag2;
  181. property BackwardTag: Byte read GetBackwardTag write SetBackwardTag;
  182. property Reserved: Byte read GetReserved write SetReserved;
  183. property KeyType: Char read GetKeyType write SetKeyType;
  184. property Tag: Pointer read FTag write FTag;
  185. end;
  186. //===========================================================================
  187. {$ifdef USE_CACHE}
  188. TIndexFile = class(TCachedFile)
  189. {$else}
  190. TIndexFile = class(TPagedFile)
  191. {$endif}
  192. protected
  193. FIndexName: string;
  194. FParsers: array[0..MaxIndexes-1] of TDbfParser;
  195. FIndexHeaders: array[0..MaxIndexes-1] of Pointer;
  196. FHeaderModified: array[0..MaxIndexes-1] of Boolean;
  197. FIndexHeader: Pointer;
  198. FIndexVersion: TXBaseVersion;
  199. FRoots: array[0..MaxIndexes-1] of TIndexPage;
  200. FLeaves: array[0..MaxIndexes-1] of TIndexPage;
  201. FCurrentParser: TDbfParser;
  202. FRoot: TIndexPage;
  203. FLeaf: TIndexPage;
  204. FMdxTag: TIndexTag;
  205. FTempMdxTag: TIndexTag;
  206. FEntryHeaderSize: Integer;
  207. FPageHeaderSize: Integer;
  208. FTagSize: Integer;
  209. FTagOffset: Integer;
  210. FHeaderPageNo: Integer;
  211. FSelectedIndex: Integer;
  212. FIsDescending: Boolean;
  213. FUniqueMode: TIndexUniqueType;
  214. FModifyMode: TIndexModifyMode;
  215. FHeaderLocked: Integer; // used to remember which header page we have locked
  216. FKeyBuffer: array[0..100] of Char;
  217. FLowBuffer: array[0..100] of Char;
  218. FHighBuffer: array[0..100] of Char;
  219. FEntryBof: Pointer;
  220. FEntryEof: Pointer;
  221. FDbfFile: Pointer;
  222. FCanEdit: Boolean;
  223. FOpened: Boolean;
  224. FRangeActive: Boolean;
  225. FUpdateMode: TIndexUpdateMode;
  226. FUserKey: PChar; // find / insert key
  227. FUserRecNo: Integer; // find / insert recno
  228. FUserBCD: array[0..10] of Byte;
  229. FUserNumeric: Double;
  230. FForceClose: Boolean;
  231. FForceReadOnly: Boolean;
  232. FLocaleID: LCID;
  233. FLocaleCP: Integer;
  234. FCodePage: Integer;
  235. FCompareKey: TDbfCompareKeyEvent;
  236. FCompareKeys: TDbfCompareKeysEvent;
  237. FOnLocaleError: TDbfLocaleErrorEvent;
  238. function GetNewPageNo: Integer;
  239. procedure TouchHeader(AHeader: Pointer);
  240. function CreateTempFile(BaseName: string): TPagedFile;
  241. procedure WriteIndexHeader(AIndex: Integer);
  242. procedure SelectIndexVars(AIndex: Integer);
  243. procedure CalcKeyProperties;
  244. procedure UpdateIndexProperties;
  245. procedure ClearRoots;
  246. function CalcTagOffset(AIndex: Integer): Pointer;
  247. function FindKey(Insert: boolean): Integer;
  248. procedure InsertKey(Buffer: PChar);
  249. procedure DeleteKey(Buffer: PChar);
  250. procedure InsertCurrent;
  251. procedure DeleteCurrent;
  252. procedure UpdateCurrent(PrevBuffer, NewBuffer: PChar);
  253. procedure ReadIndexes;
  254. procedure Resync(Relative: boolean);
  255. procedure ResyncRoot;
  256. procedure ResyncTree;
  257. procedure ResyncRange(KeepPosition: boolean);
  258. procedure ResetRange;
  259. procedure SetBracketLow;
  260. procedure SetBracketHigh;
  261. procedure WalkFirst;
  262. procedure WalkLast;
  263. function WalkPrev: boolean;
  264. function WalkNext: boolean;
  265. procedure TranslateToANSI(Src, Dest: PChar);
  266. function CompareKeyNumericNDX(Key: PChar): Integer;
  267. function CompareKeyNumericMDX(Key: PChar): Integer;
  268. function CompareKeyString(Key: PChar): Integer;
  269. function CompareKeysNumericNDX(Key1, Key2: PChar): Integer;
  270. function CompareKeysNumericMDX(Key1, Key2: PChar): Integer;
  271. function CompareKeysString(Key1, Key2: PChar): Integer;
  272. // property functions
  273. function GetName: string;
  274. function GetDbfLanguageId: Byte;
  275. function GetKeyLen: Integer;
  276. function GetKeyType: Char;
  277. // function GetIndexCount Integer;
  278. function GetExpression: string;
  279. function GetPhysicalRecNo: Integer;
  280. function GetSequentialRecNo: Integer;
  281. function GetSequentialRecordCount: Integer;
  282. procedure SetSequentialRecNo(RecNo: Integer);
  283. procedure SetPhysicalRecNo(RecNo: Integer);
  284. procedure SetUpdateMode(NewMode: TIndexUpdateMode);
  285. procedure SetIndexName(const AIndexName: string);
  286. procedure SetLocaleID(const NewID: LCID);
  287. property InternalLocaleID: LCID read FLocaleID write SetLocaleID;
  288. public
  289. constructor Create(ADbfFile: Pointer);
  290. destructor Destroy; override;
  291. procedure Open;
  292. procedure Close;
  293. procedure Clear;
  294. procedure Flush; override;
  295. procedure ClearIndex;
  296. procedure AddNewLevel;
  297. procedure UnlockHeader;
  298. procedure InsertError;
  299. procedure Insert(RecNo: Integer; Buffer: PChar);
  300. procedure Update(RecNo: Integer; PrevBuffer, NewBuffer: PChar);
  301. procedure Delete(RecNo: Integer; Buffer: PChar);
  302. function CheckKeyViolation(Buffer: PChar): Boolean;
  303. procedure RecordDeleted(RecNo: Integer; Buffer: PChar);
  304. procedure RecordRecalled(RecNo: Integer; Buffer: PChar);
  305. procedure DeleteIndex(const AIndexName: string);
  306. procedure RepageFile;
  307. procedure CompactFile;
  308. procedure CreateIndex(FieldDesc, TagName: string; Options: TIndexOptions);
  309. function ExtractKeyFromBuffer(Buffer: PChar): PChar;
  310. function SearchKey(Key: PChar; SearchType: TSearchKeyType): Boolean;
  311. function Find(RecNo: Integer; Buffer: PChar): Integer;
  312. function IndexOf(const AIndexName: string): Integer;
  313. procedure GetIndexNames(const AList: TStrings);
  314. procedure GetIndexInfo(const AIndexName: string; IndexDef: TDbfIndexDef);
  315. procedure WriteHeader; override;
  316. procedure WriteFileHeader;
  317. procedure First;
  318. procedure Last;
  319. function Next: Boolean;
  320. function Prev: Boolean;
  321. procedure SetRange(LowRange, HighRange: PChar);
  322. procedure CancelRange;
  323. function MatchKey(UserKey: PChar): Integer;
  324. function CompareKey(Key: PChar): Integer;
  325. function CompareKeys(Key1, Key2: PChar): Integer;
  326. function PrepareKey(Buffer: PChar; ResultType: TExpressionType): PChar;
  327. property KeyLen: Integer read GetKeyLen;
  328. property IndexVersion: TXBaseVersion read FIndexVersion;
  329. property EntryHeaderSize: Integer read FEntryHeaderSize;
  330. property KeyType: Char read GetKeyType;
  331. property SequentialRecordCount: Integer read GetSequentialRecordCount;
  332. property SequentialRecNo: Integer read GetSequentialRecNo write SetSequentialRecNo;
  333. property PhysicalRecNo: Integer read GetPhysicalRecNo write SetPhysicalRecNo;
  334. property HeaderPageNo: Integer read FHeaderPageNo;
  335. property IndexHeader: Pointer read FIndexHeader;
  336. property EntryBof: Pointer read FEntryBof;
  337. property EntryEof: Pointer read FEntryEof;
  338. property UniqueMode: TIndexUniqueType read FUniqueMode;
  339. property IsDescending: Boolean read FIsDescending;
  340. property UpdateMode: TIndexUpdateMode read FUpdateMode write SetUpdateMode;
  341. property IndexName: string read FIndexName write SetIndexName;
  342. property Expression: string read GetExpression;
  343. // property Count: Integer read GetIndexCount;
  344. property ForceClose: Boolean read FForceClose;
  345. property ForceReadOnly: Boolean read FForceReadOnly;
  346. property LocaleID: LCID read FLocaleID;
  347. property CodePage: Integer read FCodePage write FCodePage;
  348. property OnLocaleError: TDbfLocaleErrorEvent read FOnLocaleError write FOnLocaleError;
  349. end;
  350. //------------------------------------------------------------------------------
  351. implementation
  352. uses
  353. Dbf_DbfFile,
  354. Dbf_Fields,
  355. Dbf_Str,
  356. Dbf_Lang;
  357. const
  358. RecBOF = 0;
  359. RecEOF = MaxInt;
  360. lcidBinary = $0A03;
  361. KeyFormat_Expression = $00;
  362. KeyFormat_Data = $10;
  363. KeyFormat_Descending = $08;
  364. KeyFormat_String = $10;
  365. KeyFormat_Distinct = $20;
  366. KeyFormat_Unique = $40;
  367. Unique_None = $00;
  368. Unique_Unique = $01;
  369. Unique_Distinct = $21;
  370. type
  371. TLCIDList = class(TList)
  372. public
  373. constructor Create;
  374. procedure Enumerate;
  375. end;
  376. PMdxHdr = ^rMdxHdr;
  377. rMdxHdr = record
  378. MdxVersion : Byte; // 0
  379. Year : Byte; // 1
  380. Month : Byte; // 2
  381. Day : Byte; // 3
  382. FileName : array[0..15] of Char; // 4..19
  383. BlockSize : Word; // 20..21
  384. BlockAdder : Word; // 22..23
  385. ProdFlag : Byte; // 24
  386. NumTags : Byte; // 25
  387. TagSize : Byte; // 26
  388. Dummy1 : Byte; // 27
  389. TagsUsed : Word; // 28..29
  390. Dummy2 : Byte; // 30
  391. Language : Byte; // 31
  392. NumPages : Integer; // 32..35
  393. FreePage : Integer; // 36..39
  394. BlockFree : Integer; // 40..43
  395. UpdYear : Byte; // 44
  396. UpdMonth : Byte; // 45
  397. UpdDay : Byte; // 46
  398. Reserved : array[0..481] of Byte; // 47..528
  399. TagFlag : Byte; // 529 // dunno what this means but it ought to be 1 :-)
  400. end;
  401. // Tags -> I don't know what to with them
  402. // KeyType -> Variable position, db7 different from db4
  403. PMdx4Tag = ^rMdx4Tag;
  404. rMdx4Tag = record
  405. HeaderPageNo : Integer; // 0..3
  406. TagName : array [0..10] of Char; // 4..14 of Byte
  407. KeyFormat : Byte; // 15 00h: Calculated
  408. // 10h: Data Field
  409. ForwardTag1 : Byte; // 16
  410. ForwardTag2 : Byte; // 17
  411. BackwardTag : Byte; // 18
  412. Reserved : Byte; // 19
  413. KeyType : Char; // 20 C : Character
  414. // N : Numerical
  415. // D : Date
  416. end;
  417. PMdx7Tag = ^rMdx7Tag;
  418. rMdx7Tag = record
  419. HeaderPageNo : Integer; // 0..3
  420. TagName : array [0..32] of Char; // 4..36 of Byte
  421. KeyFormat : Byte; // 37 00h: Calculated
  422. // 10h: Data Field
  423. ForwardTag1 : Byte; // 38
  424. ForwardTag2 : Byte; // 39
  425. BackwardTag : Byte; // 40
  426. Reserved : Byte; // 41
  427. KeyType : Char; // 42 C : Character
  428. // N : Numerical
  429. // D : Date
  430. end;
  431. PIndexHdr = ^rIndexHdr;
  432. rIndexHdr = record
  433. RootPage : Integer; // 0..3
  434. NumPages : Integer; // 4..7
  435. KeyFormat : Byte; // 8 00h: Right, Left, DTOC
  436. // 08h: Descending order
  437. // 10h: String
  438. // 20h: Distinct
  439. // 40h: Unique
  440. KeyType : Char; // 9 C : Character
  441. // N : Numerical
  442. // D : Date
  443. Dummy : Word; // 10..11
  444. KeyLen : Word; // 12..13
  445. NumKeys : Word; // 14..15
  446. sKeyType : Word; // 16..17 00h: DB4: C/N; DB3: C
  447. // 01h: DB4: D ; DB3: N/D
  448. KeyRecLen : Word; // 18..19 Length of key entry in page
  449. Version : Word; // 20..21
  450. Dummy2 : Byte; // 22
  451. Unique : Byte; // 23
  452. KeyDesc : array [0..219] of Char; // 24..243
  453. Dummy3 : Byte; // 244
  454. ForExist : Byte; // 245
  455. KeyExist : Byte; // 246
  456. FirstNode : Longint; // 248..251 first node that contains data
  457. LastNode : Longint; // 252..255 last node that contains data
  458. // MDX Header has here a 506 byte block reserved
  459. // and then the FILTER expression, which obviously doesn't
  460. // fit in a NDX page, so we'll skip it
  461. end;
  462. PMdxEntry = ^rMdxEntry;
  463. rMdxEntry = record
  464. RecBlockNo: Longint; // 0..3 either recno or blockno
  465. KeyData : Char; // 4.. first byte of data, context => length
  466. end;
  467. PMdxPage = ^rMdxPage;
  468. rMdxPage = record
  469. NumEntries : Integer;
  470. PrevBlock : Integer;
  471. FirstEntry : rMdxEntry;
  472. end;
  473. PNdxEntry = ^rNdxEntry;
  474. rNdxEntry = record
  475. LowerPageNo: Integer; // 0..3 lower page
  476. RecNo : Integer; // 4..7 recno
  477. KeyData : Char;
  478. end;
  479. PNdxPage = ^rNdxPage;
  480. rNdxPage = record
  481. NumEntries: Integer; // 0..3
  482. FirstEntry: rNdxEntry;
  483. end;
  484. //---------------------------------------------------------------------------
  485. TMdxPage = class(TIndexPage)
  486. protected
  487. function GetEntry(AEntryNo: Integer): Pointer; override;
  488. function GetLowerPageNo: Integer; override;
  489. function GetKeyData: PChar; override;
  490. function GetNumEntries: Integer; override;
  491. function GetKeyDataFromEntry(AEntry: Integer): PChar; override;
  492. function GetRecNo: Integer; override;
  493. function GetIsInnerNode: Boolean; override;
  494. procedure IncNumEntries; override;
  495. procedure SetNumEntries(NewNum: Integer); override;
  496. procedure SetRecLowerPageNo(NewRecNo, NewPageNo: Integer); override;
  497. procedure SetRecLowerPageNoOfEntry(AEntry, NewRecNo, NewPageNo: Integer); override;
  498. {$ifdef TDBF_UPDATE_FIRST_LAST_NODE}
  499. procedure SetPrevBlock(NewBlock: Integer); override;
  500. {$endif}
  501. end;
  502. //---------------------------------------------------------------------------
  503. TNdxPage = class(TIndexPage)
  504. protected
  505. function GetEntry(AEntryNo: Integer): Pointer; override;
  506. function GetLowerPageNo: Integer; override;
  507. function GetKeyData: PChar; override;
  508. function GetNumEntries: Integer; override;
  509. function GetKeyDataFromEntry(AEntry: Integer): PChar; override;
  510. function GetRecNo: Integer; override;
  511. function GetIsInnerNode: Boolean; override;
  512. procedure IncNumEntries; override;
  513. procedure SetNumEntries(NewNum: Integer); override;
  514. procedure SetRecLowerPageNo(NewRecNo, NewPageNo: Integer); override;
  515. procedure SetRecLowerPageNoOfEntry(AEntry, NewRecNo, NewPageNo: Integer); override;
  516. end;
  517. //---------------------------------------------------------------------------
  518. TMdx4Tag = class(TIndexTag)
  519. protected
  520. function GetHeaderPageNo: Integer; override;
  521. function GetTagName: string; override;
  522. function GetKeyFormat: Byte; override;
  523. function GetForwardTag1: Byte; override;
  524. function GetForwardTag2: Byte; override;
  525. function GetBackwardTag: Byte; override;
  526. function GetReserved: Byte; override;
  527. function GetKeyType: Char; override;
  528. procedure SetHeaderPageNo(NewPageNo: Integer); override;
  529. procedure SetTagName(NewName: string); override;
  530. procedure SetKeyFormat(NewFormat: Byte); override;
  531. procedure SetForwardTag1(NewTag: Byte); override;
  532. procedure SetForwardTag2(NewTag: Byte); override;
  533. procedure SetBackwardTag(NewTag: Byte); override;
  534. procedure SetReserved(NewReserved: Byte); override;
  535. procedure SetKeyType(NewType: Char); override;
  536. end;
  537. //---------------------------------------------------------------------------
  538. TMdx7Tag = class(TIndexTag)
  539. function GetHeaderPageNo: Integer; override;
  540. function GetTagName: string; override;
  541. function GetKeyFormat: Byte; override;
  542. function GetForwardTag1: Byte; override;
  543. function GetForwardTag2: Byte; override;
  544. function GetBackwardTag: Byte; override;
  545. function GetReserved: Byte; override;
  546. function GetKeyType: Char; override;
  547. procedure SetHeaderPageNo(NewPageNo: Integer); override;
  548. procedure SetTagName(NewName: string); override;
  549. procedure SetKeyFormat(NewFormat: Byte); override;
  550. procedure SetForwardTag1(NewTag: Byte); override;
  551. procedure SetForwardTag2(NewTag: Byte); override;
  552. procedure SetBackwardTag(NewTag: Byte); override;
  553. procedure SetReserved(NewReserved: Byte); override;
  554. procedure SetKeyType(NewType: Char); override;
  555. end;
  556. var
  557. Entry_Mdx_BOF: rMdxEntry; //(RecBOF, #0);
  558. Entry_Mdx_EOF: rMdxEntry; //(RecBOF, #0);
  559. Entry_Ndx_BOF: rNdxEntry; //(0, RecBOF, #0);
  560. Entry_Ndx_EOF: rNdxEntry; //(0, RecEOF, #0);
  561. LCIDList: TLCIDList;
  562. //==========================================================
  563. // Locale support for all versions of Delphi/C++Builder
  564. function LocaleCallBack(LocaleString: PChar): Integer; stdcall;
  565. begin
  566. LCIDList.Add(Pointer(StrToInt('$'+LocaleString)));
  567. Result := 1;
  568. end;
  569. constructor TLCIDList.Create;
  570. begin
  571. inherited;
  572. end;
  573. procedure TLCIDList.Enumerate;
  574. begin
  575. Clear;
  576. EnumSystemLocales(@LocaleCallBack, LCID_SUPPORTED);
  577. end;
  578. //==========================================================
  579. //============ TIndexPage
  580. //==========================================================
  581. constructor TIndexPage.Create(Parent: TIndexFile);
  582. begin
  583. FIndexFile := Parent;
  584. GetMem(FPageBuffer, FIndexFile.RecordSize);
  585. FLowerPage := nil;
  586. Clear;
  587. end;
  588. destructor TIndexPage.Destroy;
  589. begin
  590. // no locks anymore?
  591. assert(FLockCount = 0);
  592. if (FLowerPage<>nil) then
  593. LowerPage.Free;
  594. WritePage;
  595. FreeMemAndNil(FPageBuffer);
  596. inherited Destroy;
  597. end;
  598. procedure TIndexPage.Clear;
  599. begin
  600. FillChar(PChar(FPageBuffer)^, FIndexFile.RecordSize, 0);
  601. FreeAndNil(FLowerPage);
  602. FUpperPage := nil;
  603. FPageNo := -1;
  604. FEntryNo := -1;
  605. FWeight := 1;
  606. FModified := false;
  607. FEntry := FIndexFile.EntryBof;
  608. FLowPage := 0;
  609. FHighPage := 0;
  610. FLowIndex := 0;
  611. FHighIndex := -1;
  612. FLockCount := 0;
  613. end;
  614. procedure TIndexPage.GetNewPage;
  615. begin
  616. FPageNo := FIndexFile.GetNewPageNo;
  617. end;
  618. procedure TIndexPage.Modified;
  619. begin
  620. FModified := true;
  621. end;
  622. procedure TIndexPage.LockPage;
  623. begin
  624. // already locked?
  625. if FLockCount = 0 then
  626. FIndexFile.LockPage(FPageNo, true);
  627. // increase count
  628. inc(FLockCount);
  629. end;
  630. procedure TIndexPage.UnlockPage;
  631. begin
  632. // still in domain?
  633. assert(FLockCount > 0);
  634. dec(FLockCount);
  635. // unlock?
  636. if FLockCount = 0 then
  637. begin
  638. if FIndexFile.NeedLocks then
  639. WritePage;
  640. FIndexFile.UnlockPage(FPageNo);
  641. end;
  642. end;
  643. procedure TIndexPage.LocalInsert(RecNo: Integer; Buffer: PChar; LowerPageNo: Integer);
  644. // *) assumes there is at least one entry free
  645. var
  646. source, dest: Pointer;
  647. size, numEntries, numKeysAvail: Integer;
  648. begin
  649. // lock page if needed; wait if not available, anyone else updating?
  650. LockPage;
  651. // check assertions
  652. numEntries := GetNumEntries;
  653. // if this is inner node, we can only store one less than max entries
  654. numKeysAvail := PIndexHdr(FIndexFile.IndexHeader).NumKeys - numEntries;
  655. if FLowerPage <> nil then
  656. dec(numKeysAvail);
  657. // check if free space
  658. assert(numKeysAvail > 0);
  659. // first free up some space
  660. source := FEntry;
  661. dest := GetEntry(FEntryNo + 1);
  662. size := (numEntries - EntryNo) * PIndexHdr(FIndexFile.IndexHeader).KeyRecLen;
  663. // if 'rightmost' entry, copy pageno too
  664. if (FLowerPage <> nil) or (numKeysAvail > 1) then
  665. size := size + FIndexFile.EntryHeaderSize;
  666. Move(source^, dest^, size);
  667. // one entry added
  668. Inc(FHighIndex);
  669. IncNumEntries;
  670. // numEntries not valid from here
  671. SetEntry(RecNo, Buffer, LowerPageNo);
  672. // done!
  673. UnlockPage;
  674. end;
  675. procedure TIndexPage.LocalDelete;
  676. function IsOnlyEntry(Page: TIndexPage): boolean;
  677. begin
  678. Result := true;
  679. repeat
  680. if Page.HighIndex > 0 then
  681. Result := false;
  682. Page := Page.UpperPage;
  683. until not Result or (Page = nil);
  684. end;
  685. var
  686. source, dest: Pointer;
  687. size, numEntries: Integer;
  688. begin
  689. // get num entries
  690. numEntries := GetNumEntries;
  691. // is this last entry? if it's not move entries after current one
  692. if EntryNo < FHighIndex then
  693. begin
  694. source := GetEntry(EntryNo + 1);
  695. dest := FEntry;
  696. size := (FHighIndex - EntryNo) * PIndexHdr(FIndexFile.IndexHeader).KeyRecLen;
  697. Move(source^, dest^, size);
  698. end else
  699. // no need to update when we're about to remove the only entry
  700. if (UpperPage <> nil) and (FHighIndex > FLowIndex) then
  701. begin
  702. // we are about to remove the last on this page, so update search
  703. // key data of parent
  704. EntryNo := FHighIndex - 1;
  705. UpperPage.SetEntry(0, GetKeyData, FPageNo);
  706. end;
  707. // one entry less now
  708. dec(numEntries);
  709. dec(FHighIndex);
  710. SetNumEntries(numEntries);
  711. // zero last one out to not get confused about internal or leaf pages
  712. // note: need to decrease numEntries and HighIndex first, otherwise
  713. // check on page key consistency will fail
  714. SetRecLowerPageNoOfEntry(FHighIndex+1, 0, 0);
  715. // update bracket indexes
  716. if FHighPage = FPageNo then
  717. dec(FHighBracket);
  718. // check if range violated
  719. if EntryNo > FHighIndex then
  720. EntryNo := FHighIndex;
  721. // check if still entries left, otherwise remove page from parent
  722. if FHighIndex = -1 then
  723. begin
  724. if UpperPage <> nil then
  725. if not IsOnlyEntry(UpperPage) then
  726. UpperPage.LocalDelete;
  727. end;
  728. // go to valid record in lowerpage
  729. if FLowerPage <> nil then
  730. SyncLowerPage;
  731. // flag modified page
  732. FModified := true;
  733. // success!
  734. end;
  735. function TIndexPage.MatchKey: Integer;
  736. // assumes Buffer <> nil
  737. var
  738. keyData: PChar;
  739. begin
  740. // get key data
  741. keyData := GetKeyData;
  742. // use locale dependant compare
  743. Result := FIndexFile.CompareKey(keyData);
  744. end;
  745. function TIndexPage.FindNearest(ARecNo: Integer): Integer;
  746. // pre:
  747. // assumes Key <> nil
  748. // assumes FLowIndex <= FHighIndex + 1
  749. // ARecNo = -2 -> search first key matching Key
  750. // ARecNo = -3 -> search first key greater than Key
  751. // ARecNo > 0 -> search key matching Key and its recno = ARecNo
  752. // post:
  753. // Result < 0 -> key,recno smaller than current entry
  754. // Result = 0 -> key,recno found, FEntryNo = found key entryno
  755. // Result > 0 -> key,recno larger than current entry
  756. var
  757. low, high, current: Integer;
  758. begin
  759. // implement binary search, keys are sorted
  760. low := FLowIndex;
  761. high := GetNumEntries;
  762. // always true: Entry(FEntryNo) = FEntry
  763. // FHighIndex >= 0 because no-entry cases in leaves have been filtered out
  764. // entry HighIndex may not be bigger than rest (in inner node)
  765. // ARecNo = -3 -> search last recno matching key
  766. // need to have: low <= high
  767. // define low - 1 = neg.inf.
  768. // define high = pos.inf
  769. // inv1: (ARecNo<>-3) -> Entry(low-1).Key < Key <= Entry(high).Key
  770. // inv2: (ARecNo =-3) -> Entry(low-1).Key <= Key < Entry(high).Key
  771. // vf: high + 1 - low
  772. while low < high do
  773. begin
  774. current := (low + high) div 2;
  775. FEntry := GetEntry(current);
  776. // calc diff
  777. Result := MatchKey;
  778. // test if we need to go lower or higher
  779. // result < 0 implies key smaller than tested entry
  780. // result = 0 implies key equal to tested entry
  781. // result > 0 implies key greater than tested entry
  782. if (Result < 0) or ((ARecNo<>-3) and (Result=0)) then
  783. high := current
  784. else
  785. low := current+1;
  786. end;
  787. // high will contain first greater-or-equal key
  788. // ARecNo <> -3 -> Entry(high).Key will contain first key that matches -> go to high
  789. // ARecNo = -3 -> Entry(high).Key will contain first key that is greater -> go to high
  790. FEntryNo := -1;
  791. EntryNo := high;
  792. // calc end result: can't inspect high if lowerpage <> nil
  793. // if this is a leaf, we need to find specific recno
  794. if (LowerPage = nil) then
  795. begin
  796. if high > FHighIndex then
  797. begin
  798. Result := 1;
  799. end else begin
  800. Result := MatchKey;
  801. // test if we need to find a specific recno
  802. // result < 0 -> current key greater -> nothing found -> don't search
  803. if (ARecNo > 0) then
  804. begin
  805. // BLS to RecNo
  806. high := FHighIndex + 1;
  807. low := FEntryNo;
  808. // inv: FLowIndex <= FEntryNo <= high <= FHighIndex + 1 /\
  809. // (Ai: FLowIndex <= i < FEntryNo: Entry(i).RecNo <> ARecNo)
  810. while FEntryNo <> high do
  811. begin
  812. // FEntryNo < high, get new entry
  813. if low <> FEntryNo then
  814. begin
  815. FEntry := GetEntry(FEntryNo);
  816. // check if entry key still ok
  817. Result := MatchKey;
  818. end;
  819. // test if out of range or found recno
  820. if (Result <> 0) or (GetRecNo = ARecNo) then
  821. high := FEntryNo
  822. else begin
  823. // default to EOF
  824. inc(FEntryNo);
  825. Result := 1;
  826. end;
  827. end;
  828. end;
  829. end;
  830. end else begin
  831. // FLowerPage <> nil -> high contains entry, can not have empty range
  832. Result := 0;
  833. end;
  834. end;
  835. procedure TIndexPage.GotoInsertEntry;
  836. // assures we really can insert here
  837. begin
  838. if FEntry = FIndexFile.EntryEof then
  839. FEntry := GetEntry(FEntryNo);
  840. end;
  841. procedure TIndexPage.SetEntry(RecNo: Integer; Key: PChar; LowerPageNo: Integer);
  842. var
  843. keyData: PChar;
  844. {$ifdef TDBF_INDEX_CHECK}
  845. prevKeyData, curKeyData, nextKeyData: PChar;
  846. {$endif}
  847. begin
  848. // get num entries
  849. keyData := GetKeyData;
  850. // check valid entryno: we should be able to insert entries!
  851. assert((EntryNo >= 0) and (EntryNo <= FHighIndex));
  852. if (UpperPage <> nil) and (FEntryNo = FHighIndex) then
  853. UpperPage.SetEntry(0, Key, FPageNo);
  854. { if PIndexHdr(FIndexFile.IndexHeader).KeyType = 'C' then }
  855. if Key <> nil then
  856. Move(Key^, keyData^, PIndexHdr(FIndexFile.IndexHeader).KeyLen)
  857. else
  858. PChar(keyData)^ := #0;
  859. {
  860. else
  861. if Key <> nil then
  862. PDouble(keyData)^ := PDouble(Key)^
  863. else
  864. PDouble(keyData)^ := 0.0;
  865. }
  866. // set entry info
  867. SetRecLowerPageNo(RecNo, LowerPageNo);
  868. // flag we modified the page
  869. FModified := true;
  870. {$ifdef TDBF_INDEX_CHECK}
  871. // check sorted entry sequence
  872. prevKeyData := GetKeyDataFromEntry(FEntryNo-1);
  873. curKeyData := GetKeyDataFromEntry(FEntryNo+0);
  874. nextKeyData := GetKeyDataFromEntry(FEntryNo+1);
  875. // check if prior entry not greater, 'rightmost' key does not have to match
  876. if (FEntryNo > 0) and ((FLowerPage = nil) or (FEntryNo < FHighIndex)) then
  877. begin
  878. if FIndexFile.CompareKeys(prevKeyData, curKeyData) > 0 then
  879. assert(false);
  880. end;
  881. // check if next entry not smaller
  882. if ((FLowerPage = nil) and (FEntryNo < FHighIndex)) or
  883. ((FLowerPage <> nil) and (FEntryNo < (FHighIndex - 1))) then
  884. begin
  885. if FIndexFile.CompareKeys(curKeyData, nextKeyData) > 0 then
  886. assert(false);
  887. end;
  888. {$endif}
  889. end;
  890. {$ifdef TDBF_UPDATE_FIRST_LAST_NODE}
  891. procedure TIndexPage.SetPrevBlock(NewBlock: Integer);
  892. begin
  893. end;
  894. {$endif}
  895. procedure TIndexPage.Split;
  896. // *) assumes this page is `nearly' full
  897. var
  898. NewPage: TIndexPage;
  899. source, dest: Pointer;
  900. paKeyData: PChar;
  901. size, oldEntryNo: Integer;
  902. splitRight, numEntries, numEntriesNew: Integer;
  903. saveLow, saveHigh: Integer;
  904. newRoot: Boolean;
  905. begin
  906. // assure parent exists, if not -> create & lock, else lock it
  907. newRoot := FUpperPage = nil;
  908. if newRoot then
  909. FIndexFile.AddNewLevel
  910. else
  911. FUpperPage.LockPage;
  912. // lock this page for updates
  913. LockPage;
  914. // get num entries
  915. numEntries := GetNumEntries;
  916. // calc split pos: split in half
  917. splitRight := numEntries div 2;
  918. if (FLowerPage <> nil) and (numEntries mod 2 = 1) then
  919. inc(splitRight);
  920. numEntriesNew := numEntries - splitRight;
  921. // check if place to insert has least entries
  922. if (numEntriesNew > splitRight) and (EntryNo > splitRight) then
  923. begin
  924. inc(splitRight);
  925. dec(numEntriesNew);
  926. end else if (numEntriesNew < splitRight) and (EntryNo < splitRight) then
  927. begin
  928. dec(splitRight);
  929. inc(numEntriesNew);
  930. end;
  931. // save current entryno
  932. oldEntryNo := EntryNo;
  933. // check if we need to save high / low bound
  934. if FLowPage = FPageNo then
  935. saveLow := FLowIndex
  936. else
  937. saveLow := -1;
  938. if FHighPage = FPageNo then
  939. saveHigh := FHighIndex
  940. else
  941. saveHigh := -1;
  942. // create new page
  943. NewPage := TIndexPageClass(ClassType).Create(FIndexFile);
  944. try
  945. // get page
  946. NewPage.GetNewPage;
  947. {$ifdef TDBF_UPDATE_FIRSTLAST_NODE}
  948. NewPage.SetPrevBlock(NewPage.PageNo - FIndexFile.PagesPerRecord);
  949. {$endif}
  950. // set modified
  951. FModified := true;
  952. NewPage.FModified := true;
  953. // compute source, dest
  954. dest := NewPage.GetEntry(0);
  955. source := GetEntry(splitRight);
  956. size := numEntriesNew * PIndexHdr(FIndexFile.IndexHeader).KeyRecLen;
  957. // if inner node, copy rightmost entry too
  958. if FLowerPage <> nil then
  959. size := size + FIndexFile.EntryHeaderSize;
  960. // copy bytes
  961. Move(source^, dest^, size);
  962. // if not inner node, clear possible 'rightmost' entry
  963. if (FLowerPage = nil) then
  964. SetRecLowerPageNoOfEntry(splitRight, 0, 0);
  965. // calc new number of entries of this page
  966. numEntries := numEntries - numEntriesNew;
  967. // if lower level, then we need adjust for new 'rightmost' node
  968. if FLowerPage <> nil then
  969. begin
  970. // right split, so we need 'new' rightmost node
  971. dec(numEntries);
  972. end;
  973. // store new number of nodes
  974. // new page is right page, so update parent to point to new right page
  975. NewPage.SetNumEntries(numEntriesNew);
  976. SetNumEntries(numEntries);
  977. // update highindex
  978. FHighIndex := numEntries;
  979. if FLowerPage = nil then
  980. dec(FHighIndex);
  981. // get data of last entry on this page
  982. paKeyData := GetKeyDataFromEntry(splitRight - 1);
  983. // reinsert ourself into parent
  984. // FUpperPage.RecurInsert(0, paKeyData, FPageNo);
  985. // we can do this via a localinsert now: we know there is at least one entry
  986. // free in this page and higher up
  987. FUpperPage.LocalInsert(0, paKeyData, FPageNo);
  988. // new page is right page, so update parent to point to new right page
  989. // we can't do this earlier: we will get lost in tree!
  990. FUpperPage.SetRecLowerPageNoOfEntry(FUpperPage.EntryNo+1, 0, NewPage.PageNo);
  991. // NOTE: UpperPage.LowerPage = Self <= inserted FPageNo, not NewPage.PageNo
  992. finally
  993. NewPage.Free;
  994. end;
  995. // done updating: unlock page
  996. UnlockPage;
  997. // save changes to parent
  998. FUpperPage.UnlockPage;
  999. // unlock new root, unlock header too
  1000. FIndexFile.UnlockHeader;
  1001. // go to entry we left on
  1002. if oldEntryNo >= splitRight then
  1003. begin
  1004. // sync upperpage with right page
  1005. FUpperPage.EntryNo := FUpperPage.EntryNo + 1;
  1006. FEntryNo := oldEntryNo - splitRight;
  1007. FEntry := GetEntry(FEntryNo);
  1008. end else begin
  1009. // in left page = this page
  1010. EntryNo := oldEntryNo;
  1011. end;
  1012. // check if we have to save high / low bound
  1013. // seen the fact that FHighPage = FPageNo -> EntryNo <= FHighIndex, it can in
  1014. // theory not happen that page is advanced to right page and high bound remains
  1015. // on left page, but we won't check for that here
  1016. if saveLow >= splitRight then
  1017. begin
  1018. FLowPage := FPageNo;
  1019. FLowIndex := saveLow - splitRight;
  1020. end;
  1021. if saveHigh >= splitRight then
  1022. begin
  1023. FHighPage := FPageNo;
  1024. FHighIndex := saveHigh - splitRight;
  1025. end;
  1026. end;
  1027. procedure TIndexPage.Delete;
  1028. begin
  1029. LocalDelete;
  1030. end;
  1031. procedure TIndexPage.WritePage;
  1032. begin
  1033. // check if we modified current page
  1034. if FModified and (FPageNo > 0) then
  1035. begin
  1036. FIndexFile.WriteRecord(FPageNo, FPageBuffer);
  1037. FModified := false;
  1038. end;
  1039. end;
  1040. procedure TIndexPage.Flush;
  1041. begin
  1042. WritePage;
  1043. if FLowerPage <> nil then
  1044. FLowerPage.Flush;
  1045. end;
  1046. procedure TIndexPage.RecalcWeight;
  1047. begin
  1048. if FLowerPage <> nil then
  1049. begin
  1050. FWeight := FLowerPage.Weight * PIndexHdr(FIndexFile.IndexHeader).NumKeys;
  1051. end else begin
  1052. FWeight := 1;
  1053. end;
  1054. if FUpperPage <> nil then
  1055. FUpperPage.RecalcWeight;
  1056. end;
  1057. procedure TIndexPage.UpdateWeight;
  1058. begin
  1059. if FLowerPage <> nil then
  1060. FLowerPage.UpdateWeight
  1061. else
  1062. RecalcWeight;
  1063. end;
  1064. procedure TIndexPage.SetUpperPage(NewPage: TIndexPage);
  1065. begin
  1066. if FUpperPage <> NewPage then
  1067. begin
  1068. // root height changed: update weights
  1069. FUpperPage := NewPage;
  1070. UpdateWeight;
  1071. end;
  1072. end;
  1073. procedure TIndexPage.SetLowPage(NewPage: Integer);
  1074. begin
  1075. if FLowPage <> NewPage then
  1076. begin
  1077. FLowPage := NewPage;
  1078. UpdateBounds(FLowerPage <> nil);
  1079. end;
  1080. end;
  1081. procedure TIndexPage.SetHighPage(NewPage: Integer);
  1082. begin
  1083. if FHighPage <> NewPage then
  1084. begin
  1085. FHighPage := NewPage;
  1086. UpdateBounds(FLowerPage <> nil);
  1087. end;
  1088. end;
  1089. procedure TIndexPage.UpdateBounds(IsInnerNode: Boolean);
  1090. begin
  1091. // update low / high index range
  1092. if FPageNo = FLowPage then
  1093. FLowIndex := FLowBracket
  1094. else
  1095. FLowIndex := 0;
  1096. if FPageNo = FHighPage then
  1097. FHighIndex := FHighBracket
  1098. else begin
  1099. FHighIndex := GetNumEntries;
  1100. if not IsInnerNode then
  1101. dec(FHighIndex);
  1102. end;
  1103. end;
  1104. function TMdxPage.GetIsInnerNode: Boolean;
  1105. begin
  1106. Result := PMdxPage(FPageBuffer).NumEntries < PIndexHdr(FIndexFile.IndexHeader).NumKeys;
  1107. // if there is still an entry after the last one, this has to be an inner node
  1108. if Result then
  1109. Result := PMdxEntry(GetEntry(PMdxPage(FPageBuffer).NumEntries)).RecBlockNo <> 0;
  1110. end;
  1111. function TNdxPage.GetIsInnerNode: Boolean;
  1112. begin
  1113. Result := PNdxEntry(GetEntry(0)).LowerPageNo <> 0;
  1114. end;
  1115. procedure TIndexPage.SetPageNo(NewPageNo: Integer);
  1116. var
  1117. isInnerNode: Boolean;
  1118. begin
  1119. if (NewPageNo <> FPageNo) or FIndexFile.NeedLocks then
  1120. begin
  1121. // save changes
  1122. WritePage;
  1123. // no locks
  1124. assert(FLockCount = 0);
  1125. // goto new page
  1126. FPageNo := NewPageNo;
  1127. // remind ourselves we need to load new entry when page loaded
  1128. FEntryNo := -1;
  1129. if (NewPageNo > 0) and (NewPageNo <= FIndexFile.RecordCount) then
  1130. begin
  1131. // read page from disk
  1132. FIndexFile.ReadRecord(NewPageNo, FPageBuffer);
  1133. // fixup descending tree
  1134. isInnerNode := GetIsInnerNode;
  1135. // update low / high index range
  1136. UpdateBounds(isInnerNode);
  1137. // read inner node if any
  1138. if isInnerNode then
  1139. begin
  1140. if FLowerPage = nil then
  1141. begin
  1142. FLowerPage := TIndexPageClass(ClassType).Create(FIndexFile);
  1143. FLowerPage.UpperPage := Self;
  1144. end;
  1145. // read first entry, don't do this sooner, not created lowerpage yet
  1146. // don't recursively resync all lower pages
  1147. {$ifdef TDBF_INDEX_CHECK}
  1148. end else if FLowerPage <> nil then
  1149. begin
  1150. // FLowerPage.Free;
  1151. // FLowerPage := nil;
  1152. assert(false);
  1153. {$endif}
  1154. end else begin
  1155. // we don't have to check autoresync here because we're already at lowest level
  1156. EntryNo := FLowIndex;
  1157. end;
  1158. end;
  1159. end;
  1160. end;
  1161. procedure TIndexPage.SyncLowerPage;
  1162. // *) assumes FLowerPage <> nil!
  1163. begin
  1164. FLowerPage.PageNo := GetLowerPageNo;
  1165. end;
  1166. procedure TIndexPage.SetEntryNo(value: Integer);
  1167. begin
  1168. // do not bother if no change
  1169. if value <> FEntryNo then
  1170. begin
  1171. // check if out of range
  1172. if (value < FLowIndex) then
  1173. begin
  1174. if FLowerPage = nil then
  1175. FEntryNo := FLowIndex - 1;
  1176. FEntry := FIndexFile.EntryBof;
  1177. end else if value > FHighIndex then begin
  1178. FEntryNo := FHighIndex + 1;
  1179. FEntry := FIndexFile.EntryEof;
  1180. end else begin
  1181. FEntryNo := value;
  1182. FEntry := GetEntry(value);
  1183. // sync lowerpage with entry
  1184. if (FLowerPage <> nil) then
  1185. SyncLowerPage;
  1186. end;
  1187. end;
  1188. end;
  1189. function TIndexPage.PhysicalRecNo: Integer;
  1190. var
  1191. entryRec: Integer;
  1192. begin
  1193. // get num entries
  1194. entryRec := GetRecNo;
  1195. // check if in range
  1196. if (FEntryNo >= FLowIndex) and (FEntryNo <= FHighIndex) then
  1197. Result := entryRec
  1198. else
  1199. Result := -1;
  1200. end;
  1201. function TIndexPage.RecurPrev: Boolean;
  1202. begin
  1203. EntryNo := EntryNo - 1;
  1204. Result := Entry <> FIndexFile.EntryBof;
  1205. if Result then
  1206. begin
  1207. if FLowerPage <> nil then
  1208. begin
  1209. FLowerPage.RecurLast;
  1210. end;
  1211. end else begin
  1212. if FUpperPage<>nil then
  1213. begin
  1214. Result := FUpperPage.RecurPrev;
  1215. end;
  1216. end;
  1217. end;
  1218. function TIndexPage.RecurNext: Boolean;
  1219. begin
  1220. EntryNo := EntryNo + 1;
  1221. Result := Entry <> FIndexFile.EntryEof;
  1222. if Result then
  1223. begin
  1224. if FLowerPage <> nil then
  1225. begin
  1226. FLowerPage.RecurFirst;
  1227. end;
  1228. end else begin
  1229. if FUpperPage<>nil then
  1230. begin
  1231. Result := FUpperPage.RecurNext;
  1232. end;
  1233. end;
  1234. end;
  1235. procedure TIndexPage.RecurFirst;
  1236. begin
  1237. EntryNo := FLowIndex;
  1238. if (FLowerPage<>nil) then
  1239. FLowerPage.RecurFirst;
  1240. end;
  1241. procedure TIndexPage.RecurLast;
  1242. begin
  1243. EntryNo := FHighIndex;
  1244. if (FLowerPage<>nil) then
  1245. FLowerPage.RecurLast;
  1246. end;
  1247. //==============================================================================
  1248. //============ Mdx specific access routines
  1249. //==============================================================================
  1250. function TMdxPage.GetEntry(AEntryNo: Integer): Pointer;
  1251. begin
  1252. // get base + offset
  1253. Result := PChar(@PMdxPage(PageBuffer).FirstEntry) + (PIndexHdr(IndexFile.IndexHeader).KeyRecLen * AEntryNo);
  1254. end;
  1255. function TMdxPage.GetLowerPageNo: Integer;
  1256. // *) assumes LowerPage <> nil
  1257. begin
  1258. // if LowerPage = nil then
  1259. // Result := 0
  1260. // else
  1261. Result := PMdxEntry(Entry).RecBlockNo;
  1262. end;
  1263. function TMdxPage.GetKeyData: PChar;
  1264. begin
  1265. Result := @PMdxEntry(Entry).KeyData;
  1266. end;
  1267. function TMdxPage.GetNumEntries: Integer;
  1268. begin
  1269. Result := PMdxPage(PageBuffer).NumEntries;
  1270. end;
  1271. function TMdxPage.GetKeyDataFromEntry(AEntry: Integer): PChar;
  1272. begin
  1273. Result := @PMdxEntry(GetEntry(AEntry)).KeyData;
  1274. end;
  1275. function TMdxPage.GetRecNo: Integer;
  1276. begin
  1277. Result := PMdxEntry(Entry).RecBlockNo;
  1278. end;
  1279. procedure TMdxPage.SetNumEntries(NewNum: Integer);
  1280. begin
  1281. PMdxPage(PageBuffer).NumEntries := NewNum;
  1282. end;
  1283. procedure TMdxPage.IncNumEntries;
  1284. begin
  1285. Inc(PMdxPage(PageBuffer).NumEntries);
  1286. end;
  1287. procedure TMdxPage.SetRecLowerPageNo(NewRecNo, NewPageNo: Integer);
  1288. begin
  1289. if FLowerPage = nil then
  1290. PMdxEntry(Entry).RecBlockNo := NewRecNo
  1291. else
  1292. PMdxEntry(Entry).RecBlockNo := NewPageNo;
  1293. end;
  1294. procedure TMdxPage.SetRecLowerPageNoOfEntry(AEntry, NewRecNo, NewPageNo: Integer);
  1295. begin
  1296. if FLowerPage = nil then
  1297. PMdxEntry(GetEntry(AEntry)).RecBlockNo := NewRecNo
  1298. else
  1299. PMdxEntry(GetEntry(AEntry)).RecBlockNo := NewPageNo;
  1300. end;
  1301. {$ifdef TDBF_UPDATE_FIRST_LAST_NODE}
  1302. procedure TMdxPage.SetPrevBlock(NewBlock: Integer);
  1303. begin
  1304. PMdxPage(PageBuffer).PrevBlock := NewBlock;
  1305. end;
  1306. {$endif}
  1307. //==============================================================================
  1308. //============ Ndx specific access routines
  1309. //==============================================================================
  1310. function TNdxPage.GetEntry(AEntryNo: Integer): Pointer;
  1311. begin
  1312. // get base + offset
  1313. Result := PChar(@PNdxPage(PageBuffer).FirstEntry) + (PIndexHdr(FIndexFile.IndexHeader).KeyRecLen * AEntryNo);
  1314. end;
  1315. function TNdxPage.GetLowerPageNo: Integer;
  1316. // *) assumes LowerPage <> nil
  1317. begin
  1318. // if LowerPage = nil then
  1319. // Result := 0
  1320. // else
  1321. Result := PNdxEntry(Entry).LowerPageNo
  1322. end;
  1323. function TNdxPage.GetRecNo: Integer;
  1324. begin
  1325. Result := PNdxEntry(Entry).RecNo;
  1326. end;
  1327. function TNdxPage.GetKeyData: PChar;
  1328. begin
  1329. Result := @PNdxEntry(Entry).KeyData;
  1330. end;
  1331. function TNdxPage.GetKeyDataFromEntry(AEntry: Integer): PChar;
  1332. begin
  1333. Result := @PNdxEntry(GetEntry(AEntry)).KeyData;
  1334. end;
  1335. function TNdxPage.GetNumEntries: Integer;
  1336. begin
  1337. Result := PNdxPage(PageBuffer).NumEntries;
  1338. end;
  1339. procedure TNdxPage.IncNumEntries;
  1340. begin
  1341. Inc(PNdxPage(PageBuffer).NumEntries);
  1342. end;
  1343. procedure TNdxPage.SetNumEntries(NewNum: Integer);
  1344. begin
  1345. PNdxPage(PageBuffer).NumEntries := NewNum;
  1346. end;
  1347. procedure TNdxPage.SetRecLowerPageNo(NewRecNo, NewPageNo: Integer);
  1348. begin
  1349. PNdxEntry(Entry).RecNo := NewRecNo;
  1350. PNdxEntry(Entry).LowerPageNo := NewPageNo;
  1351. end;
  1352. procedure TNdxPage.SetRecLowerPageNoOfEntry(AEntry, NewRecNo, NewPageNo: Integer);
  1353. begin
  1354. PNdxEntry(GetEntry(AEntry)).RecNo := NewRecNo;
  1355. PNdxEntry(GetEntry(AEntry)).LowerPageNo := NewPageNo;
  1356. end;
  1357. //==============================================================================
  1358. //============ MDX version 4 header access routines
  1359. //==============================================================================
  1360. function TMdx4Tag.GetHeaderPageNo: Integer;
  1361. begin
  1362. Result := PMdx4Tag(Tag).HeaderPageNo;
  1363. end;
  1364. function TMdx4Tag.GetTagName: string;
  1365. begin
  1366. Result := PMdx4Tag(Tag).TagName;
  1367. end;
  1368. function TMdx4Tag.GetKeyFormat: Byte;
  1369. begin
  1370. Result := PMdx4Tag(Tag).KeyFormat;
  1371. end;
  1372. function TMdx4Tag.GetForwardTag1: Byte;
  1373. begin
  1374. Result := PMdx4Tag(Tag).ForwardTag1;
  1375. end;
  1376. function TMdx4Tag.GetForwardTag2: Byte;
  1377. begin
  1378. Result := PMdx4Tag(Tag).ForwardTag2;
  1379. end;
  1380. function TMdx4Tag.GetBackwardTag: Byte;
  1381. begin
  1382. Result := PMdx4Tag(Tag).BackwardTag;
  1383. end;
  1384. function TMdx4Tag.GetReserved: Byte;
  1385. begin
  1386. Result := PMdx4Tag(Tag).Reserved;
  1387. end;
  1388. function TMdx4Tag.GetKeyType: Char;
  1389. begin
  1390. Result := PMdx4Tag(Tag).KeyType;
  1391. end;
  1392. procedure TMdx4Tag.SetHeaderPageNo(NewPageNo: Integer);
  1393. begin
  1394. PMdx4Tag(Tag).HeaderPageNo := NewPageNo;
  1395. end;
  1396. procedure TMdx4Tag.SetTagName(NewName: string);
  1397. begin
  1398. StrPLCopy(PMdx4Tag(Tag).TagName, NewName, 10);
  1399. PMdx4Tag(Tag).TagName[10] := #0;
  1400. end;
  1401. procedure TMdx4Tag.SetKeyFormat(NewFormat: Byte);
  1402. begin
  1403. PMdx4Tag(Tag).KeyFormat := NewFormat;
  1404. end;
  1405. procedure TMdx4Tag.SetForwardTag1(NewTag: Byte);
  1406. begin
  1407. PMdx4Tag(Tag).ForwardTag1 := NewTag;
  1408. end;
  1409. procedure TMdx4Tag.SetForwardTag2(NewTag: Byte);
  1410. begin
  1411. PMdx4Tag(Tag).ForwardTag2 := NewTag;
  1412. end;
  1413. procedure TMdx4Tag.SetBackwardTag(NewTag: Byte);
  1414. begin
  1415. PMdx4Tag(Tag).BackwardTag := NewTag;
  1416. end;
  1417. procedure TMdx4Tag.SetReserved(NewReserved: Byte);
  1418. begin
  1419. PMdx4Tag(Tag).Reserved := NewReserved;
  1420. end;
  1421. procedure TMdx4Tag.SetKeyType(NewType: Char);
  1422. begin
  1423. PMdx4Tag(Tag).KeyType := NewType;
  1424. end;
  1425. //==============================================================================
  1426. //============ MDX version 7 headertag access routines
  1427. //==============================================================================
  1428. function TMdx7Tag.GetHeaderPageNo: Integer;
  1429. begin
  1430. Result := PMdx7Tag(Tag).HeaderPageNo;
  1431. end;
  1432. function TMdx7Tag.GetTagName: string;
  1433. begin
  1434. Result := PMdx7Tag(Tag).TagName;
  1435. end;
  1436. function TMdx7Tag.GetKeyFormat: Byte;
  1437. begin
  1438. Result := PMdx7Tag(Tag).KeyFormat;
  1439. end;
  1440. function TMdx7Tag.GetForwardTag1: Byte;
  1441. begin
  1442. Result := PMdx7Tag(Tag).ForwardTag1;
  1443. end;
  1444. function TMdx7Tag.GetForwardTag2: Byte;
  1445. begin
  1446. Result := PMdx7Tag(Tag).ForwardTag2;
  1447. end;
  1448. function TMdx7Tag.GetBackwardTag: Byte;
  1449. begin
  1450. Result := PMdx7Tag(Tag).BackwardTag;
  1451. end;
  1452. function TMdx7Tag.GetReserved: Byte;
  1453. begin
  1454. Result := PMdx7Tag(Tag).Reserved;
  1455. end;
  1456. function TMdx7Tag.GetKeyType: Char;
  1457. begin
  1458. Result := PMdx7Tag(Tag).KeyType;
  1459. end;
  1460. procedure TMdx7Tag.SetHeaderPageNo(NewPageNo: Integer);
  1461. begin
  1462. PMdx7Tag(Tag).HeaderPageNo := NewPageNo;
  1463. end;
  1464. procedure TMdx7Tag.SetTagName(NewName: string);
  1465. begin
  1466. StrPLCopy(PMdx7Tag(Tag).TagName, NewName, 32);
  1467. PMdx7Tag(Tag).TagName[32] := #0;
  1468. end;
  1469. procedure TMdx7Tag.SetKeyFormat(NewFormat: Byte);
  1470. begin
  1471. PMdx7Tag(Tag).KeyFormat := NewFormat;
  1472. end;
  1473. procedure TMdx7Tag.SetForwardTag1(NewTag: Byte);
  1474. begin
  1475. PMdx7Tag(Tag).ForwardTag1 := NewTag;
  1476. end;
  1477. procedure TMdx7Tag.SetForwardTag2(NewTag: Byte);
  1478. begin
  1479. PMdx7Tag(Tag).ForwardTag2 := NewTag;
  1480. end;
  1481. procedure TMdx7Tag.SetBackwardTag(NewTag: Byte);
  1482. begin
  1483. PMdx7Tag(Tag).BackwardTag := NewTag;
  1484. end;
  1485. procedure TMdx7Tag.SetReserved(NewReserved: Byte);
  1486. begin
  1487. PMdx7Tag(Tag).Reserved := NewReserved;
  1488. end;
  1489. procedure TMdx7Tag.SetKeyType(NewType: Char);
  1490. begin
  1491. PMdx7Tag(Tag).KeyType := NewType;
  1492. end;
  1493. //==============================================================================
  1494. //============ TIndexFile
  1495. //==============================================================================
  1496. constructor TIndexFile.Create(ADbfFile: Pointer);
  1497. var
  1498. I: Integer;
  1499. begin
  1500. inherited Create;
  1501. // clear variables
  1502. FOpened := false;
  1503. FRangeActive := false;
  1504. FUpdateMode := umCurrent;
  1505. FModifyMode := mmNormal;
  1506. FTempMode := TDbfFile(ADbfFile).TempMode;
  1507. SelectIndexVars(-1);
  1508. for I := 0 to MaxIndexes - 1 do
  1509. begin
  1510. FParsers[I] := nil;
  1511. FRoots[I] := nil;
  1512. FLeaves[I] := nil;
  1513. FHeaderModified[I] := false;
  1514. end;
  1515. // store pointer to `parent' dbf file
  1516. FDbfFile := ADbfFile;
  1517. end;
  1518. destructor TIndexFile.Destroy;
  1519. begin
  1520. // close file
  1521. Close;
  1522. // call ancestor
  1523. inherited Destroy;
  1524. end;
  1525. procedure TIndexFile.Open;
  1526. var
  1527. I: Integer;
  1528. ext: string;
  1529. localeError: TLocaleError;
  1530. localeSolution: TLocaleSolution;
  1531. DbfLangId: Byte;
  1532. begin
  1533. if not FOpened then
  1534. begin
  1535. // open physical file
  1536. OpenFile;
  1537. // page offsets are not related to header length
  1538. PageOffsetByHeader := false;
  1539. // we need physical page locks
  1540. VirtualLocks := false;
  1541. // not selected index expression => can't edit yet
  1542. FCanEdit := false;
  1543. FUserKey := nil;
  1544. FUserRecNo := -1;
  1545. FHeaderLocked := -1;
  1546. FHeaderPageNo := 0;
  1547. FForceClose := false;
  1548. FForceReadOnly := false;
  1549. FMdxTag := nil;
  1550. // get index type
  1551. ext := UpperCase(ExtractFileExt(FileName));
  1552. if (ext = '.MDX') then
  1553. begin
  1554. FEntryHeaderSize := 4;
  1555. FPageHeaderSize := 8;
  1556. FEntryBof := @Entry_Mdx_BOF;
  1557. FEntryEof := @Entry_Mdx_EOF;
  1558. HeaderSize := 2048;
  1559. RecordSize := 1024;
  1560. PageSize := 512;
  1561. if FileCreated then
  1562. begin
  1563. FIndexVersion := TDbfFile(FDbfFile).DbfVersion;
  1564. if FIndexVersion = xBaseIII then
  1565. FIndexVersion := xBaseIV;
  1566. end else begin
  1567. case PMdxHdr(Header).MdxVersion of
  1568. 3: FIndexVersion := xBaseVII;
  1569. else
  1570. FIndexVersion := xBaseIV;
  1571. end;
  1572. end;
  1573. case FIndexVersion of
  1574. xBaseVII:
  1575. begin
  1576. FMdxTag := TMdx7Tag.Create;
  1577. FTempMdxTag := TMdx7Tag.Create;
  1578. end;
  1579. else
  1580. FMdxTag := TMdx4Tag.Create;
  1581. FTempMdxTag := TMdx4Tag.Create;
  1582. end;
  1583. // get mem for all index headers..we're going to cache these
  1584. for I := 0 to MaxIndexes - 1 do
  1585. begin
  1586. GetMem(FIndexHeaders[I], RecordSize);
  1587. FillChar(FIndexHeaders[I]^, RecordSize, 0);
  1588. end;
  1589. // set pointers to first index
  1590. FIndexHeader := FIndexHeaders[0];
  1591. end else begin
  1592. // don't waste memory on another header block: we can just use
  1593. // the pagedfile one, there is only one index in this file
  1594. FIndexVersion := xBaseIII;
  1595. FEntryHeaderSize := 8;
  1596. FPageHeaderSize := 4;
  1597. FEntryBof := @Entry_Ndx_BOF;
  1598. FEntryEof := @Entry_Ndx_EOF;
  1599. HeaderSize := 512;
  1600. RecordSize := 512;
  1601. // have to read header first before we can assign following vars
  1602. FIndexHeaders[0] := Header;
  1603. FIndexHeader := Header;
  1604. // create default root
  1605. FParsers[0] := TDbfParser.Create(FDbfFile);
  1606. FRoots[0] := TNdxPage.Create(Self);
  1607. FCurrentParser := FParsers[0];
  1608. FRoot := FRoots[0];
  1609. FSelectedIndex := 0;
  1610. // parse index expression
  1611. FCurrentParser.ParseExpression(PIndexHdr(FIndexHeader).KeyDesc);
  1612. // set index locale
  1613. InternalLocaleID := LCID(lcidBinary);
  1614. end;
  1615. // determine how to open file
  1616. if FileCreated then
  1617. begin
  1618. FillChar(Header^, HeaderSize, 0);
  1619. Clear;
  1620. end else begin
  1621. // determine locale type
  1622. localeError := leNone;
  1623. if (FIndexVersion >= xBaseIV) then
  1624. begin
  1625. // get parent language id
  1626. DbfLangId := GetDbfLanguageId;
  1627. // no ID?
  1628. if (DbfLangId = 0) { and (TDbfFile(FDbfFile).DbfVersion = xBaseIII)} then
  1629. begin
  1630. // if dbf is version 3, no language id, if no MDX language, use binary
  1631. if PMdxHdr(Header).Language = 0 then
  1632. InternalLocaleID := lcidBinary
  1633. else
  1634. InternalLocaleID := LangId_To_Locale[PMdxHdr(Header).Language];
  1635. end else begin
  1636. // check if MDX - DBF language id's match
  1637. if (PMdxHdr(Header).Language = 0) or (PMdxHdr(Header).Language = DbfLangId) then
  1638. InternalLocaleID := LangId_To_Locale[DbfLangId]
  1639. else
  1640. localeError := leTableIndexMismatch;
  1641. end;
  1642. // don't overwrite previous error
  1643. if (FLocaleID = DbfLocale_NotFound) and (localeError = leNone) then
  1644. localeError := leUnknown;
  1645. end else begin
  1646. // dbase III always binary?
  1647. InternalLocaleID := lcidBinary;
  1648. end;
  1649. // check if selected locale is available, binary is always available...
  1650. if (localeError <> leNone) and (FLocaleID <> LCID(lcidBinary)) then
  1651. begin
  1652. if LCIDList.IndexOf(Pointer(FLocaleID)) < 0 then
  1653. localeError := leNotAvailable;
  1654. end;
  1655. // check if locale error detected
  1656. if localeError <> leNone then
  1657. begin
  1658. // provide solution, well, solution...
  1659. localeSolution := lsNotOpen;
  1660. // call error handler
  1661. if Assigned(FOnLocaleError) then
  1662. FOnLocaleError(localeError, localeSolution);
  1663. // act to solution
  1664. case localeSolution of
  1665. lsNotOpen: FForceClose := true;
  1666. lsNoEdit: FForceReadOnly := true;
  1667. else
  1668. // `trust' user knows correct locale
  1669. InternalLocaleID := LCID(localeSolution);
  1670. end;
  1671. end;
  1672. // now read info
  1673. if not ForceClose then
  1674. ReadIndexes;
  1675. end;
  1676. // default to update all
  1677. UpdateMode := umAll;
  1678. // flag open
  1679. FOpened := true;
  1680. end;
  1681. end;
  1682. procedure TIndexFile.Close;
  1683. var
  1684. I: Integer;
  1685. begin
  1686. if FOpened then
  1687. begin
  1688. // save headers
  1689. Flush;
  1690. // remove parser reference
  1691. FCurrentParser := nil;
  1692. // free roots
  1693. if FIndexVersion >= xBaseIV then
  1694. begin
  1695. for I := 0 to MaxIndexes - 1 do
  1696. begin
  1697. FreeMemAndNil(FIndexHeaders[I]);
  1698. FreeAndNil(FParsers[I]);
  1699. FreeAndNil(FRoots[I]);
  1700. end;
  1701. end else begin
  1702. FreeAndNil(FRoot);
  1703. end;
  1704. // free mem
  1705. FMdxTag.Free;
  1706. FTempMdxTag.Free;
  1707. // close physical file
  1708. CloseFile;
  1709. // not opened any more
  1710. FOpened := false;
  1711. end;
  1712. end;
  1713. procedure TIndexFile.ClearRoots;
  1714. //
  1715. // *) assumes FIndexVersion >= xBaseIV
  1716. //
  1717. var
  1718. I, prevIndex: Integer;
  1719. begin
  1720. prevIndex := FSelectedIndex;
  1721. for I := 0 to MaxIndexes - 1 do
  1722. begin
  1723. SelectIndexVars(I);
  1724. if FRoot <> nil then
  1725. begin
  1726. // clear this entry
  1727. ClearIndex;
  1728. FLeaves[I] := FRoots[I];
  1729. end;
  1730. FHeaderModified[I] := false;
  1731. end;
  1732. // reselect previously selected index
  1733. SelectIndexVars(prevIndex);
  1734. // deselect index
  1735. end;
  1736. procedure TIndexFile.Clear;
  1737. var
  1738. year, month, day: Word;
  1739. HdrFileName, HdrFileExt: string;
  1740. pos, prevSelIndex: Integer;
  1741. DbfLangId: Byte;
  1742. begin
  1743. // flush cache to prevent reading corrupted data
  1744. Flush;
  1745. // completely erase index
  1746. if FIndexVersion >= xBaseIV then
  1747. begin
  1748. DecodeDate(Now, year, month, day);
  1749. PMdxHdr(Header).MdxVersion := 2;
  1750. PMdxHdr(Header).Year := year - 1900;
  1751. PMdxHdr(Header).Month := month;
  1752. PMdxHdr(Header).Day := day;
  1753. HdrFileName := ExtractFileName(FileName);
  1754. HdrFileExt := ExtractFileExt(HdrFileName);
  1755. if Length(HdrFileExt) > 0 then
  1756. begin
  1757. pos := System.Pos(HdrFileExt, HdrFileName);
  1758. if pos > 0 then
  1759. SetLength(HdrFileName, pos - 1);
  1760. end;
  1761. if Length(HdrFileName) > 15 then
  1762. SetLength(HdrFileName, 15);
  1763. StrPCopy(PMdxHdr(Header).FileName, HdrFileName);
  1764. PMdxHdr(Header).BlockSize := 2;
  1765. PMdxHdr(Header).BlockAdder := 1024;
  1766. PMdxHdr(Header).ProdFlag := 1;
  1767. PMdxHdr(Header).NumTags := 48;
  1768. PMdxHdr(Header).TagSize := 32;
  1769. // PMdxHdr(Header).TagsUsed := 0;
  1770. PMdxHdr(Header).Dummy2 := 0;
  1771. PMdxHdr(Header).Language := GetDbfLanguageID;
  1772. PMdxHdr(Header).NumPages := HeaderSize div PageSize; // = 4
  1773. TouchHeader(Header);
  1774. PMdxHdr(Header).TagFlag := 1;
  1775. // use locale id of parent
  1776. DbfLangId := GetDbfLanguageId;
  1777. if DbfLangId = 0 then
  1778. InternalLocaleID := lcidBinary
  1779. else
  1780. InternalLocaleID := LangID_To_Locale[DbfLangId];
  1781. WriteFileHeader;
  1782. // write index headers
  1783. prevSelIndex := FSelectedIndex;
  1784. for pos := 0 to PMdxHdr(Header).TagsUsed - 1 do
  1785. begin
  1786. SelectIndexVars(pos);
  1787. FMdxTag.HeaderPageNo := GetNewPageNo;
  1788. WriteRecord(FMdxTag.HeaderPageNo, FIndexHeader);
  1789. end;
  1790. // reselect previously selected index
  1791. SelectIndexVars(prevSelIndex);
  1792. // clear roots
  1793. ClearRoots;
  1794. // init vars
  1795. FTagSize := 32;
  1796. FTagOffset := 544;
  1797. // clear entries
  1798. RecordCount := PMdxHdr(Header).NumPages;
  1799. end else begin
  1800. // clear single index entry
  1801. ClearIndex;
  1802. RecordCount := PIndexHdr(FIndexHeader).NumPages;
  1803. end;
  1804. end;
  1805. procedure TIndexFile.ClearIndex;
  1806. var
  1807. prevHeaderLocked: Integer;
  1808. needHeaderLock: Boolean;
  1809. begin
  1810. // flush cache to prevent reading corrupted data
  1811. Flush;
  1812. // modifying header: lock page
  1813. needHeaderLock := FHeaderLocked <> 0;
  1814. prevHeaderLocked := FHeaderLocked;
  1815. if needHeaderLock then
  1816. begin
  1817. LockPage(0, true);
  1818. FHeaderLocked := 0;
  1819. end;
  1820. // initially, we have 1 page: header
  1821. PIndexHdr(FIndexHeader).NumPages := HeaderSize div PageSize;
  1822. // clear memory of root
  1823. FRoot.Clear;
  1824. // get new page for root
  1825. FRoot.GetNewPage;
  1826. // store new root page
  1827. PIndexHdr(FIndexHeader).RootPage := FRoot.PageNo;
  1828. {$ifdef TDBF_UPDATE_FIRSTLAST_NODE}
  1829. PIndexHdr(FIndexHeader).FirstNode := FRoot.PageNo;
  1830. {$endif}
  1831. // update leaf pointers
  1832. FLeaves[FSelectedIndex] := FRoot;
  1833. FLeaf := FRoot;
  1834. // write new header
  1835. WriteHeader;
  1836. FRoot.Modified;
  1837. FRoot.WritePage;
  1838. // done updating: unlock header
  1839. if needHeaderLock then
  1840. begin
  1841. UnlockPage(0);
  1842. FHeaderLocked := prevHeaderLocked;
  1843. end;
  1844. end;
  1845. procedure TIndexFile.CalcKeyProperties;
  1846. // given KeyLen, this func calcs KeyRecLen and NumEntries
  1847. var
  1848. remainder: Integer;
  1849. begin
  1850. // now adjust keylen to align on DWORD boundaries
  1851. PIndexHdr(FIndexHeader).KeyRecLen := PIndexHdr(FIndexHeader).KeyLen + FEntryHeaderSize;
  1852. remainder := (PIndexHdr(FIndexHeader).KeyRecLen) mod 4;
  1853. if (remainder > 0) then
  1854. PIndexHdr(FIndexHeader).KeyRecLen := PIndexHdr(FIndexHeader).KeyRecLen + 4 - remainder;
  1855. PIndexHdr(FIndexHeader).NumKeys := (RecordSize - FPageHeaderSize) div PIndexHdr(FIndexHeader).KeyRecLen;
  1856. end;
  1857. function TIndexFile.GetName: string;
  1858. begin
  1859. // get suitable name of index: if tag name defined use that otherwise filename
  1860. if FIndexVersion >= xBaseIV then
  1861. Result := FIndexName
  1862. else
  1863. Result := FileName;
  1864. end;
  1865. procedure TIndexFile.CreateIndex(FieldDesc, TagName: string; Options: TIndexOptions);
  1866. var
  1867. tagNo: Integer;
  1868. fieldType: Char;
  1869. TempParser: TDbfParser;
  1870. begin
  1871. // check if we have exclusive access to table
  1872. TDbfFile(FDbfFile).CheckExclusiveAccess;
  1873. // parse index expression; if it cannot be parsed, why bother making index?
  1874. TempParser := TDbfParser.Create(FDbfFile);
  1875. try
  1876. TempParser.ParseExpression(FieldDesc);
  1877. // check if result type is correct
  1878. case TempParser.ResultType of
  1879. etString: fieldType := 'C';
  1880. etInteger, etLargeInt, etFloat: fieldType := 'N';
  1881. else
  1882. raise EDbfError.Create(STRING_INVALID_INDEX_TYPE);
  1883. end;
  1884. finally
  1885. TempParser.Free;
  1886. end;
  1887. // select empty index
  1888. if FIndexVersion >= xBaseIV then
  1889. begin
  1890. // get next entry no
  1891. tagNo := PMdxHdr(Header).TagsUsed;
  1892. // check if too many indexes
  1893. if tagNo = MaxIndexes then
  1894. raise EDbfError.Create(STRING_TOO_MANY_INDEXES);
  1895. // get memory for root
  1896. if FRoots[tagNo] = nil then
  1897. begin
  1898. FParsers[tagNo] := TDbfParser.Create(FDbfFile);
  1899. FRoots[tagNo] := TMdxPage.Create(Self)
  1900. end else begin
  1901. FreeAndNil(FRoots[tagNo].FLowerPage);
  1902. end;
  1903. // set leaves pointer
  1904. FLeaves[tagNo] := FRoots[tagNo];
  1905. // get pointer to index header
  1906. FIndexHeader := FIndexHeaders[tagNo];
  1907. // load root + leaf
  1908. FCurrentParser := FParsers[tagNo];
  1909. FRoot := FRoots[tagNo];
  1910. FLeaf := FLeaves[tagNo];
  1911. // create new tag
  1912. FTempMdxTag.Tag := CalcTagOffset(tagNo);
  1913. FTempMdxTag.TagName := UpperCase(TagName);
  1914. // if expression then calculate
  1915. FTempMdxTag.KeyFormat := KeyFormat_Data;
  1916. if ixExpression in Options then
  1917. FTempMdxTag.KeyFormat := KeyFormat_Expression;
  1918. // what use have these reference tags?
  1919. FTempMdxTag.ForwardTag1 := 0;
  1920. FTempMdxTag.ForwardTag2 := 0;
  1921. FTempMdxTag.BackwardTag := 0;
  1922. FTempMdxTag.Reserved := 2;
  1923. FTempMdxTag.KeyType := fieldType;
  1924. // save this part of tag, need to save before GetNewPageNo,
  1925. // it will reread header
  1926. WriteFileHeader;
  1927. // store selected index
  1928. FSelectedIndex := tagNo;
  1929. FIndexName := TagName;
  1930. // store new headerno
  1931. FHeaderPageNo := GetNewPageNo;
  1932. FTempMdxTag.HeaderPageNo := FHeaderPageNo;
  1933. // increase number of indexes active
  1934. inc(PMdxHdr(Header).TagsUsed);
  1935. // update updatemode
  1936. UpdateMode := umAll;
  1937. // index header updated
  1938. WriteFileHeader;
  1939. end;
  1940. // clear index
  1941. ClearIndex;
  1942. // parse expression, we know it's parseable, we've checked that
  1943. FCurrentParser.ParseExpression(FieldDesc);
  1944. // looked up index expression: now we can edit
  1945. // FIsExpression := ixExpression in Options;
  1946. FCanEdit := not FForceReadOnly;
  1947. // init key variables
  1948. PIndexHdr(FIndexHeader).KeyFormat := 0;
  1949. // descending
  1950. if ixDescending in Options then
  1951. PIndexHdr(FIndexHeader).KeyFormat := PIndexHdr(FIndexHeader).KeyFormat or KeyFormat_Descending;
  1952. // key type
  1953. if fieldType = 'C' then
  1954. PIndexHdr(FIndexHeader).KeyFormat := PIndexHdr(FIndexHeader).KeyFormat or KeyFormat_String;
  1955. PIndexHdr(FIndexHeader).KeyType := fieldType;
  1956. // uniqueness
  1957. PIndexHdr(FIndexHeader).Unique := Unique_None;
  1958. if ixPrimary in Options then
  1959. begin
  1960. PIndexHdr(FIndexHeader).KeyFormat := PIndexHdr(FIndexHeader).KeyFormat or KeyFormat_Distinct or KeyFormat_Unique;
  1961. PIndexHdr(FIndexHeader).Unique := Unique_Distinct;
  1962. end else if ixUnique in Options then
  1963. begin
  1964. PIndexHdr(FIndexHeader).KeyFormat := PIndexHdr(FIndexHeader).KeyFormat or KeyFormat_Unique;
  1965. PIndexHdr(FIndexHeader).Unique := Unique_Unique;
  1966. end;
  1967. // keylen is exact length of field
  1968. if fieldType = 'C' then
  1969. PIndexHdr(FIndexHeader).KeyLen := FCurrentParser.ResultLen
  1970. else if FIndexVersion >= xBaseIV then
  1971. PIndexHdr(FIndexHeader).KeyLen := 12
  1972. else
  1973. PIndexHdr(FIndexHeader).KeyLen := 8;
  1974. CalcKeyProperties;
  1975. // key desc
  1976. StrPLCopy(PIndexHdr(FIndexHeader).KeyDesc, FieldDesc, 219);
  1977. PIndexHdr(FIndexHeader).KeyDesc[219] := #0;
  1978. // init various
  1979. if FIndexVersion >= xBaseIV then
  1980. PIndexHdr(FIndexHeader).Dummy := 0 // MDX -> language driver
  1981. else
  1982. PIndexHdr(FIndexHeader).Dummy := $5800; // NDX -> same ???
  1983. case fieldType of
  1984. 'C':
  1985. PIndexHdr(FIndexHeader).sKeyType := 0;
  1986. 'D':
  1987. PIndexHdr(FIndexHeader).sKeyType := 1;
  1988. 'N', 'F':
  1989. if FIndexVersion >= xBaseIV then
  1990. PIndexHdr(FIndexHeader).sKeyType := 0
  1991. else
  1992. PIndexHdr(FIndexHeader).sKeyType := 1;
  1993. else
  1994. PIndexHdr(FIndexHeader).sKeyType := 0;
  1995. end;
  1996. PIndexHdr(FIndexHeader).Version := 2; // this is what DB4 writes into file
  1997. PIndexHdr(FIndexHeader).Dummy2 := 0;
  1998. PIndexHdr(FIndexHeader).Dummy3 := 0;
  1999. PIndexHdr(FIndexHeader).ForExist := 0; // false
  2000. PIndexHdr(FIndexHeader).KeyExist := 1; // true
  2001. {$ifndef TDBF_UPDATE_FIRSTLAST_NODE}
  2002. // if not defined, init to zero
  2003. PIndexHdr(FIndexHeader).FirstNode := 0;
  2004. PIndexHdr(FIndexHeader).LastNode := 0;
  2005. {$endif}
  2006. WriteHeader;
  2007. // update internal properties
  2008. UpdateIndexProperties;
  2009. // for searches / inserts / deletes
  2010. FKeyBuffer[PIndexHdr(FIndexHeader).KeyLen] := #0;
  2011. end;
  2012. procedure TIndexFile.ReadIndexes;
  2013. var
  2014. I: Integer;
  2015. procedure CheckHeaderIntegrity;
  2016. begin
  2017. if PIndexHdr(FIndexHeader).NumKeys * PIndexHdr(FIndexHeader).KeyRecLen > RecordSize then
  2018. begin
  2019. // adjust index header so that integrity is correct
  2020. // WARNING: we can't be sure this gives a correct result, but at
  2021. // least we won't AV (as easily). user will probably have to regenerate this index
  2022. if PIndexHdr(FIndexHeader).KeyLen > 100 then
  2023. PIndexHdr(FIndexHeader).KeyLen := 100;
  2024. CalcKeyProperties;
  2025. end;
  2026. end;
  2027. begin
  2028. // force header reread
  2029. inherited ReadHeader;
  2030. // examine all indexes
  2031. if FIndexVersion >= xBaseIV then
  2032. begin
  2033. // clear all roots
  2034. ClearRoots;
  2035. // tags are extended at beginning?
  2036. FTagSize := PMdxHdr(Header).TagSize;
  2037. FTagOffset := 544 + FTagSize - 32;
  2038. for I := 0 to PMdxHdr(Header).TagsUsed - 1 do
  2039. begin
  2040. // read page header
  2041. FTempMdxTag.Tag := CalcTagOffset(I);
  2042. ReadRecord(FTempMdxTag.HeaderPageNo, FIndexHeaders[I]);
  2043. // select it
  2044. FIndexHeader := FIndexHeaders[I];
  2045. // create root if needed
  2046. if FRoots[I] = nil then
  2047. begin
  2048. FParsers[I] := TDbfParser.Create(FDbfFile);
  2049. FRoots[I] := TMdxPage.Create(Self);
  2050. end;
  2051. // check header integrity
  2052. CheckHeaderIntegrity;
  2053. // read tree
  2054. FRoots[I].PageNo := PIndexHdr(FIndexHeader).RootPage;
  2055. // go to first record
  2056. FRoots[I].RecurFirst;
  2057. // store leaf
  2058. FLeaves[I] := FRoots[I];
  2059. while FLeaves[I].LowerPage <> nil do
  2060. FLeaves[I] := FLeaves[I].LowerPage;
  2061. // parse expression
  2062. FParsers[I].ParseExpression(PIndexHdr(FIndexHeader).KeyDesc);
  2063. end;
  2064. end else begin
  2065. // clear root
  2066. FRoot.Clear;
  2067. // check recordsize constraint
  2068. CheckHeaderIntegrity;
  2069. // just one index: read tree
  2070. FRoot.PageNo := PIndexHdr(FIndexHeader).RootPage;
  2071. // go to first valid record
  2072. FRoot.RecurFirst;
  2073. // get leaf page
  2074. FLeaf := FRoot;
  2075. while FLeaf.LowerPage <> nil do
  2076. FLeaf := FLeaf.LowerPage;
  2077. // write leaf pointer to first index
  2078. FLeaves[0] := FLeaf;
  2079. // get index properties -> internal props
  2080. UpdateIndexProperties;
  2081. end;
  2082. end;
  2083. procedure TIndexFile.DeleteIndex(const AIndexName: string);
  2084. var
  2085. I, found, numTags, moveItems: Integer;
  2086. tempHeader: Pointer;
  2087. tempRoot, tempLeaf: TIndexPage;
  2088. tempParser: TDbfParser;
  2089. begin
  2090. // check if we have exclusive access to table
  2091. TDbfFile(FDbfFile).CheckExclusiveAccess;
  2092. if FIndexVersion = xBaseIII then
  2093. begin
  2094. Close;
  2095. DeleteFile;
  2096. end else if FIndexVersion >= xBaseIV then
  2097. begin
  2098. // find index
  2099. found := IndexOf(AIndexName);
  2100. if found >= 0 then
  2101. begin
  2102. // just remove this tag by copying memory over it
  2103. numTags := PMdxHdr(Header).TagsUsed;
  2104. moveItems := numTags - found - 1;
  2105. // anything to move?
  2106. if moveItems > 0 then
  2107. begin
  2108. // move entries after found one
  2109. Move((Header + FTagOffset + (found+1) * FTagSize)^,
  2110. (Header + FTagOffset + found * FTagSize)^, moveItems * FTagSize);
  2111. // nullify last entry
  2112. FillChar((Header + FTagOffset + numTags * FTagSize)^, FTagSize, 0);
  2113. // index headers, roots, leaves
  2114. tempHeader := FIndexHeaders[found];
  2115. tempParser := FParsers[found];
  2116. tempRoot := FRoots[found];
  2117. tempLeaf := FLeaves[found];
  2118. for I := 0 to moveItems - 1 do
  2119. begin
  2120. FIndexHeaders[found + I] := FIndexHeaders[found + I + 1];
  2121. FParsers[found + I] := FParsers[found + I + 1];
  2122. FRoots[found + I] := FRoots[found + I + 1];
  2123. FLeaves[found + I] := FLeaves[found + I + 1];
  2124. FHeaderModified[found + I] := true;
  2125. end;
  2126. FIndexHeaders[found + moveItems] := tempHeader;
  2127. FParsers[found + moveItems] := tempParser;
  2128. FRoots[found + moveItems] := tempRoot;
  2129. FLeaves[found + moveItems] := tempLeaf;
  2130. FHeaderModified[found + moveItems] := false; // non-existant header
  2131. end;
  2132. // one entry less left
  2133. dec(PMdxHdr(Header).TagsUsed);
  2134. // ---*** numTags not valid from here ***---
  2135. // file header changed
  2136. WriteFileHeader;
  2137. // repage index to free space used by deleted index
  2138. // RepageFile;
  2139. end;
  2140. end;
  2141. end;
  2142. procedure TIndexFile.TouchHeader(AHeader: Pointer);
  2143. var
  2144. year, month, day: Word;
  2145. begin
  2146. DecodeDate(Now, year, month, day);
  2147. PMdxHdr(AHeader).UpdYear := year - 1900;
  2148. PMdxHdr(AHeader).UpdMonth := month;
  2149. PMdxHdr(AHeader).UpdDay := day;
  2150. end;
  2151. function TIndexFile.CreateTempFile(BaseName: string): TPagedFile;
  2152. var
  2153. lModifier: Integer;
  2154. begin
  2155. // create temporary in-memory index file
  2156. lModifier := 0;
  2157. FindNextName(BaseName, BaseName, lModifier);
  2158. Result := TPagedFile.Create;
  2159. Result.FileName := BaseName;
  2160. Result.Mode := pfExclusiveCreate;
  2161. Result.AutoCreate := true;
  2162. Result.OpenFile;
  2163. Result.HeaderSize := HeaderSize;
  2164. Result.RecordSize := RecordSize;
  2165. Result.PageSize := PageSize;
  2166. Result.PageOffsetByHeader := false;
  2167. end;
  2168. procedure TIndexFile.RepageFile;
  2169. var
  2170. TempFile: TPagedFile;
  2171. TempIdxHeader: PIndexHdr;
  2172. I, newPageNo: Integer;
  2173. prevIndex: Integer;
  2174. function GetNewPageNo: Integer;
  2175. begin
  2176. Result := newPageNo;
  2177. Inc(newPageNo, PagesPerRecord);
  2178. if FIndexVersion >= xBaseIV then
  2179. Inc(PMdxHdr(TempFile.Header).NumPages, PagesPerRecord);
  2180. Inc(TempIdxHeader.NumPages, PagesPerRecord);
  2181. end;
  2182. function WriteTree(NewPage: TIndexPage): Integer;
  2183. var
  2184. J: Integer;
  2185. begin
  2186. // get us a page so that page no's are more logically ordered
  2187. Result := GetNewPageNo;
  2188. // use postorder visiting, first do all children
  2189. if NewPage.LowerPage <> nil then
  2190. begin
  2191. for J := 0 to NewPage.HighIndex do
  2192. begin
  2193. NewPage.EntryNo := J;
  2194. WriteTree(NewPage.LowerPage);
  2195. end;
  2196. end;
  2197. // now create new page for ourselves and write
  2198. // update page pointer in parent
  2199. if NewPage.UpperPage <> nil then
  2200. begin
  2201. if FIndexVersion >= xBaseIV then
  2202. begin
  2203. PMdxEntry(NewPage.UpperPage.Entry).RecBlockNo := Result;
  2204. {$ifdef TDBF_UPDATE_FIRSTLAST_NODE}
  2205. // write previous node
  2206. if FRoot = NewPage then
  2207. PMdxPage(NewPage.PageBuffer).PrevBlock := 0
  2208. else
  2209. PMdxPage(NewPage.PageBuffer).PrevBlock := Result - PagesPerRecord;
  2210. {$endif}
  2211. end else begin
  2212. PNdxEntry(NewPage.UpperPage.Entry).LowerPageNo := Result;
  2213. end;
  2214. end;
  2215. // store page
  2216. TempFile.WriteRecord(Result, NewPage.PageBuffer);
  2217. end;
  2218. procedure CopySelectedIndex;
  2219. var
  2220. hdrPageNo: Integer;
  2221. begin
  2222. // copy current index settings
  2223. Move(FIndexHeader^, TempIdxHeader^, RecordSize);
  2224. // clear number of pages
  2225. TempIdxHeader.NumPages := PagesPerRecord;
  2226. // allocate a page no for header
  2227. hdrPageNo := GetNewPageNo;
  2228. // use recursive function to write all pages
  2229. TempIdxHeader.RootPage := WriteTree(FRoot);
  2230. {$ifdef TDBF_UPDATE_FIRSTLAST_NODE}
  2231. TempIdxHeader.FirstNode := TempIdxHeader.RootPage;
  2232. {$endif}
  2233. // write index header now we know the root page
  2234. TempFile.WriteRecord(hdrPageNo, TempIdxHeader);
  2235. if FIndexVersion >= xBaseIV then
  2236. begin
  2237. // calculate tag offset in tempfile header
  2238. FTempMdxTag.Tag := PChar(TempFile.Header) + (PChar(CalcTagOffset(I)) - Header);
  2239. FTempMdxTag.HeaderPageNo := hdrPageNo;
  2240. end;
  2241. end;
  2242. begin
  2243. CheckExclusiveAccess;
  2244. prevIndex := FSelectedIndex;
  2245. newPageNo := HeaderSize div PageSize;
  2246. TempFile := CreateTempFile(FileName);
  2247. if FIndexVersion >= xBaseIV then
  2248. begin
  2249. // copy header
  2250. Move(Header^, TempFile.Header^, HeaderSize);
  2251. TouchHeader(TempFile.Header);
  2252. // reset header
  2253. PMdxHdr(TempFile.Header).NumPages := HeaderSize div PageSize;
  2254. TempFile.WriteHeader;
  2255. GetMem(TempIdxHeader, RecordSize);
  2256. // now recreate indexes to that file
  2257. for I := 0 to PMdxHdr(Header).TagsUsed - 1 do
  2258. begin
  2259. // select this index
  2260. SelectIndexVars(I);
  2261. CopySelectedIndex;
  2262. end;
  2263. FreeMem(TempIdxHeader);
  2264. end else begin
  2265. // indexversion = xBaseIII
  2266. TempIdxHeader := PIndexHdr(TempFile.Header);
  2267. CopySelectedIndex;
  2268. end;
  2269. TempFile.WriteHeader;
  2270. TempFile.CloseFile;
  2271. CloseFile;
  2272. // rename temporary file if all went successfull
  2273. if not TempFile.WriteError then
  2274. begin
  2275. SysUtils.DeleteFile(FileName);
  2276. SysUtils.RenameFile(TempFile.FileName, FileName);
  2277. end;
  2278. TempFile.Free;
  2279. DisableForceCreate;
  2280. OpenFile;
  2281. ReadIndexes;
  2282. SelectIndexVars(prevIndex);
  2283. end;
  2284. procedure TIndexFile.CompactFile;
  2285. var
  2286. TempFile: TPagedFile;
  2287. TempIdxHeader: PIndexHdr;
  2288. I, newPageNo: Integer;
  2289. prevIndex: Integer;
  2290. function GetNewPageNo: Integer;
  2291. begin
  2292. Result := newPageNo;
  2293. Inc(newPageNo, PagesPerRecord);
  2294. if FIndexVersion >= xBaseIV then
  2295. Inc(PMdxHdr(TempFile.Header).NumPages, PagesPerRecord);
  2296. Inc(TempIdxHeader.NumPages, PagesPerRecord);
  2297. end;
  2298. function CreateNewPage: TIndexPage;
  2299. begin
  2300. // create new page + space
  2301. if FIndexVersion >= xBaseIV then
  2302. Result := TMdxPage.Create(Self)
  2303. else
  2304. Result := TNdxPage.Create(Self);
  2305. Result.FPageNo := GetNewPageNo;
  2306. // set new page properties
  2307. Result.SetNumEntries(0);
  2308. end;
  2309. procedure GetNewEntry(APage: TIndexPage);
  2310. // makes a new entry available and positions current 'pos' on it
  2311. // NOTES: uses TIndexPage *very* carefully
  2312. // - may not read from self (tindexfile)
  2313. // - page.FLowerPage is assigned -> SyncLowerPage may *not* be called
  2314. // - do not set PageNo (= SetPageNo)
  2315. // - do not set EntryNo
  2316. begin
  2317. if APage.HighIndex >= PIndexHdr(FIndexHeader).NumKeys-1 then
  2318. begin
  2319. if APage.UpperPage = nil then
  2320. begin
  2321. // add new upperlevel to page
  2322. APage.FUpperPage := CreateNewPage;
  2323. APage.UpperPage.FLowerPage := APage;
  2324. APage.UpperPage.FEntryNo := 0;
  2325. APage.UpperPage.FEntry := EntryEof;
  2326. APage.UpperPage.GotoInsertEntry;
  2327. APage.UpperPage.LocalInsert(0, APage.Key, APage.PageNo);
  2328. // non-leaf pages need 'rightmost' key; numentries = real# - 1
  2329. APage.UpperPage.SetNumEntries(0);
  2330. end;
  2331. // page done, store
  2332. TempFile.WriteRecord(APage.FPageNo, APage.PageBuffer);
  2333. // allocate new page
  2334. APage.FPageNo := GetNewPageNo;
  2335. // clear
  2336. APage.SetNumEntries(0);
  2337. APage.FHighIndex := -1;
  2338. APage.FLowIndex := 0;
  2339. // clear 'right-most' blockno
  2340. APage.SetRecLowerPageNoOfEntry(0, 0, 0);
  2341. // get new entry in upper page for current new apage
  2342. GetNewEntry(APage.UpperPage);
  2343. APage.UpperPage.LocalInsert(0, nil, 0);
  2344. // non-leaf pages need 'rightmost' key; numentries = real# - 1
  2345. if APage.UpperPage.EntryNo = 0 then
  2346. APage.UpperPage.SetNumEntries(0);
  2347. end;
  2348. APage.FEntryNo := APage.HighIndex+1;
  2349. APage.FEntry := EntryEof;
  2350. APage.GotoInsertEntry;
  2351. end;
  2352. procedure CopySelectedIndex;
  2353. var
  2354. APage: TIndexPage;
  2355. hdrPageNo: Integer;
  2356. begin
  2357. // copy current index settings
  2358. Move(FIndexHeader^, TempIdxHeader^, RecordSize);
  2359. // clear number of pages
  2360. TempIdxHeader.NumPages := PagesPerRecord;
  2361. // allocate a page no for header
  2362. hdrPageNo := GetNewPageNo;
  2363. // copy all records
  2364. APage := CreateNewPage;
  2365. FLeaf.RecurFirst;
  2366. while not (FRoot.Entry = FEntryEof) do
  2367. begin
  2368. GetNewEntry(APage);
  2369. APage.LocalInsert(FLeaf.PhysicalRecNo, FLeaf.Key, 0);
  2370. FLeaf.RecurNext;
  2371. end;
  2372. // flush remaining (partially filled) pages
  2373. repeat
  2374. TempFile.WriteRecord(APage.FPageNo, APage.PageBuffer);
  2375. if APage.UpperPage <> nil then
  2376. APage := APage.UpperPage
  2377. else break;
  2378. until false;
  2379. // copy index header + root page
  2380. TempIdxHeader.RootPage := APage.PageNo;
  2381. {$ifdef TDBF_UPDATE_FIRSTLAST_NODE}
  2382. TempIdxHeader.FirstNode := APage.PageNo;
  2383. {$endif}
  2384. // write index header now we know the root page
  2385. TempFile.WriteRecord(hdrPageNo, TempIdxHeader);
  2386. if FIndexVersion >= xBaseIV then
  2387. begin
  2388. // calculate tag offset in tempfile header
  2389. FTempMdxTag.Tag := PChar(TempFile.Header) + (PChar(CalcTagOffset(I)) - Header);
  2390. FTempMdxTag.HeaderPageNo := hdrPageNo;
  2391. end;
  2392. end;
  2393. begin
  2394. CheckExclusiveAccess;
  2395. prevIndex := FSelectedIndex;
  2396. newPageNo := HeaderSize div PageSize;
  2397. TempFile := CreateTempFile(FileName);
  2398. if FIndexVersion >= xBaseIV then
  2399. begin
  2400. // copy header
  2401. Move(Header^, TempFile.Header^, HeaderSize);
  2402. TouchHeader(TempFile.Header);
  2403. // reset header
  2404. PMdxHdr(TempFile.Header).NumPages := HeaderSize div PageSize;
  2405. TempFile.WriteHeader;
  2406. GetMem(TempIdxHeader, RecordSize);
  2407. // now recreate indexes to that file
  2408. for I := 0 to PMdxHdr(Header).TagsUsed - 1 do
  2409. begin
  2410. // select this index
  2411. SelectIndexVars(I);
  2412. CopySelectedIndex;
  2413. end;
  2414. FreeMem(TempIdxHeader);
  2415. end else begin
  2416. // indexversion = xBaseIII
  2417. TempIdxHeader := PIndexHdr(TempFile.Header);
  2418. CopySelectedIndex;
  2419. end;
  2420. TempFile.WriteHeader;
  2421. TempFile.CloseFile;
  2422. CloseFile;
  2423. // rename temporary file if all went successfull
  2424. if not TempFile.WriteError then
  2425. begin
  2426. SysUtils.DeleteFile(FileName);
  2427. SysUtils.RenameFile(TempFile.FileName, FileName);
  2428. end;
  2429. TempFile.Free;
  2430. DisableForceCreate;
  2431. OpenFile;
  2432. ReadIndexes;
  2433. SelectIndexVars(prevIndex);
  2434. end;
  2435. function TIndexFile.GetNewPageNo: Integer;
  2436. var
  2437. needLockHeader: Boolean;
  2438. begin
  2439. // update header -> lock it if not already locked
  2440. needLockHeader := FHeaderLocked <> 0;
  2441. if needLockHeader then
  2442. begin
  2443. // lock header page
  2444. LockPage(0, true);
  2445. // someone else could be inserting records at the same moment
  2446. if NeedLocks then
  2447. inherited ReadHeader;
  2448. end;
  2449. if FIndexVersion >= xBaseIV then
  2450. begin
  2451. Result := PMdxHdr(Header).NumPages;
  2452. PMdxHdr(Header).NumPages := PMdxHdr(Header).NumPages + PagesPerRecord;
  2453. {$ifdef TDBF_UPDATE_FIRSTLAST_NODE}
  2454. // adjust high page
  2455. PIndexHdr(FIndexHeader).LastNode := Result;
  2456. {$endif}
  2457. WriteFileHeader;
  2458. end else begin
  2459. Result := PIndexHdr(FIndexHeader).NumPages;
  2460. end;
  2461. PIndexHdr(FIndexHeader).NumPages := PIndexHdr(FIndexHeader).NumPages + PagesPerRecord;
  2462. WriteHeader;
  2463. // done updating header -> unlock if locked
  2464. if needLockHeader then
  2465. UnlockPage(0);
  2466. end;
  2467. procedure TIndexFile.Insert(RecNo: Integer; Buffer: PChar); {override;}
  2468. var
  2469. I, curSel: Integer;
  2470. begin
  2471. // check if updating all or only current
  2472. FUserRecNo := RecNo;
  2473. if (FUpdateMode = umAll) or (FSelectedIndex = -1) then
  2474. begin
  2475. // remember currently selected index
  2476. curSel := FSelectedIndex;
  2477. for I := 0 to PMdxHdr(Header).TagsUsed - 1 do
  2478. begin
  2479. SelectIndexVars(I);
  2480. InsertKey(Buffer);
  2481. end;
  2482. // restore previous selected index
  2483. SelectIndexVars(curSel);
  2484. end else begin
  2485. InsertKey(Buffer);
  2486. end;
  2487. // check range, disabled by insert
  2488. ResyncRange(true);
  2489. end;
  2490. function TIndexFile.CheckKeyViolation(Buffer: PChar): Boolean;
  2491. var
  2492. I, curSel: Integer;
  2493. begin
  2494. Result := false;
  2495. FUserRecNo := -2;
  2496. if FIndexVersion = xBaseIV then
  2497. begin
  2498. curSel := FSelectedIndex;
  2499. I := 0;
  2500. while (I < PMdxHdr(Header).TagsUsed) and not Result do
  2501. begin
  2502. SelectIndexVars(I);
  2503. if FUniqueMode = iuDistinct then
  2504. begin
  2505. FUserKey := ExtractKeyFromBuffer(Buffer);
  2506. Result := FindKey(false) = 0;
  2507. end;
  2508. Inc(I);
  2509. end;
  2510. SelectIndexVars(curSel);
  2511. end else begin
  2512. if FUniqueMode = iuDistinct then
  2513. begin
  2514. FUserKey := ExtractKeyFromBuffer(Buffer);
  2515. Result := FindKey(false) = 0;
  2516. end;
  2517. end;
  2518. end;
  2519. function TIndexFile.PrepareKey(Buffer: PChar; ResultType: TExpressionType): PChar;
  2520. var
  2521. FloatRec: TFloatRec;
  2522. I, IntSrc, NumDecimals: Integer;
  2523. ExtValue: Extended;
  2524. BCDdigit: Byte;
  2525. {$ifdef SUPPORT_INT64}
  2526. Int64Src: Int64;
  2527. {$endif}
  2528. begin
  2529. // need to convert numeric?
  2530. Result := Buffer;
  2531. if PIndexHdr(FIndexHeader).KeyType in ['N', 'F'] then
  2532. begin
  2533. if FIndexVersion = xBaseIII then
  2534. begin
  2535. // DB3 -> index always 8 byte float, if original integer, convert to double
  2536. case ResultType of
  2537. etInteger:
  2538. begin
  2539. FUserNumeric := PInteger(Result)^;
  2540. Result := PChar(@FUserNumeric);
  2541. end;
  2542. {$ifdef SUPPORT_INT64}
  2543. etLargeInt:
  2544. begin
  2545. FUserNumeric := PLargeInt(Result)^;
  2546. Result := PChar(@FUserNumeric);
  2547. end;
  2548. {$endif}
  2549. end;
  2550. end else begin
  2551. // DB4 MDX
  2552. NumDecimals := 0;
  2553. case ResultType of
  2554. etInteger:
  2555. begin
  2556. IntSrc := PInteger(Result)^;
  2557. // handle zero differently: no decimals
  2558. if IntSrc <> 0 then
  2559. NumDecimals := GetStrFromInt(IntSrc, @FloatRec.Digits[0])
  2560. else
  2561. NumDecimals := 0;
  2562. FloatRec.Negative := IntSrc < 0;
  2563. end;
  2564. {$ifdef SUPPORT_INT64}
  2565. etLargeInt:
  2566. begin
  2567. Int64Src := PLargeInt(Result)^;
  2568. if Int64Src <> 0 then
  2569. NumDecimals := GetStrFromInt64(Int64Src, @FloatRec.Digits[0])
  2570. else
  2571. NumDecimals := 0;
  2572. FloatRec.Negative := Int64Src < 0;
  2573. end;
  2574. {$endif}
  2575. etFloat:
  2576. begin
  2577. ExtValue := PDouble(Result)^;
  2578. FloatToDecimal(FloatRec, ExtValue, {$ifndef FPC_VERSION}fvExtended,{$endif} 9999, 15);
  2579. if ExtValue <> 0.0 then
  2580. NumDecimals := StrLen(@FloatRec.Digits[0])
  2581. else
  2582. NumDecimals := 0;
  2583. // maximum number of decimals possible to encode in BCD is 16
  2584. if NumDecimals > 16 then
  2585. NumDecimals := 16;
  2586. end;
  2587. end;
  2588. case ResultType of
  2589. etInteger {$ifdef SUPPORT_INT64}, etLargeInt{$endif}:
  2590. begin
  2591. FloatRec.Exponent := NumDecimals;
  2592. // MDX-BCD does not count ending zeroes as `data' space length
  2593. while (NumDecimals > 0) and (FloatRec.Digits[NumDecimals-1] = '0') do
  2594. Dec(NumDecimals);
  2595. // null-terminate string
  2596. FloatRec.Digits[NumDecimals] := #0;
  2597. end;
  2598. end;
  2599. // write 'header', contains number of digits before decimal separator
  2600. FUserBCD[0] := $34 + FloatRec.Exponent;
  2601. // clear rest of BCD
  2602. FillChar(FUserBCD[1], SizeOf(FUserBCD)-1, 0);
  2603. // store number of bytes used (in number of bits + 1)
  2604. FUserBCD[1] := (((NumDecimals+1) div 2) * 8) + 1;
  2605. // where to store decimal dot position? now implicitly in first byte
  2606. // store negative sign
  2607. if FloatRec.Negative then
  2608. FUserBCD[1] := FUserBCD[1] or $80;
  2609. // convert string to BCD
  2610. I := 0;
  2611. while I < NumDecimals do
  2612. begin
  2613. // only one byte left?
  2614. if FloatRec.Digits[I+1] = #0 then
  2615. BCDdigit := 0
  2616. else
  2617. BCDdigit := Byte(FloatRec.Digits[I+1]) - Byte('0');
  2618. // pack two bytes into bcd
  2619. FUserBCD[2+(I div 2)] := ((Byte(FloatRec.Digits[I]) - Byte('0')) shl 4) or BCDdigit;
  2620. // goto next 2 bytes
  2621. Inc(I, 2);
  2622. end;
  2623. // set result pointer to BCD
  2624. Result := PChar(@FUserBCD[0]);
  2625. end;
  2626. end;
  2627. end;
  2628. function TIndexFile.ExtractKeyFromBuffer(Buffer: PChar): PChar;
  2629. begin
  2630. // execute expression to get key
  2631. Result := PrepareKey(FCurrentParser.ExtractFromBuffer(Buffer), FCurrentParser.ResultType);
  2632. end;
  2633. procedure TIndexFile.InsertKey(Buffer: PChar);
  2634. begin
  2635. // check proper index and modifiability
  2636. if FCanEdit and (PIndexHdr(FIndexHeader).KeyLen <> 0) then
  2637. begin
  2638. // get key from buffer
  2639. FUserKey := ExtractKeyFromBuffer(Buffer);
  2640. // patch through
  2641. InsertCurrent;
  2642. end;
  2643. end;
  2644. procedure TIndexFile.InsertCurrent;
  2645. // insert in current index
  2646. // assumes: FUserKey is an OEM key
  2647. var
  2648. SearchKey: array[0..100] of Char;
  2649. OemKey: PChar;
  2650. begin
  2651. // only insert if not recalling or mode = distinct
  2652. // modify = mmDeleteRecall /\ unique <> distinct -> key already present
  2653. if (FModifyMode <> mmDeleteRecall) or (FUniqueMode = iuDistinct) then
  2654. begin
  2655. // translate OEM key to ANSI key for searching
  2656. OemKey := FUserKey;
  2657. if KeyType = 'C' then
  2658. begin
  2659. FUserKey := @SearchKey[0];
  2660. TranslateToANSI(OemKey, FUserKey);
  2661. end;
  2662. // temporarily remove range to find correct location of key
  2663. ResetRange;
  2664. // find this record as closely as possible
  2665. // if result = 0 then key already exists
  2666. // if unique index, then don't insert key if already present
  2667. if (FindKey(true) <> 0) or (FUniqueMode = iuNormal) then
  2668. begin
  2669. // switch to oem key
  2670. FUserKey := OemKey;
  2671. // if we found eof, write to pagebuffer
  2672. FLeaf.GotoInsertEntry;
  2673. // insert requested entry, we know there is an entry available
  2674. FLeaf.LocalInsert(FUserRecNo, FUserKey, 0);
  2675. end else begin
  2676. // key already exists -> test possible key violation
  2677. if FUniqueMode = iuDistinct then
  2678. begin
  2679. // raising -> reset modify mode
  2680. FModifyMode := mmNormal;
  2681. InsertError;
  2682. end;
  2683. end;
  2684. end;
  2685. end;
  2686. procedure TIndexFile.InsertError;
  2687. var
  2688. InfoKey: string;
  2689. begin
  2690. // prepare info for user
  2691. InfoKey := FUserKey;
  2692. SetLength(InfoKey, KeyLen);
  2693. raise EDbfError.CreateFmt(STRING_KEY_VIOLATION, [GetName, PhysicalRecNo, TrimRight(InfoKey)]);
  2694. end;
  2695. procedure TIndexFile.Delete(RecNo: Integer; Buffer: PChar);
  2696. var
  2697. I, curSel: Integer;
  2698. begin
  2699. // check if updating all or only current
  2700. FUserRecNo := RecNo;
  2701. if (FUpdateMode = umAll) or (FSelectedIndex = -1) then
  2702. begin
  2703. // remember currently selected index
  2704. curSel := FSelectedIndex;
  2705. for I := 0 to PMdxHdr(Header).TagsUsed - 1 do
  2706. begin
  2707. SelectIndexVars(I);
  2708. DeleteKey(Buffer);
  2709. end;
  2710. // restore previous selected index
  2711. SelectIndexVars(curSel);
  2712. end else begin
  2713. DeleteKey(Buffer);
  2714. end;
  2715. // range may be changed
  2716. ResyncRange(true);
  2717. end;
  2718. procedure TIndexFile.DeleteKey(Buffer: PChar);
  2719. begin
  2720. if FCanEdit and (PIndexHdr(FIndexHeader).KeyLen <> 0) then
  2721. begin
  2722. // get key from record buffer
  2723. FUserKey := ExtractKeyFromBuffer(Buffer);
  2724. // call function
  2725. DeleteCurrent;
  2726. end;
  2727. end;
  2728. procedure TIndexFile.DeleteCurrent;
  2729. // deletes from current index
  2730. var
  2731. SearchKey: array[0..100] of Char;
  2732. OemKey: PChar;
  2733. begin
  2734. // only delete if not delete record or mode = distinct
  2735. // modify = mmDeleteRecall /\ unique = distinct -> key needs to be deleted from index
  2736. if (FModifyMode <> mmDeleteRecall) or (FUniqueMode = iuDistinct) then
  2737. begin
  2738. // prevent "confined" view of index while deleting
  2739. ResetRange;
  2740. // search correct entry to delete
  2741. if FLeaf.PhysicalRecNo <> FUserRecNo then
  2742. begin
  2743. // translate OEM key to ANSI key for searching
  2744. OemKey := FUserKey;
  2745. if KeyType = 'C' then
  2746. begin
  2747. FUserKey := @SearchKey[0];
  2748. TranslateToANSI(OemKey, FUserKey);
  2749. end;
  2750. FindKey(false);
  2751. end;
  2752. // delete selected entry
  2753. FLeaf.Delete;
  2754. end;
  2755. end;
  2756. procedure TIndexFile.Update(RecNo: Integer; PrevBuffer, NewBuffer: PChar);
  2757. var
  2758. I, curSel: Integer;
  2759. begin
  2760. // check if updating all or only current
  2761. FUserRecNo := RecNo;
  2762. if (FUpdateMode = umAll) or (FSelectedIndex = -1) then
  2763. begin
  2764. // remember currently selected index
  2765. curSel := FSelectedIndex;
  2766. for I := 0 to PMdxHdr(Header).TagsUsed - 1 do
  2767. begin
  2768. SelectIndexVars(I);
  2769. UpdateCurrent(PrevBuffer, NewBuffer);
  2770. end;
  2771. // restore previous selected index
  2772. SelectIndexVars(curSel);
  2773. end else begin
  2774. UpdateCurrent(PrevBuffer, NewBuffer);
  2775. end;
  2776. end;
  2777. procedure TIndexFile.UpdateCurrent(PrevBuffer, NewBuffer: PChar);
  2778. var
  2779. TempBuffer: array [0..100] of Char;
  2780. begin
  2781. if FCanEdit and (PIndexHdr(FIndexHeader).KeyLen <> 0) then
  2782. begin
  2783. // get key from newbuffer
  2784. FUserKey := ExtractKeyFromBuffer(NewBuffer);
  2785. Move(FUserKey^, TempBuffer, PIndexHdr(FIndexHeader).KeyLen);
  2786. // get key from prevbuffer
  2787. FUserKey := ExtractKeyFromBuffer(PrevBuffer);
  2788. // compare to see if anything changed
  2789. if CompareKeys(@TempBuffer[0], FUserKey) <> 0 then
  2790. begin
  2791. // first set userkey to key to delete
  2792. // FUserKey = KeyFrom(PrevBuffer)
  2793. DeleteCurrent;
  2794. // now set userkey to key to insert
  2795. FUserKey := @TempBuffer[0];
  2796. InsertCurrent;
  2797. // check range, disabled by delete/insert
  2798. ResyncRange(true);
  2799. end;
  2800. end;
  2801. end;
  2802. procedure TIndexFile.AddNewLevel;
  2803. var
  2804. lNewPage: TIndexPage;
  2805. pKeyData: PChar;
  2806. begin
  2807. // create new page + space
  2808. if FIndexVersion >= xBaseIV then
  2809. lNewPage := TMdxPage.Create(Self)
  2810. else
  2811. lNewPage := TNdxPage.Create(Self);
  2812. lNewPage.GetNewPage;
  2813. // lock this new page; will be unlocked by caller
  2814. lNewPage.LockPage;
  2815. // lock index header; will be unlocked by caller
  2816. LockPage(FHeaderPageNo, true);
  2817. FHeaderLocked := FHeaderPageNo;
  2818. // modify header
  2819. PIndexHdr(FIndexHeader).RootPage := lNewPage.PageNo;
  2820. // set new page properties
  2821. lNewPage.SetNumEntries(0);
  2822. lNewPage.EntryNo := 0;
  2823. lNewPage.GotoInsertEntry;
  2824. {$ifdef TDBF_UPDATE_FIRST_LAST_NODE}
  2825. lNewPage.SetPrevBlock(lNewPage.PageNo - PagesPerRecord);
  2826. {$endif}
  2827. pKeyData := FRoot.GetKeyDataFromEntry(0);
  2828. lNewPage.FLowerPage := FRoot;
  2829. lNewPage.FHighIndex := 0;
  2830. lNewPage.SetEntry(0, pKeyData, FRoot.PageNo);
  2831. // update root pointer
  2832. FRoot.UpperPage := lNewPage;
  2833. FRoots[FSelectedIndex] := lNewPage;
  2834. FRoot := lNewPage;
  2835. // write new header
  2836. WriteRecord(FHeaderPageNo, FIndexHeader);
  2837. end;
  2838. procedure TIndexFile.UnlockHeader;
  2839. begin
  2840. if FHeaderLocked <> -1 then
  2841. begin
  2842. UnlockPage(FHeaderLocked);
  2843. FHeaderLocked := -1;
  2844. end;
  2845. end;
  2846. procedure TIndexFile.ResyncRoot;
  2847. begin
  2848. if FIndexVersion >= xBaseIV then
  2849. begin
  2850. // read header page
  2851. inherited ReadRecord(FHeaderPageNo, FIndexHeader);
  2852. end else
  2853. inherited ReadHeader;
  2854. // reread tree
  2855. FRoot.PageNo := PIndexHdr(FIndexHeader).RootPage;
  2856. end;
  2857. function TIndexFile.SearchKey(Key: PChar; SearchType: TSearchKeyType): Boolean;
  2858. var
  2859. findres, currRecNo: Integer;
  2860. begin
  2861. // save current position
  2862. currRecNo := SequentialRecNo;
  2863. // search, these are always from the root: no need for first
  2864. findres := Find(-2, Key);
  2865. // test result
  2866. case SearchType of
  2867. stEqual:
  2868. Result := findres = 0;
  2869. stGreaterEqual:
  2870. Result := findres <= 0;
  2871. stGreater:
  2872. begin
  2873. if findres = 0 then
  2874. begin
  2875. // find next record that is greater
  2876. // NOTE: MatchKey assumes key to search for is already specified
  2877. // in FUserKey, it is because we have called Find
  2878. repeat
  2879. Result := WalkNext;
  2880. until not Result or (MatchKey(Key) <> 0);
  2881. end else
  2882. Result := findres < 0;
  2883. end;
  2884. else
  2885. Result := false;
  2886. end;
  2887. // search failed -> restore previous position
  2888. if not Result then
  2889. SequentialRecNo := currRecNo;
  2890. end;
  2891. function TIndexFile.Find(RecNo: Integer; Buffer: PChar): Integer;
  2892. begin
  2893. // execute find
  2894. FUserRecNo := RecNo;
  2895. FUserKey := Buffer;
  2896. Result := FindKey(false);
  2897. end;
  2898. function TIndexFile.FindKey(Insert: boolean): Integer;
  2899. //
  2900. // if you set Insert = true, you need to re-enable range after insert!!
  2901. //
  2902. var
  2903. TempPage, NextPage: TIndexPage;
  2904. numEntries, numKeysAvail, done, searchRecNo: Integer;
  2905. begin
  2906. // reread index header (to discover whether root page changed)
  2907. if NeedLocks then
  2908. ResyncRoot;
  2909. // if distinct or unique index -> every entry only occurs once ->
  2910. // does not matter which recno we search -> search recno = -2 ->
  2911. // extra info = recno
  2912. if (FUniqueMode = iuNormal) then
  2913. begin
  2914. // if inserting, search last entry matching key
  2915. if Insert then
  2916. searchRecNo := -3
  2917. else
  2918. searchRecNo := FUserRecNo
  2919. end else begin
  2920. searchRecNo := -2;
  2921. end;
  2922. // start from root
  2923. TempPage := FRoot;
  2924. repeat
  2925. // find key
  2926. done := 0;
  2927. Result := TempPage.FindNearest(searchRecNo);
  2928. if TempPage.LowerPage = nil then
  2929. begin
  2930. // if key greater than last, try next leaf
  2931. if (Result > 0) and (searchRecNo > 0) then
  2932. begin
  2933. // find first parent in tree so we can advance to next item
  2934. NextPage := TempPage;
  2935. repeat
  2936. NextPage := NextPage.UpperPage;
  2937. until (NextPage = nil) or (NextPage.EntryNo < NextPage.HighIndex);
  2938. // found page?
  2939. if NextPage <> nil then
  2940. begin
  2941. // go to parent
  2942. TempPage := NextPage;
  2943. TempPage.EntryNo := TempPage.EntryNo + 1;
  2944. // resync rest of tree
  2945. TempPage.LowerPage.RecurFirst;
  2946. // go to lower page to continue search
  2947. TempPage := TempPage.LowerPage;
  2948. // check if still more lowerpages
  2949. if TempPage.LowerPage <> nil then
  2950. begin
  2951. // flag we need to traverse down further
  2952. done := 2;
  2953. end else begin
  2954. // this is next child, we don't know if found
  2955. done := 1;
  2956. end;
  2957. end;
  2958. end;
  2959. end else begin
  2960. // need to traverse lower down
  2961. done := 2;
  2962. end;
  2963. // check if we need to split page
  2964. // done = 1 -> not found entry on insert path yet
  2965. if Insert and (done <> 1) then
  2966. begin
  2967. // now we are on our path to destination where entry is to be inserted
  2968. // check if this page is full, then split it
  2969. numEntries := TempPage.NumEntries;
  2970. // if this is inner node, we can only store one less than max entries
  2971. numKeysAvail := PIndexHdr(FIndexHeader).NumKeys - numEntries;
  2972. if TempPage.LowerPage <> nil then
  2973. dec(numKeysAvail);
  2974. // too few available -> split
  2975. if numKeysAvail = 0 then
  2976. TempPage.Split;
  2977. end;
  2978. // do we need to go lower down?
  2979. if done = 2 then
  2980. TempPage := TempPage.LowerPage;
  2981. until done = 0;
  2982. end;
  2983. function TIndexFile.MatchKey(UserKey: PChar): Integer;
  2984. begin
  2985. // BOF and EOF always false
  2986. if FLeaf.Entry = FEntryBof then
  2987. Result := 1
  2988. else
  2989. if FLeaf.Entry = FEntryEof then
  2990. Result := -1
  2991. else begin
  2992. FUserKey := UserKey;
  2993. Result := FLeaf.MatchKey;
  2994. end;
  2995. end;
  2996. procedure TIndexFile.SetRange(LowRange, HighRange: PChar);
  2997. begin
  2998. Move(LowRange^, FLowBuffer[0], KeyLen);
  2999. Move(HighRange^, FHighBuffer[0], KeyLen);
  3000. FRangeActive := true;
  3001. ResyncRange(true);
  3002. end;
  3003. procedure TIndexFile.RecordDeleted(RecNo: Integer; Buffer: PChar);
  3004. begin
  3005. // are we distinct -> then delete record from index
  3006. FModifyMode := mmDeleteRecall;
  3007. Delete(RecNo, Buffer);
  3008. FModifyMode := mmNormal;
  3009. end;
  3010. procedure TIndexFile.RecordRecalled(RecNo: Integer; Buffer: PChar);
  3011. begin
  3012. // are we distinct -> then reinsert record in index
  3013. FModifyMode := mmDeleteRecall;
  3014. Insert(RecNo, Buffer);
  3015. FModifyMode := mmNormal;
  3016. end;
  3017. procedure TIndexFile.SetLocaleID(const NewID: LCID);
  3018. {$ifdef WIN32}
  3019. var
  3020. InfoStr: array[0..7] of Char;
  3021. {$endif}
  3022. begin
  3023. FLocaleID := NewID;
  3024. if NewID = lcidBinary then
  3025. begin
  3026. // no conversion on binary sort order
  3027. FLocaleCP := FCodePage;
  3028. end else begin
  3029. // get default ansi codepage for comparestring
  3030. {$ifdef WIN32}
  3031. GetLocaleInfo(NewID, LOCALE_IDEFAULTANSICODEPAGE, InfoStr, 8);
  3032. FLocaleCP := StrToIntDef(InfoStr, GetACP);
  3033. {$else}
  3034. FLocaleCP := GetACP;
  3035. {$endif}
  3036. end;
  3037. end;
  3038. procedure TIndexFile.SetPhysicalRecNo(RecNo: Integer);
  3039. begin
  3040. // check if already at specified recno
  3041. if FLeaf.PhysicalRecNo = RecNo then
  3042. exit;
  3043. // check record actually exists
  3044. if TDbfFile(FDbfFile).IsRecordPresent(RecNo) then
  3045. begin
  3046. // read buffer of this RecNo
  3047. TDbfFile(FDbfFile).ReadRecord(RecNo, TDbfFile(FDbfFile).PrevBuffer);
  3048. // extract key
  3049. FUserKey := ExtractKeyFromBuffer(TDbfFile(FDbfFile).PrevBuffer);
  3050. // translate to a search key
  3051. if KeyType = 'C' then
  3052. TranslateToANSI(FUserKey, FUserKey);
  3053. // find this key
  3054. FUserRecNo := RecNo;
  3055. FindKey(false);
  3056. end;
  3057. end;
  3058. procedure TIndexFile.SetUpdateMode(NewMode: TIndexUpdateMode);
  3059. begin
  3060. // if there is only one index, don't waste time and just set single
  3061. if (FIndexVersion = xBaseIII) or (PMdxHdr(Header).TagsUsed <= 1) then
  3062. FUpdateMode := umCurrent
  3063. else
  3064. FUpdateMode := NewMode;
  3065. end;
  3066. procedure TIndexFile.WalkFirst;
  3067. begin
  3068. // search first node
  3069. FRoot.RecurFirst;
  3070. // out of index - BOF
  3071. FLeaf.EntryNo := FLeaf.EntryNo - 1;
  3072. end;
  3073. procedure TIndexFile.WalkLast;
  3074. begin
  3075. // search last node
  3076. FRoot.RecurLast;
  3077. // out of index - EOF
  3078. // we need to skip two entries to go out-of-bound
  3079. FLeaf.EntryNo := FLeaf.EntryNo + 2;
  3080. end;
  3081. procedure TIndexFile.First;
  3082. begin
  3083. // resync tree
  3084. Resync(false);
  3085. WalkFirst;
  3086. end;
  3087. procedure TIndexFile.Last;
  3088. begin
  3089. // resync tree
  3090. Resync(false);
  3091. WalkLast;
  3092. end;
  3093. procedure TIndexFile.ResyncRange(KeepPosition: boolean);
  3094. var
  3095. Result: Boolean;
  3096. currRecNo: integer;
  3097. begin
  3098. if not FRangeActive then
  3099. exit;
  3100. // disable current range if any
  3101. if KeepPosition then
  3102. currRecNo := SequentialRecNo;
  3103. ResetRange;
  3104. // search lower bound
  3105. Result := SearchKey(FLowBuffer, stGreaterEqual);
  3106. if not Result then
  3107. begin
  3108. // not found? -> make empty range
  3109. WalkLast;
  3110. end;
  3111. // set lower bound
  3112. SetBracketLow;
  3113. // search upper bound
  3114. Result := SearchKey(FHighBuffer, stGreater);
  3115. // if result true, then need to get previous item <=>
  3116. // last of equal/lower than key
  3117. if Result then
  3118. begin
  3119. Result := WalkPrev;
  3120. if not Result then
  3121. begin
  3122. // cannot go prev -> empty range
  3123. WalkFirst;
  3124. end;
  3125. end else begin
  3126. // not found -> EOF found, go EOF, then to last record
  3127. WalkLast;
  3128. WalkPrev;
  3129. end;
  3130. // set upper bound
  3131. SetBracketHigh;
  3132. if KeepPosition then
  3133. SequentialRecNo := currRecNo;
  3134. end;
  3135. procedure TIndexFile.Resync(Relative: boolean);
  3136. begin
  3137. if NeedLocks then
  3138. begin
  3139. if not Relative then
  3140. begin
  3141. ResyncRoot;
  3142. ResyncRange(false);
  3143. end else begin
  3144. // resyncing tree implies resyncing range
  3145. ResyncTree;
  3146. end;
  3147. end;
  3148. end;
  3149. procedure TIndexFile.ResyncTree;
  3150. var
  3151. action, recno: integer;
  3152. begin
  3153. // if at BOF or EOF, then we need to resync by first or last
  3154. // remember where the cursor was
  3155. if FLeaf.Entry = FEntryBof then
  3156. begin
  3157. action := 0;
  3158. end else if FLeaf.Entry = FEntryEof then begin
  3159. action := 1;
  3160. end else begin
  3161. // read current key into buffer
  3162. Move(FLeaf.Key^, FKeyBuffer, PIndexHdr(FIndexHeader).KeyLen);
  3163. // translate to searchable key
  3164. if KeyType = 'C' then
  3165. TranslateToANSI(FKeyBuffer, FKeyBuffer);
  3166. recno := FLeaf.PhysicalRecNo;
  3167. action := 2;
  3168. end;
  3169. // we now know cursor position, resync possible range
  3170. ResyncRange(false);
  3171. // go to cursor position
  3172. case action of
  3173. 0: WalkFirst;
  3174. 1: WalkLast;
  3175. 2:
  3176. begin
  3177. // search current in-mem key on disk
  3178. if (Find(recno, FKeyBuffer) <> 0) then
  3179. begin
  3180. // houston, we've got a problem!
  3181. // our `current' record has gone. we need to find it
  3182. // find it by using physical recno
  3183. PhysicalRecNo := recno;
  3184. end;
  3185. end;
  3186. end;
  3187. end;
  3188. function TIndexFile.WalkPrev: boolean;
  3189. var
  3190. curRecNo: Integer;
  3191. begin
  3192. // save current recno, find different next!
  3193. curRecNo := FLeaf.PhysicalRecNo;
  3194. repeat
  3195. // return false if we are at first entry
  3196. Result := FLeaf.RecurPrev;
  3197. until not Result or (curRecNo <> FLeaf.PhysicalRecNo);
  3198. end;
  3199. function TIndexFile.WalkNext: boolean;
  3200. var
  3201. curRecNo: Integer;
  3202. begin
  3203. // save current recno, find different prev!
  3204. curRecNo := FLeaf.PhysicalRecNo;
  3205. repeat
  3206. // return false if we are at last entry
  3207. Result := FLeaf.RecurNext;
  3208. until not Result or (curRecNo <> FLeaf.PhysicalRecNo);
  3209. end;
  3210. function TIndexFile.Prev: Boolean;
  3211. begin
  3212. // resync in-mem tree with tree on disk
  3213. Resync(true);
  3214. Result := WalkPrev;
  3215. end;
  3216. function TIndexFile.Next: Boolean;
  3217. begin
  3218. // resync in-mem tree with tree on disk
  3219. Resync(true);
  3220. Result := WalkNext;
  3221. end;
  3222. function TIndexFile.GetKeyLen: Integer;
  3223. begin
  3224. Result := PIndexHdr(FIndexHeader).KeyLen;
  3225. end;
  3226. function TIndexFile.GetKeyType: Char;
  3227. begin
  3228. Result := PIndexHdr(FIndexHeader).KeyType;
  3229. end;
  3230. function TIndexFile.GetPhysicalRecNo: Integer;
  3231. begin
  3232. Result := FLeaf.PhysicalRecNo;
  3233. end;
  3234. function TIndexFile.GetSequentialRecordCount: Integer;
  3235. begin
  3236. Result := FRoot.Weight * (FRoot.HighIndex + 1);
  3237. end;
  3238. function TIndexFile.GetSequentialRecNo: Integer;
  3239. var
  3240. TempPage: TIndexPage;
  3241. begin
  3242. // check if at BOF or EOF, special values
  3243. if FLeaf.EntryNo < FLeaf.LowIndex then begin
  3244. Result := RecBOF;
  3245. end else if FLeaf.EntryNo > FLeaf.HighIndex then begin
  3246. Result := RecEOF;
  3247. end else begin
  3248. // first record is record 1
  3249. Result := 1;
  3250. TempPage := FRoot;
  3251. repeat
  3252. inc(Result, TempPage.EntryNo * TempPage.Weight);
  3253. TempPage := TempPage.LowerPage;
  3254. until TempPage = nil;
  3255. end;
  3256. end;
  3257. procedure TIndexFile.SetSequentialRecNo(RecNo: Integer);
  3258. var
  3259. TempPage: TIndexPage;
  3260. gotoEntry: Integer;
  3261. begin
  3262. // use our weighting system to quickly go to a seq recno
  3263. // recno starts at 1, entries at zero
  3264. Dec(RecNo);
  3265. TempPage := FRoot;
  3266. repeat
  3267. // don't div by zero
  3268. assert(TempPage.Weight > 0);
  3269. gotoEntry := RecNo div TempPage.Weight;
  3270. RecNo := RecNo mod TempPage.Weight;
  3271. // do we have this much entries?
  3272. if (TempPage.HighIndex < gotoEntry) then
  3273. begin
  3274. // goto next entry in upper page if not
  3275. // if recurnext fails, we have come at the end of the index
  3276. if (TempPage.UpperPage <> nil) and TempPage.UpperPage.RecurNext then
  3277. begin
  3278. // lower recno to get because we skipped an entry
  3279. TempPage.EntryNo := TempPage.LowIndex;
  3280. RecNo := 0;
  3281. end else begin
  3282. // this can only happen if too big RecNo was entered, go to last
  3283. TempPage.RecurLast;
  3284. // terminate immediately
  3285. TempPage := FLeaf;
  3286. end;
  3287. end else begin
  3288. TempPage.EntryNo := gotoEntry;
  3289. end;
  3290. // get lower node
  3291. TempPage := TempPage.LowerPage;
  3292. until TempPage = nil;
  3293. end;
  3294. procedure TIndexFile.SetBracketLow;
  3295. var
  3296. TempPage: TIndexPage;
  3297. begin
  3298. // set current record as lower bound
  3299. TempPage := FRoot;
  3300. repeat
  3301. TempPage.LowBracket := TempPage.EntryNo;
  3302. TempPage.LowPage := TempPage.PageNo;
  3303. TempPage := TempPage.LowerPage;
  3304. until TempPage = nil;
  3305. end;
  3306. procedure TIndexFile.SetBracketHigh;
  3307. var
  3308. TempPage: TIndexPage;
  3309. begin
  3310. // set current record as lower bound
  3311. TempPage := FRoot;
  3312. repeat
  3313. TempPage.HighBracket := TempPage.EntryNo;
  3314. TempPage.HighPage := TempPage.PageNo;
  3315. TempPage := TempPage.LowerPage;
  3316. until TempPage = nil;
  3317. end;
  3318. procedure TIndexFile.CancelRange;
  3319. begin
  3320. FRangeActive := false;
  3321. ResetRange;
  3322. end;
  3323. procedure TIndexFile.ResetRange;
  3324. var
  3325. TempPage: TIndexPage;
  3326. begin
  3327. // disable lower + upper bound
  3328. TempPage := FRoot;
  3329. repeat
  3330. // set a page the index should never reach
  3331. TempPage.LowPage := 0;
  3332. TempPage.HighPage := 0;
  3333. TempPage := TempPage.LowerPage;
  3334. until TempPage = nil;
  3335. end;
  3336. function MemComp(P1, P2: Pointer; const Length: Integer): Integer;
  3337. var
  3338. I: Integer;
  3339. begin
  3340. for I := 0 to Length - 1 do
  3341. begin
  3342. // still equal?
  3343. if PByte(P1)^ <> PByte(P2)^ then
  3344. begin
  3345. Result := Integer(PByte(P1)^) - Integer(PByte(P2)^);
  3346. exit;
  3347. end;
  3348. // go to next byte
  3349. Inc(PChar(P1));
  3350. Inc(PChar(P2));
  3351. end;
  3352. // memory equal
  3353. Result := 0;
  3354. end;
  3355. function TIndexFile.CompareKeys(Key1, Key2: PChar): Integer;
  3356. begin
  3357. // call compare routine
  3358. Result := FCompareKeys(Key1, Key2);
  3359. // if descending then reverse order
  3360. if FIsDescending then
  3361. Result := -Result;
  3362. end;
  3363. function TIndexFile.CompareKeysNumericNDX(Key1, Key2: PChar): Integer;
  3364. var
  3365. v1,v2: Double;
  3366. begin
  3367. v1 := PDouble(Key1)^;
  3368. v2 := PDouble(Key2)^;
  3369. if v1 > v2 then Result := 1
  3370. else if v1 < v2 then Result := -1
  3371. else Result := 0;
  3372. end;
  3373. function TIndexFile.CompareKeysNumericMDX(Key1, Key2: PChar): Integer;
  3374. var
  3375. neg1, neg2: Boolean;
  3376. begin
  3377. // first byte - $34 contains dot position
  3378. neg1 := (Byte(Key1[1]) and $80) <> 0;
  3379. neg2 := (Byte(Key2[1]) and $80) <> 0;
  3380. // check if both negative or both positive
  3381. if neg1 = neg2 then
  3382. begin
  3383. // check alignment
  3384. if Key1[0] = Key2[0] then
  3385. begin
  3386. // no alignment needed -> have same alignment
  3387. Result := MemComp(Key1+2, Key2+2, 10-2);
  3388. end else begin
  3389. // greater 10-power implies bigger number except for zero
  3390. if (Byte(Key1[0]) = $01) and (Byte(Key1[1]) = $34) then
  3391. Result := -1
  3392. else
  3393. if (Byte(Key2[0]) = $01) and (Byte(Key2[1]) = $34) then
  3394. Result := 1
  3395. else
  3396. Result := Byte(Key1[0]) - Byte(Key2[0]);
  3397. end;
  3398. // negate result if both negative
  3399. if neg1 and neg2 then
  3400. Result := -Result;
  3401. end else if neg1 {-> not neg2} then
  3402. Result := -1
  3403. else { not neg1 and neg2 }
  3404. Result := 1;
  3405. end;
  3406. function TIndexFile.CompareKeysString(Key1, Key2: PChar): Integer;
  3407. var
  3408. Key1T, Key2T: array [0..100] of Char;
  3409. FromCP, ToCP: Integer;
  3410. begin
  3411. if FLocaleID = LCID(lcidBinary) then
  3412. begin
  3413. Result := StrLComp(Key1, Key2, KeyLen)
  3414. end else begin
  3415. FromCP := FCodePage;
  3416. ToCP := FLocaleCP;
  3417. TranslateString(FromCP, ToCP, Key1, Key1T, KeyLen);
  3418. TranslateString(FromCP, ToCP, Key2, Key2T, KeyLen);
  3419. Result := CompareString(FLocaleID, 0, Key1T, KeyLen, Key2T, KeyLen);
  3420. if Result > 0 then
  3421. Dec(Result, 2);
  3422. end
  3423. end;
  3424. function TIndexFile.CompareKey(Key: PChar): Integer;
  3425. begin
  3426. // call compare routine
  3427. Result := FCompareKey(Key);
  3428. // if descending then reverse order
  3429. if FIsDescending then
  3430. Result := -Result;
  3431. end;
  3432. function TIndexFile.CompareKeyNumericNDX(Key: PChar): Integer;
  3433. begin
  3434. Result := CompareKeysNumericNDX(FUserKey, Key);
  3435. end;
  3436. function TIndexFile.CompareKeyNumericMDX(Key: PChar): Integer;
  3437. begin
  3438. Result := CompareKeysNumericMDX(FUserKey, Key);
  3439. end;
  3440. procedure TIndexFile.TranslateToANSI(Src, Dest: PChar);
  3441. begin
  3442. { FromCP = FCodePage; }
  3443. { ToCP = FLocaleCP; }
  3444. TranslateString(FCodePage, FLocaleCP, Src, Dest, KeyLen);
  3445. end;
  3446. function TIndexFile.CompareKeyString(Key: PChar): Integer;
  3447. var
  3448. KeyT: array [0..100] of Char;
  3449. begin
  3450. if FLocaleID = LCID(lcidBinary) then
  3451. begin
  3452. Result := StrLComp(FUserKey, Key, KeyLen)
  3453. end else begin
  3454. TranslateToANSI(Key, KeyT);
  3455. Result := CompareString(FLocaleID, 0, FUserKey, KeyLen, KeyT, KeyLen);
  3456. if Result > 0 then
  3457. Dec(Result, 2);
  3458. end
  3459. end;
  3460. function TIndexFile.IndexOf(const AIndexName: string): Integer;
  3461. // *) assumes FIndexVersion >= xBaseIV
  3462. var
  3463. I: Integer;
  3464. begin
  3465. // get index of this index :-)
  3466. Result := -1;
  3467. I := 0;
  3468. while (I < PMdxHdr(Header).TagsUsed) and (Result < 0) do
  3469. begin
  3470. FTempMdxTag.Tag := CalcTagOffset(I);
  3471. if AnsiCompareText(AIndexName, FTempMdxTag.TagName) = 0 then
  3472. Result := I;
  3473. inc(I);
  3474. end;
  3475. end;
  3476. procedure TIndexFile.SetIndexName(const AIndexName: string);
  3477. var
  3478. found: Integer;
  3479. begin
  3480. // we can only select a different index if we are MDX
  3481. if FIndexVersion >= xBaseIV then
  3482. begin
  3483. // find index
  3484. found := IndexOf(AIndexName);
  3485. end else
  3486. found := 0;
  3487. // we can now select by index
  3488. if found >= 0 then
  3489. SelectIndexVars(found);
  3490. end;
  3491. function TIndexFile.CalcTagOffset(AIndex: Integer): Pointer;
  3492. begin
  3493. Result := PChar(Header) + FTagOffset + AIndex * FTagSize;
  3494. end;
  3495. procedure TIndexFile.SelectIndexVars(AIndex: Integer);
  3496. // *) assumes index is in range
  3497. begin
  3498. if AIndex >= 0 then
  3499. begin
  3500. // get pointer to index header
  3501. FIndexHeader := FIndexHeaders[AIndex];
  3502. // load root + leaf
  3503. FCurrentParser := FParsers[AIndex];
  3504. FRoot := FRoots[AIndex];
  3505. FLeaf := FLeaves[AIndex];
  3506. // if xBaseIV then we need to store where pageno of current header
  3507. if FIndexVersion >= xBaseIV then
  3508. begin
  3509. FMdxTag.Tag := CalcTagOffset(AIndex);
  3510. FIndexName := FMdxTag.TagName;
  3511. FHeaderPageNo := FMdxTag.HeaderPageNo;
  3512. // does dBase actually use this flag?
  3513. // FIsExpression := FMdxTag.KeyFormat = KeyFormat_Expression;
  3514. end else begin
  3515. // how does dBase III store whether it is expression?
  3516. // FIsExpression := true;
  3517. end;
  3518. // retrieve properties
  3519. UpdateIndexProperties;
  3520. end else begin
  3521. // not a valid index
  3522. FIndexName := EmptyStr;
  3523. end;
  3524. // store selected index
  3525. FSelectedIndex := AIndex;
  3526. FCanEdit := not FForceReadOnly;
  3527. end;
  3528. procedure TIndexFile.UpdateIndexProperties;
  3529. begin
  3530. // get properties
  3531. FIsDescending := (PIndexHdr(FIndexHeader).KeyFormat and KeyFormat_Descending) <> 0;
  3532. FUniqueMode := iuNormal;
  3533. if (PIndexHdr(FIndexHeader).KeyFormat and KeyFormat_Unique) <> 0 then
  3534. FUniqueMode := iuUnique;
  3535. if (PIndexHdr(FIndexHeader).KeyFormat and KeyFormat_Distinct) <> 0 then
  3536. FUniqueMode := iuDistinct;
  3537. // select key compare routine
  3538. if PIndexHdr(FIndexHeader).KeyType = 'C' then
  3539. begin
  3540. FCompareKeys := CompareKeysString;
  3541. FCompareKey := CompareKeyString;
  3542. end else
  3543. if FIndexVersion >= xBaseIV then
  3544. begin
  3545. FCompareKeys := CompareKeysNumericMDX;
  3546. FCompareKey := CompareKeyNumericMDX;
  3547. end else begin
  3548. FCompareKeys := CompareKeysNumericNDX;
  3549. FCompareKey := CompareKeyNumericNDX;
  3550. end;
  3551. end;
  3552. procedure TIndexFile.Flush;
  3553. var
  3554. I: Integer;
  3555. begin
  3556. // save changes to pages
  3557. if FIndexVersion >= xBaseIV then
  3558. begin
  3559. for I := 0 to MaxIndexes - 1 do
  3560. begin
  3561. if FHeaderModified[I] then
  3562. WriteIndexHeader(I);
  3563. if FRoots[I] <> nil then
  3564. FRoots[I].Flush
  3565. end;
  3566. end else begin
  3567. if FRoot <> nil then
  3568. FRoot.Flush;
  3569. end;
  3570. // save changes to header
  3571. FlushHeader;
  3572. inherited;
  3573. end;
  3574. (*
  3575. function TIndexFile.GetIndexCount: Integer;
  3576. begin
  3577. if FIndexVersion = xBaseIII then
  3578. Result := 1
  3579. else
  3580. if FIndexVersion = xBaseIV then
  3581. Result := PMdxHdr(Header).TagsUsed;
  3582. else
  3583. Result := 0;
  3584. end;
  3585. *)
  3586. procedure TIndexFile.GetIndexNames(const AList: TStrings);
  3587. var
  3588. I: Integer;
  3589. begin
  3590. // only applicable to MDX files
  3591. if FIndexVersion >= xBaseIV then
  3592. begin
  3593. for I := 0 to PMdxHdr(Header).TagsUsed - 1 do
  3594. begin
  3595. FTempMdxTag.Tag := CalcTagOffset(I);
  3596. AList.AddObject(FTempMdxTag.TagName, Self);
  3597. end;
  3598. end;
  3599. end;
  3600. procedure TIndexFile.GetIndexInfo(const AIndexName: string; IndexDef: TDbfIndexDef);
  3601. var
  3602. SaveIndexName: string;
  3603. begin
  3604. // remember current index
  3605. SaveIndexName := IndexName;
  3606. // select index
  3607. IndexName := AIndexName;
  3608. // copy properties
  3609. IndexDef.IndexFile := AIndexName;
  3610. IndexDef.Expression := PIndexHdr(FIndexHeader).KeyDesc;
  3611. IndexDef.Options := [];
  3612. IndexDef.Temporary := true;
  3613. if FIsDescending then
  3614. IndexDef.Options := IndexDef.Options + [ixDescending];
  3615. IndexDef.Options := IndexDef.Options + [ixExpression];
  3616. case FUniqueMode of
  3617. iuUnique: IndexDef.Options := IndexDef.Options + [ixUnique];
  3618. iuDistinct: IndexDef.Options := IndexDef.Options + [ixPrimary];
  3619. end;
  3620. // reselect previous index
  3621. IndexName := SaveIndexName;
  3622. end;
  3623. function TIndexFile.GetExpression: string;
  3624. begin
  3625. if FCurrentParser <> nil then
  3626. Result := FCurrentParser.Expression
  3627. else
  3628. Result := EmptyStr;
  3629. end;
  3630. function TIndexFile.GetDbfLanguageId: Byte;
  3631. begin
  3632. // check if parent DBF version 7, get language id
  3633. if (TDbfFile(FDbfFile).DbfVersion = xBaseVII) then
  3634. begin
  3635. // get language id of parent dbf
  3636. Result := GetLangId_From_LangName(TDbfFile(FDbfFile).LanguageStr);
  3637. end else begin
  3638. // dBase IV has language id in header
  3639. Result := TDbfFile(FDbfFile).LanguageID;
  3640. end;
  3641. end;
  3642. procedure TIndexFile.WriteHeader; {override;}
  3643. begin
  3644. // if NDX, then this means file header
  3645. if FIndexVersion >= xBaseIV then
  3646. if NeedLocks then
  3647. WriteIndexHeader(FSelectedIndex)
  3648. else
  3649. FHeaderModified[FSelectedIndex] := true
  3650. else
  3651. WriteFileHeader;
  3652. end;
  3653. procedure TIndexFile.WriteFileHeader;
  3654. begin
  3655. inherited WriteHeader;
  3656. end;
  3657. procedure TIndexFile.WriteIndexHeader(AIndex: Integer);
  3658. begin
  3659. FTempMdxTag.Tag := CalcTagOffset(AIndex);
  3660. WriteRecord(FTempMdxTag.HeaderPageNo, FIndexHeaders[AIndex]);
  3661. FHeaderModified[AIndex] := false;
  3662. end;
  3663. //==========================================================
  3664. //============ TDbfIndexDef
  3665. //==========================================================
  3666. constructor TDbfIndexDef.Create(Collection: TCollection); {override;}
  3667. begin
  3668. inherited Create(Collection);
  3669. FTemporary := false;
  3670. end;
  3671. destructor TDbfIndexDef.Destroy; {override;}
  3672. begin
  3673. inherited Destroy;
  3674. end;
  3675. procedure TDbfIndexDef.Assign(Source: TPersistent);
  3676. begin
  3677. // we can't do anything with it if not a TDbfIndexDef
  3678. if Source is TDbfIndexDef then
  3679. begin
  3680. FIndexName := TDbfIndexDef(Source).IndexFile;
  3681. FExpression := TDbfIndexDef(Source).Expression;
  3682. FOptions := TDbfIndexDef(Source).Options;
  3683. end else
  3684. inherited;
  3685. end;
  3686. procedure TDbfIndexDef.SetIndexName(NewName: string);
  3687. begin
  3688. FIndexName := AnsiUpperCase(Trim(NewName));
  3689. end;
  3690. procedure TDbfIndexDef.SetExpression(NewField: string);
  3691. begin
  3692. FExpression := AnsiUpperCase(Trim(NewField));
  3693. end;
  3694. initialization
  3695. {
  3696. Entry_Mdx_BOF.RecBlockNo := RecBOF;
  3697. Entry_Mdx_BOF.KeyData := #0;
  3698. Entry_Mdx_EOF.RecBlockNo := RecEOF;
  3699. Entry_Mdx_EOF.KeyData := #0;
  3700. Entry_Ndx_BOF.LowerPageNo := 0;
  3701. Entry_Ndx_BOF.RecNo := RecBOF;
  3702. Entry_Ndx_BOF.KeyData := #0;
  3703. Entry_Ndx_EOF.LowerPageNo := 0;
  3704. Entry_Ndx_EOF.RecNo := RecEOF;
  3705. Entry_Ndx_EOF.KeyData := #0;
  3706. }
  3707. LCIDList := TLCIDList.Create;
  3708. LCIDList.Enumerate;
  3709. finalization
  3710. LCIDList.Free;
  3711. end.