DBINDXES.MOD 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062
  1. IMPLEMENTATION MODULE DBIndxes;
  2. (*# check(overflow=>off) *)
  3. (*/NOCHECK O *)
  4. (*
  5. * ModBase
  6. * Release 3.0
  7. * (c) Copyright 1986 - 1990 Donald G. Fletcher
  8. * (c) Copyright 1986 - 1991 PMI
  9. Copyright 1988 - 1991 John McMonagle
  10. * P.O. Box 8402
  11. * Green Bay Wi 53308
  12. * All Rights Reserved
  13. *)
  14. (*
  15. - DBIndex
  16. - Bug in CloseIndex fixed April 2, 1987
  17. - DeleteEntry rewritten April 2, 1987
  18. - CloseIndex does not write its rootnode unless ndx^.open is TRUE
  19. - Most FOR loops have been rewritten to use Move or ShiftArrayRight
  20. - September 14, 1987 - Rewritten with LONGINT for Logitech 3.0
  21. *)
  22. (*System modules*)
  23. FROM SYSTEM IMPORT ADR,ADDRESS,SIZE,BYTE;
  24. FROM M2Strings IMPORT Assign,CompareStr, Copy,Length,Concat;
  25. (*PMI modules*)
  26. FROM LowLevel IMPORT Move, Fill, ShiftArrayRight,Address8086,
  27. BitwiseAnd,ShiftLeft;
  28. IMPORT StringIO; (*from Repertoire*)
  29. IMPORT HandleIO,FAPI; (*from Repertoire*)
  30. FROM Numbers IMPORT Max;
  31. FROM StrEdit IMPORT CrunchBlanks,CAPstr,Append;
  32. FROM PosUtils IMPORT Equal;
  33. FROM NumTypes IMPORT Real8;
  34. (*ModBase modules*)
  35. FROM ErrorManager IMPORT WARN;
  36. FROM StrConv IMPORT StrToReal;
  37. FROM ModBase3 IMPORT DBFile, ReadDBRec, GetField, SetDBBuffer,
  38. UpDateIndexes,SetRecordMode,OpenDBF,Appending,SetIndexList,
  39. IndexList,NumberRecords,FieldList,BufferSize,PosOfField,Record,
  40. DBFieldPtr,Deleted;
  41. FROM VStorage IMPORT
  42. DosAlloc, DosDealloc,DosAvail;
  43. (* IMPORT ChkInd; *)
  44. (*key numbering convention node[0] contains the number of keys but
  45. getkey etc. the first one is 1 not 0. *)
  46. IMPORT Locks,ModBase3;
  47. PROCEDURE DEALLOCATE(VAR loc:ADDRESS;size:CARDINAL);
  48. BEGIN
  49. DosDealloc(loc,size);
  50. END DEALLOCATE;
  51. PROCEDURE ALLOCATE(VAR loc:ADDRESS;size:CARDINAL);
  52. BEGIN
  53. DosAlloc(loc,size);
  54. END ALLOCATE;
  55. CONST
  56. FirstKey = 0; (* the number of the first key in a node *)
  57. MaxKey = 128;
  58. RecNumLen = 4;
  59. NodeSize = 512;
  60. IndexNameLength = 80;
  61. MaxField = 128;
  62. MaxDepth = 24;
  63. FirstNode = 0;
  64. RootNodePtrPos = 4;
  65. NextFreeNodePtrPos = 8;
  66. KeyLenPos = 12;
  67. KeyEntPos = 14;
  68. KeyTypePos = 22;
  69. KeyExpPos = 24;
  70. DepthPosition = 256;
  71. InitCode =56317;
  72. Bins=128;
  73. TYPE
  74. HeaderNodeType=
  75. RECORD
  76. rootptr,
  77. nextfreenode,
  78. FreeList :LONGINT; (* this may not be true dbase compatable *)
  79. keylength,
  80. keyspernode:CARDINAL;
  81. NumType,fill:BOOLEAN;
  82. entrylength:CARDINAL;
  83. Flag,
  84. fill2:CARDINAL;
  85. KeyExpression:ARRAY[0..487] OF CHAR;
  86. END (* record *);
  87. NodeType = ARRAY [0..NodeSize-1] OF CHAR;
  88. IndexBuffer = POINTER TO NodeBuffType;
  89. NodeBuffType =
  90. RECORD
  91. node: NodeType;
  92. (* clean:CARDINAL; diag to test for overwriting node *)
  93. number: LONGINT;
  94. next, prev,NHash,PHash: IndexBuffer;
  95. Lock,NeedToWrite:BOOLEAN;
  96. END;
  97. KeyPosType = RECORD
  98. buffer: IndexBuffer ;
  99. keynum : CARDINAL;
  100. END;
  101. Route = ARRAY [0..MaxDepth] OF KeyPosType;
  102. EntryType =
  103. RECORD
  104. lowernode: LONGINT;
  105. recordnum: LONGINT;
  106. value : ARRAY [0..MaxKey] OF CHAR;
  107. END;
  108. KeyPointer=POINTER TO EntryType;
  109. RealEntry =
  110. RECORD
  111. lowernode: LONGINT;
  112. recordnum: LONGINT;
  113. key:Real8;
  114. END;
  115. RealKeyPointer=POINTER TO RealEntry;
  116. CompareType=(LT,EQ,GT);
  117. CompareProc= PROCEDURE( ADDRESS,ADDRESS,CARDINAL): CompareType;
  118. DBIndex= POINTER TO IndexRec;
  119. IndexRec =
  120. RECORD
  121. name: ARRAY [1..IndexNameLength] OF CHAR;
  122. f: CARDINAL; (* this is the file handle *)
  123. Locked:CARDINAL; (* indicates file is locked *)
  124. includedeleted,
  125. Exclusive, (* indicates file locking not needed *)
  126. Changed,
  127. open: BOOLEAN;
  128. Safety: BOOLEAN;
  129. KeyNumber,
  130. depth,
  131. Init:CARDINAL;
  132. alias:DBFile;
  133. Header:HeaderNodeType;
  134. KeyProc :KeyProcedure;
  135. posarray: Route;
  136. CASE :BOOLEAN OF
  137. TRUE: currentkey: KeyPointer;|
  138. FALSE: numkey : RealKeyPointer;
  139. END;
  140. first, last: IndexBuffer;
  141. currsize: CARDINAL;
  142. buffsize: CARDINAL;
  143. (* number of Nodes in Buffer *)
  144. UpdateList:DBIndex;(* consider having list in seperate record
  145. so that Index can be updated from more than one DBF *)
  146. Hash:ARRAY[0..Bins-1] OF IndexBuffer;
  147. END;
  148. PROCEDURE HashP(number:LONGINT):CARDINAL;
  149. TYPE
  150. LSet=SET OF [0..31];
  151. BEGIN
  152. RETURN VAL(CARDINAL,LONGINT(LSet(127)*LSet(number) ) );
  153. END HashP;
  154. PROCEDURE AddToTable(ndx:DBIndex;BufPtr:IndexBuffer);
  155. VAR
  156. ptr:IndexBuffer;
  157. h:CARDINAL;
  158. BEGIN
  159. h:=HashP(BufPtr^.number);
  160. ptr:=ndx^.Hash[h];
  161. BufPtr^.NHash:=ptr;
  162. IF ptr#NIL THEN
  163. ptr^.PHash:=BufPtr
  164. END;
  165. BufPtr^.PHash:=NIL;
  166. ndx^.Hash[h]:=BufPtr;
  167. END AddToTable;
  168. PROCEDURE RemoveFromTable(ndx:DBIndex;BufPtr:IndexBuffer);
  169. BEGIN
  170. IF BufPtr^.PHash=NIL THEN
  171. ndx^.Hash[HashP(BufPtr^.number)]:=BufPtr^.NHash;
  172. ELSE
  173. BufPtr^.PHash^.NHash:=BufPtr^.NHash
  174. END;
  175. IF BufPtr^.NHash#NIL THEN
  176. BufPtr^.NHash^.PHash:=BufPtr^.PHash;
  177. END;
  178. BufPtr^.PHash:=NIL;
  179. BufPtr^.NHash:=NIL;
  180. END RemoveFromTable;
  181. PROCEDURE InitPosarray( ndx: DBIndex);
  182. VAR
  183. i:CARDINAL;
  184. BEGIN
  185. FOR i:= 0 TO MaxDepth DO
  186. ndx^.posarray[i].buffer:=NIL;
  187. END;
  188. END InitPosarray;
  189. PROCEDURE InBuffer( ndx: DBIndex; nodenumber: LONGINT; VAR
  190. BufPtr: IndexBuffer): BOOLEAN;
  191. BEGIN
  192. BufPtr:=ndx^.Hash[HashP(nodenumber)];
  193. IF BufPtr = NIL THEN
  194. RETURN FALSE
  195. ELSE
  196. LOOP
  197. WITH BufPtr^ DO
  198. IF (number = nodenumber) THEN
  199. RETURN TRUE
  200. END;
  201. IF (NHash = NIL) THEN
  202. RETURN FALSE
  203. END;
  204. END (* WITH *);
  205. BufPtr := BufPtr^.NHash;
  206. END; (* loop *)
  207. END;
  208. END InBuffer;
  209. PROCEDURE AddBuffer( ndx:DBIndex;VAR buffer: IndexBuffer );
  210. BEGIN
  211. (* always add to top *)
  212. buffer^.next:=ndx^.first;
  213. buffer^.prev:=NIL;
  214. IF buffer^.next=NIL
  215. THEN
  216. ndx^.last:=buffer;
  217. ELSE
  218. ndx^.first^.prev:=buffer;
  219. END;
  220. ndx^.first:=buffer;
  221. buffer^.Lock:=TRUE;
  222. INC(ndx^.currsize);
  223. END AddBuffer;
  224. PROCEDURE RemoveBuffer(ndx:DBIndex;VAR buffer: IndexBuffer );
  225. BEGIN
  226. IF buffer^.next=NIL
  227. THEN
  228. IF buffer^.prev=NIL
  229. THEN
  230. ndx^.first:=NIL;
  231. ndx^.last:=NIL;
  232. ELSE
  233. ndx^.last:=buffer^.prev;
  234. buffer^.prev^.next:=buffer^.next;
  235. END;
  236. ELSE
  237. IF buffer^.prev=NIL
  238. THEN
  239. ndx^.first:=buffer^.next;
  240. buffer^.next^.prev:=buffer^.prev;
  241. ELSE
  242. buffer^.next^.prev:=buffer^.prev;
  243. buffer^.prev^.next:=buffer^.next;
  244. END;
  245. END;
  246. DEC(ndx^.currsize);
  247. END RemoveBuffer;
  248. PROCEDURE WriteNode( ndx: DBIndex; nodenumber: LONGINT;
  249. VAR nodeblock: NodeType);
  250. VAR pos: LONGINT;
  251. FileError: StringIO.ErrorMessage;
  252. (*PROCEDURE Errorchk;(* diag *)
  253. VAR
  254. buffer:IndexBuffer;
  255. i:CARDINAL;
  256. BEGIN
  257. IF NOT InBuffer(ndx,nodenumber,buffer)
  258. THEN
  259. HALT;
  260. END;
  261. FOR i:=0 TO 511 DO
  262. IF buffer^.node[i]#nodeblock[i]
  263. THEN
  264. HALT;
  265. END;
  266. END;
  267. END Errorchk;*)
  268. BEGIN
  269. (* IF nodenumber>VAL(LONGINT,1)
  270. THEN
  271. Errorchk
  272. END; *)
  273. pos := nodenumber * VAL(LONGINT, NodeSize);
  274. HandleIO.SetFilePtr(ndx^.f, HandleIO.FromStart, pos);
  275. FileError := HandleIO.BlockWrite(ndx^.f, ADR(nodeblock), NodeSize);
  276. IF FileError # StringIO.NoError THEN
  277. WARN('Block write failure in WriteNode');
  278. END;
  279. IF ndx^.Safety
  280. THEN
  281. HandleIO.UpdateDisk(ndx^.f);
  282. END;
  283. END WriteNode;
  284. PROCEDURE InitNode(VAR node: NodeType);
  285. BEGIN
  286. Fill(ADR(node), NodeSize, 0C);
  287. END InitNode;
  288. PROCEDURE FindFreeBuffer( ndx:DBIndex;VAR buffer: IndexBuffer ):BOOLEAN;
  289. BEGIN
  290. buffer:=ndx^.last;
  291. WHILE buffer^.Lock
  292. DO
  293. IF buffer^.prev=NIL
  294. THEN
  295. RETURN FALSE;
  296. END;
  297. buffer:=buffer^.prev;
  298. END;
  299. (* if one is looking for buffer it will be reused so write it
  300. out if safety is off *)
  301. IF buffer^.NeedToWrite
  302. THEN
  303. WriteNode(ndx,buffer^.number,buffer^.node);
  304. END;
  305. RETURN TRUE;
  306. END FindFreeBuffer;
  307. PROCEDURE GetBuffer( ndx:DBIndex;VAR buffer: IndexBuffer;
  308. NodeNumber:LONGINT );
  309. BEGIN
  310. IF (( ndx^.buffsize>ndx^.currsize) AND DosAvail(8000))
  311. THEN
  312. DosAlloc(buffer,SIZE(buffer^));
  313. ELSE;
  314. IF FindFreeBuffer(ndx,buffer)
  315. THEN
  316. RemoveBuffer(ndx,buffer);
  317. RemoveFromTable(ndx,buffer);
  318. ELSE
  319. DosAlloc(buffer,SIZE(buffer^));
  320. END;
  321. END;
  322. AddBuffer(ndx,buffer);
  323. (* init buffer *)
  324. buffer^.number:=NodeNumber;
  325. AddToTable(ndx,buffer);
  326. (* buffer^.clean:=37513; diag *)
  327. buffer^.NeedToWrite:=FALSE;
  328. buffer^.Lock:=FALSE;
  329. InitNode(buffer^.node);
  330. END GetBuffer;
  331. PROCEDURE ReadNode( ndx: DBIndex;
  332. nodenumber: LONGINT;
  333. VAR buffer: IndexBuffer);
  334. (* the file associated with the index must already be open *)
  335. VAR pos: LONGINT;
  336. FileError: StringIO.ErrorMessage;
  337. BEGIN
  338. IF InBuffer(ndx,nodenumber,buffer)
  339. THEN
  340. RemoveBuffer(ndx,buffer);
  341. AddBuffer(ndx,buffer);
  342. RETURN
  343. END;
  344. GetBuffer(ndx,buffer,nodenumber);
  345. (*buffer^.number:=nodenumber;*)
  346. pos := nodenumber * VAL(LONGINT, NodeSize);
  347. HandleIO.SetFilePtr(ndx^.f, HandleIO.FromStart, pos);
  348. FileError := HandleIO.BlockRead(ndx^.f, ADR(buffer^.node), NodeSize);
  349. IF FileError # StringIO.NoError THEN
  350. WARN('Block read failure in ReadNode');
  351. END;
  352. END ReadNode;
  353. PROCEDURE NewNode( ndx:DBIndex;VAR buffer:IndexBuffer) ;
  354. BEGIN
  355. IF ndx^.Header.FreeList=VAL(LONGINT,0)
  356. THEN
  357. GetBuffer(ndx,buffer,ndx^.Header.nextfreenode);
  358. (*buffer^.number:=ndx^.Header.nextfreenode;*)
  359. INC(ndx^.Header.nextfreenode)
  360. ELSE
  361. ReadNode(ndx,ndx^.Header.FreeList,buffer);
  362. Move(ADR(buffer^.node),ADR(ndx^.Header.FreeList),4);
  363. InitNode(buffer^.node);
  364. END;
  365. END NewNode;
  366. PROCEDURE ReadIntoArray( ndx :DBIndex;
  367. nodenumber: LONGINT;
  368. level :CARDINAL);
  369. BEGIN
  370. IF ndx^.posarray[level].buffer#NIL
  371. THEN
  372. ndx^.posarray[level].buffer^.Lock:=FALSE;
  373. END;
  374. ReadNode(ndx, nodenumber, ndx^.posarray[level].buffer);
  375. ndx^.posarray[level].buffer^.number:=nodenumber;
  376. ndx^.posarray[level].buffer^.Lock:=TRUE;
  377. END ReadIntoArray;
  378. PROCEDURE ReadHeader( ndx:DBIndex);
  379. BEGIN
  380. HandleIO.SetFilePtr(ndx^.f, HandleIO.FromStart, 0);
  381. StringIO.PrintMessage(HandleIO.BlockRead(ndx^.f,ADR(ndx^.Header),
  382. SIZE(ndx^.Header)))
  383. END ReadHeader;
  384. PROCEDURE WriteHeader( ndx:DBIndex);
  385. BEGIN
  386. HandleIO.SetFilePtr(ndx^.f, HandleIO.FromStart, 0);
  387. StringIO.PrintMessage(HandleIO.BlockWrite(ndx^.f,ADR(ndx^.Header),
  388. SIZE(ndx^.Header)))
  389. END WriteHeader;
  390. PROCEDURE GetKeyPtr(VAR node: NodeType;
  391. ndx: DBIndex;
  392. keynumber: CARDINAL):KeyPointer;
  393. BEGIN
  394. RETURN ADR(node[4 +(keynumber * ndx^.Header.entrylength)]);
  395. END GetKeyPtr;
  396. PROCEDURE GetKey(VAR node: NodeType;
  397. ndx: DBIndex;
  398. keynumber: CARDINAL;
  399. VAR key: EntryType);
  400. BEGIN
  401. (* entire procedure should be eliminated and placed in line.
  402. changes are based on assumption that the max key size is 128
  403. and 129 are avalable. error checking could be done in buildindex!!!!*)
  404. Move(ADR(node[4 +(keynumber * ndx^.Header.entrylength)]), ADR(key),
  405. ndx^.Header.entrylength);
  406. key.value[ndx^.Header.keylength]:=0C;
  407. END GetKey;
  408. (*
  409. PROCEDURE CompareKeyReal( ad1,ad2 : ADDRESS;size:CARDINAL) : CompareType;
  410. TYPE
  411. rp= RECORD
  412. CASE:CARDINAL OF
  413. 1:
  414. r:POINTER TO Real8;
  415. | 2:
  416. b:POINTER TO ARRAY[0..7] OF BYTE;
  417. | 3:
  418. a:ADDRESS;
  419. END;
  420. END;
  421. VAR
  422. r1,r2:rp;
  423. BEGIN
  424. r1.a:=ad1;
  425. r2.a:=ad2;
  426. IF (r1.r^ < r2.r^) THEN
  427. RETURN LT
  428. ELSIF (r1.b^ = r2.b^) THEN
  429. RETURN EQ
  430. ELSE
  431. RETURN GT
  432. END;
  433. END CompareKeyReal;
  434. *)
  435. PROCEDURE CompareKeyReal(s1, s2 : ADDRESS;size:CARDINAL) : CompareType;
  436. TYPE
  437. rp= RECORD
  438. CASE:CARDINAL OF
  439. 1:
  440. r:POINTER TO Real8;
  441. | 2:
  442. c:POINTER TO CARDINAL;
  443. | 3:
  444. a:ADDRESS;
  445. | 4:
  446. off,seg:CARDINAL;
  447. | 5:
  448. b:POINTER TO BITSET;
  449. END;
  450. END;
  451. VAR
  452. TmpAdr1, TmpAdr2 : rp;
  453. cnt : CARDINAL;
  454. neg:BOOLEAN;
  455. BEGIN
  456. TmpAdr1.a := s1;
  457. TmpAdr2.a := s2;
  458. INC(TmpAdr1.off,6);
  459. INC(TmpAdr2.off,6);
  460. neg:=(15 IN TmpAdr1.b^) OR (15 IN TmpAdr2.b^);
  461. cnt := 0;
  462. WHILE cnt<4 DO
  463. IF TmpAdr1.c^#TmpAdr2.c^ THEN
  464. IF TmpAdr1.c^>TmpAdr2.c^ THEN
  465. IF neg THEN
  466. RETURN LT
  467. ELSE
  468. RETURN GT
  469. END;
  470. ELSE
  471. IF neg THEN
  472. RETURN GT
  473. ELSE
  474. RETURN LT;
  475. END;
  476. END;
  477. ELSE
  478. INC(cnt);
  479. DEC(TmpAdr1.off,2);
  480. DEC(TmpAdr2.off,2);
  481. END;
  482. END;
  483. RETURN EQ;
  484. END CompareKeyReal;
  485. PROCEDURE CompareKey(s1, s2 : ADDRESS;size:CARDINAL) : CompareType;
  486. VAR
  487. TmpAdr1, TmpAdr2 : Address8086;
  488. cnt : CARDINAL;
  489. BEGIN
  490. TmpAdr1.a := s1;
  491. TmpAdr2.a := s2;
  492. cnt := 0;
  493. WHILE cnt<size DO
  494. IF TmpAdr1.b^#TmpAdr2.b^ THEN
  495. IF TmpAdr1.b^>TmpAdr2.b^ THEN
  496. RETURN GT;
  497. ELSE
  498. RETURN LT;
  499. END;
  500. ELSE
  501. IF (TmpAdr1.b^=0C)
  502. THEN (* if on 0C were done *)
  503. RETURN EQ
  504. END;
  505. INC(cnt);
  506. INC(TmpAdr1.off);
  507. INC(TmpAdr2.off);
  508. END;
  509. END;
  510. RETURN EQ;
  511. END CompareKey;
  512. PROCEDURE FindPosition( ndx: DBIndex;
  513. KeyValue: ADDRESS;
  514. VAR found: BOOLEAN;
  515. CompareP:CompareProc);
  516. VAR level, keysinnode,
  517. diff,
  518. Top,Bottom,
  519. crrntkeynum: CARDINAL;
  520. done: BOOLEAN;
  521. keyptr:KeyPointer;
  522. nextnode: LONGINT;
  523. BEGIN
  524. IF OpenIndex(ndx)=FALSE
  525. THEN
  526. WARN('Error opening Index in FindPosition');
  527. END;
  528. nextnode := ndx^.Header.rootptr; (* start search with root node *)
  529. level := 0;
  530. found:=FALSE;
  531. REPEAT
  532. ReadIntoArray(ndx, nextnode, level);
  533. WITH ndx^.posarray[level] DO
  534. keysinnode := ORD(buffer^.node[0]);
  535. Top:=keysinnode;
  536. Bottom:=0;
  537. crrntkeynum:=(Top)DIV 2;(* use shift operator? *)
  538. done := FALSE;
  539. (* IF buffer^.clean# 37513 THEN HALT END; diag *)
  540. LOOP
  541. keyptr:=GetKeyPtr(buffer^.node, ndx, crrntkeynum);
  542. WITH keyptr^ DO
  543. CASE CompareP(KeyValue,ADR(value),ndx^.Header.keylength) OF
  544. LT: (* key is less than tested value *)
  545. diff:=crrntkeynum-Bottom;
  546. IF diff<1
  547. THEN
  548. EXIT; (* done *)
  549. END;
  550. Top:=crrntkeynum; (* check lower half *)
  551. crrntkeynum:=Bottom+(diff DIV 2);
  552. |GT: (* key is greater than test *)
  553. diff:=Top-crrntkeynum;
  554. IF diff<=1
  555. THEN
  556. crrntkeynum:=Top; (*done but currect one was top*)
  557. keyptr:=GetKeyPtr(buffer^.node, ndx, crrntkeynum);
  558. EXIT;
  559. END;
  560. Bottom:=crrntkeynum; (* Next check upper half *)
  561. crrntkeynum:=Bottom+(diff DIV 2);
  562. |EQ:
  563. (* * * * * * * * * * * * * * * * * * * *testing stuff for ed *)
  564. (* found a position - might not be the first one though *)
  565. (* loop backwards through the index to find the first if many *)
  566. LOOP
  567. IF crrntkeynum < 1
  568. THEN EXIT;
  569. END; (* at the first *)
  570. DEC(crrntkeynum);
  571. keyptr := GetKeyPtr(buffer^.node,ndx,crrntkeynum);
  572. IF CompareP(KeyValue,ADR(keyptr^.value),ndx^.Header.keylength) = GT
  573. THEN INC(crrntkeynum);
  574. keyptr := GetKeyPtr(buffer^.node,ndx,crrntkeynum);
  575. EXIT;
  576. END;
  577. (* * * * * * * * * * * End of stuff by ed * * * * * * * * * * *)
  578. END; (* end of loop and end of my stuff *)
  579. found:= TRUE;
  580. EXIT;
  581. END; (* CASE *)
  582. END;
  583. END (*loop*);
  584. (* compare the key to the keys in the rootnode until the fieldstring
  585. <= currentkey or the last entry in the node is encountered *)
  586. keynum := crrntkeynum;
  587. (* currentkey set by getkey *)
  588. nextnode:=keyptr^.lowernode;
  589. END;
  590. INC(level);
  591. UNTIL nextnode=VAL(LONGINT,0);
  592. ndx^.depth:=level-1;
  593. ndx^.currentkey:=keyptr;
  594. END FindPosition;
  595. PROCEDURE FindPositionCh( ndx: DBIndex;
  596. keystr: ARRAY OF CHAR;
  597. VAR found: BOOLEAN);
  598. VAR
  599. TestStr:ARRAY[0..MaxKey] OF CHAR;
  600. BEGIN
  601. Fill(ADR(TestStr),MaxKey,' ');
  602. Copy(keystr,0,Length(keystr),TestStr);
  603. FindPosition(ndx,ADR(TestStr),found,CompareKey);
  604. END FindPositionCh;
  605. PROCEDURE FindPositionR( ndx: DBIndex;
  606. KeyValue: Real8;
  607. VAR found: BOOLEAN);
  608. BEGIN
  609. FindPosition(ndx,ADR(KeyValue),found,CompareKeyReal);
  610. END FindPositionR;
  611. PROCEDURE FindPositionN(ndx: DBIndex;
  612. keystr: ARRAY OF CHAR;
  613. VAR found: BOOLEAN);
  614. VAR
  615. KeyValue:Real8;
  616. BEGIN
  617. IF NOT StrToReal(keystr, 0,KeyValue) THEN KeyValue:=0.0 END;
  618. FindPositionR(ndx,KeyValue,found);
  619. END FindPositionN;
  620. PROCEDURE AddRecord( alias: DBFile;
  621. ndx: DBIndex);
  622. VAR
  623. fieldstring: ARRAY [1..MaxField] OF CHAR;
  624. found: BOOLEAN;
  625. key:
  626. RECORD
  627. CASE :BOOLEAN OF
  628. TRUE:num:Real8;|
  629. FALSE:str:ARRAY[0..7] OF CHAR;
  630. END;
  631. END;
  632. BEGIN
  633. IF OpenIndex(ndx)=FALSE
  634. THEN
  635. WARN('Error opening Index in AddRecord');
  636. END;
  637. EnterLock(ndx);
  638. (* get key from record *);
  639. ndx^.KeyProc(alias,ndx,fieldstring);
  640. IF ndx^.Header.NumType THEN
  641. IF NOT StrToReal(fieldstring, 0,key.num) THEN key.num:=0.0 END;
  642. FindPositionR(ndx, key.num, found);
  643. InsertEntry(ndx, key.str, Record(alias));
  644. ELSE
  645. FindPositionCh(ndx, fieldstring, found);
  646. InsertEntry(ndx, fieldstring, Record(alias));
  647. END; (* Now we have the position where the new Entry
  648. should be inserted *)
  649. ExitLock(ndx);
  650. END AddRecord;
  651. PROCEDURE DefaultKeyProcedure(alias: DBFile; ndx: DBIndex;
  652. VAR str:ARRAY OF CHAR );
  653. BEGIN
  654. GetField(alias, ndx^.KeyNumber, str);
  655. END DefaultKeyProcedure;
  656. PROCEDURE AdjustUpperNode( ndx: DBIndex;VAR KeyStr:ARRAY OF CHAR;
  657. level: CARDINAL);
  658. (* need to pass keystring because may be fixing at the current level
  659. but not in the route level-1 must point to worknode!!*)
  660. VAR offset: CARDINAL;
  661. upkey,key:KeyPointer;
  662. BEGIN
  663. (* do not check to see if it is nessary but make sure level#0 *)
  664. IF (level=0) THEN RETURN END;
  665. (*Adjust upernode*)
  666. IF (ndx^.posarray[level-1].keynum#
  667. ORD(ndx^.posarray[level-1].buffer^.node[0]))
  668. THEN (* can simplify when changing upkey to upkey^ *)
  669. upkey:=GetKeyPtr(ndx^.posarray[level-1].buffer^.node,ndx,
  670. ndx^.posarray[level-1].keynum);
  671. Move(ADR(KeyStr), ADR(upkey^.value), ndx^.Header.keylength);
  672. IF ndx^.Safety OR NOT ndx^.Exclusive
  673. THEN
  674. WriteNode(ndx, ndx^.posarray[level-1].buffer^.number,
  675. ndx^.posarray[level-1].buffer^.node);
  676. ELSE
  677. ndx^.posarray[level-1].buffer^.NeedToWrite:=TRUE;
  678. END;
  679. ELSE
  680. AdjustUpperNode(ndx,KeyStr,level-1);
  681. END;
  682. END AdjustUpperNode;
  683. PROCEDURE DeleteEntry(ndx: DBIndex;
  684. deletelevel: CARDINAL);
  685. VAR offset: CARDINAL;
  686. upkey,key:KeyPointer;
  687. empty:BOOLEAN;
  688. BEGIN
  689. (* 4/21/89 notes for changes pos is not need as parameter
  690. if top node is changed need to fix all the way down if
  691. needed not just one as it is there is some chance of error.
  692. cosider making procedure to fix uper node as it is used in
  693. move keys also *)
  694. (* Find the offset of the keyentry following
  695. the entry to be deleted, and then shift the rest of the
  696. node up to cover the deleted node *)
  697. ndx^.Changed:=TRUE;
  698. offset := ((ndx^.posarray[deletelevel].keynum + 1) * (ndx^.Header.entrylength)) + 4;
  699. Move(ADR(ndx^.posarray[deletelevel].buffer^.node[offset]),
  700. ADR(ndx^.posarray[deletelevel].buffer^.node[offset-ndx^.Header.entrylength]),
  701. NodeSize-offset);
  702. (* not correct or nessisary ?
  703. Fill(ADR(ndx^.posarray[deletelevel].buffer^.
  704. node[offset -ndx^.Header.entrylength]), ndx^.Header.entrylength, 0C);*)
  705. (* Next decrement the number of keys in the node and
  706. write the decremented number in the first byte of the
  707. node. *)
  708. empty:=FALSE;
  709. empty:=(ndx^.posarray[deletelevel].buffer^.node[0])=0C;
  710. IF NOT empty THEN
  711. DEC(ndx^.posarray[deletelevel].buffer^.node[0]);
  712. empty:=(ndx^.posarray[deletelevel].buffer^.node[0]=0C) AND
  713. (deletelevel =ndx^.depth)
  714. END;
  715. IF (deletelevel > 0)
  716. THEN
  717. IF empty
  718. THEN (* THE NODE IS EMPTY *)
  719. (* posarray must be good *)
  720. (* put in freenode list *)
  721. Move(ADR(ndx^.Header.FreeList),
  722. ADR(ndx^.posarray[deletelevel].buffer^.node),4);
  723. ndx^.Header.FreeList:=ndx^.posarray[deletelevel].buffer^.number;
  724. DeleteEntry(ndx,deletelevel-1);
  725. ELSE;
  726. (* Check to see if deleted top node *)
  727. IF deletelevel=ndx^.depth
  728. THEN
  729. offset:=1
  730. ELSE
  731. offset:=0
  732. END;
  733. IF ndx^.posarray[deletelevel].keynum =
  734. ( ORD(ndx^.posarray[deletelevel].buffer^.node[0])+1-offset)
  735. THEN
  736. key:=GetKeyPtr(ndx^.posarray[deletelevel].buffer^.node,
  737. ndx,ndx^.posarray[deletelevel].keynum-1);
  738. AdjustUpperNode(ndx,
  739. key^.value,
  740. deletelevel);
  741. END;
  742. END;
  743. END; (* if *)
  744. IF ndx^.Safety OR NOT ndx^.Exclusive
  745. THEN
  746. WriteNode(ndx, ndx^.posarray[deletelevel].buffer^.number,
  747. ndx^.posarray[deletelevel].buffer^.node);
  748. ELSE
  749. ndx^.posarray[deletelevel].buffer^.NeedToWrite:=TRUE;
  750. END;
  751. END DeleteEntry;
  752. PROCEDURE DeleteCurrentEntry( ndx: DBIndex);
  753. VAR
  754. rec:LONGINT;
  755. BEGIN
  756. IF OpenIndex(ndx)=FALSE
  757. THEN
  758. WARN('Error opening Index in DeleteCurrentEntry');
  759. END;
  760. rec:=ndx^.currentkey^.recordnum;
  761. EnterLock(ndx);
  762. IF rec=ndx^.currentkey^.recordnum THEN (* do not delete if not there *)
  763. DeleteEntry(ndx, ndx^.depth);
  764. END;
  765. ExitLock(ndx);
  766. END DeleteCurrentEntry;
  767. PROCEDURE UpdateIndexHeader(ndx :DBIndex );
  768. VAR
  769. Buffer:IndexBuffer;
  770. BEGIN
  771. IF NOT ndx^.open
  772. THEN
  773. RETURN;
  774. END;
  775. HandleIO.SetFilePtr(ndx^.f,HandleIO.FromStart,VAL(LONGINT,0));
  776. StringIO.PrintMessage(
  777. HandleIO.BlockWrite(ndx^.f,ADR(ndx^.Header),SIZE(ndx^.Header)));
  778. IF NOT ndx^.Safety AND ndx^.Exclusive
  779. THEN (* Write out Buffers if safety off*)
  780. Buffer:=ndx^.first;
  781. WHILE Buffer#NIL
  782. DO
  783. IF Buffer^.NeedToWrite
  784. THEN
  785. WriteNode(ndx,Buffer^.number,Buffer^.node);
  786. Buffer^.NeedToWrite:=FALSE;
  787. END;
  788. Buffer:=Buffer^.next;
  789. END;
  790. END;
  791. END UpdateIndexHeader;
  792. PROCEDURE InsertEntry( ndx: DBIndex;
  793. kstr: ARRAY OF CHAR;
  794. recno: LONGINT);
  795. VAR i, level, middle: CARDINAL;
  796. tempkey:EntryType;
  797. NewRoot,NewBuffer:IndexBuffer;
  798. noOverflow,InHighNode: BOOLEAN;
  799. (*diag,*)oldnodenum, newnodenum, lowernodenum: LONGINT;
  800. key:
  801. RECORD
  802. CASE :BOOLEAN OF
  803. TRUE:num:Real8;|
  804. FALSE:str:ARRAY[0..7] OF CHAR;
  805. END;
  806. END;
  807. PROCEDURE AddKeyTo( lower, recnum: LONGINT; VAR node: NodeType;
  808. val: ARRAY OF CHAR; NewKey:BOOLEAN);
  809. (* This procedure assumes that there is room in the node for another
  810. key entry; it does not test for correct positioning, it assumes
  811. that the ndx^.posarray has been correctly updated by all prior
  812. operations *)
  813. VAR moveblocksize, i, entrypos, keystomove : CARDINAL;
  814. BEGIN
  815. (* consider changing to copy into entry then move all *)
  816. entrypos := ndx^.posarray[level].keynum;
  817. keystomove := ORD(node[0])-entrypos+1;
  818. node[0] := CHR(ORD(node[0])+1);
  819. i := 4 + (entrypos * ndx^.Header.entrylength); (* 4 bytes reserved for key count *)
  820. (* make space for the new entry *)
  821. moveblocksize := keystomove*ndx^.Header.entrylength+4;
  822. ShiftArrayRight((* from *) ADR(node[i]),
  823. (* size *) moveblocksize ,
  824. (* distance *) ndx^.Header.entrylength);
  825. Move(ADR(recnum), ADR(node[i+4]), 4);
  826. (* trims to size *)
  827. Move(ADR(val),ADR(node[i+8]), ndx^.Header.entrylength - 8);
  828. Move(ADR(lower), ADR(node[i]), 4);
  829. (* the Move statement modifies the pointer after the inserted key
  830. so that it points to the appropriate node. It is hard to
  831. remember that the only reason an entry would be inserted into
  832. a node other than a leaf node is because the lower node was split. *)
  833. IF (ndx^.depth=level)
  834. THEN
  835. IF NewKey THEN
  836. ndx^.currentkey:=ADR(node[i])
  837. END;
  838. IF (keystomove=1)
  839. THEN
  840. AdjustUpperNode(ndx,val,level)
  841. END;
  842. END;
  843. END AddKeyTo;
  844. PROCEDURE Split(VAR old, new: IndexBuffer);
  845. VAR
  846. c:CHAR;
  847. key:EntryType;
  848. i, j, middlekeypos,
  849. keysinold: CARDINAL;
  850. BEGIN
  851. keysinold := ORD(old^.node[0]);
  852. new^.node := old^.node;
  853. (* if a key has been handed up from a split node it points to the
  854. newnode created by the last split *)
  855. (* save node numbers in case root node is being split *)
  856. newnodenum:=new^.number;
  857. oldnodenum:=old^.number;
  858. middle := (ndx^.Header.keyspernode DIV 2);
  859. keysinold := keysinold - middle;
  860. middlekeypos := 4 + (middle)* ndx^.Header.entrylength;
  861. (* middlekeypos is the END of the middlekey *)
  862. Fill(ADR(new^.node[middlekeypos]), NodeSize - middlekeypos, 0C);
  863. (* the new node gets the first keys, the rest are nulled out *)
  864. Move(ADR((*from*) old^.node[middlekeypos]),
  865. (* to *) ADR(old^.node[4]),
  866. (*size*) (NodeSize-middlekeypos));
  867. Fill(ADR(old^.node[8+keysinold*ndx^.Header.entrylength]),
  868. NodeSize-(8+keysinold*ndx^.Header.entrylength),0C);
  869. new^.node[0] := CHR(middle);
  870. old^.node[0] := CHR(keysinold);
  871. (* IF new=old
  872. THEN
  873. HALT;
  874. END; (* diag *)
  875. *)
  876. IF ndx^.posarray[level].keynum <= middle THEN
  877. (* insert into new node ( lowernode ) *)
  878. (* new is yet in posarray so must trick Addkeyto to not
  879. try and adjust upper node as it will be inserted latter*)
  880. INC(new^.node[0]);
  881. AddKeyTo(lowernodenum, recno, new^.node, kstr,TRUE);
  882. DEC(new^.node[0]);
  883. INC(middle); (* because an entry has been inserted ahead of it *)
  884. GetKey(new^.node,ndx,middle-1,key); (* get key value to
  885. insert in lowernode before we lose it *)
  886. IF lowernodenum#VAL(LONGINT,0)
  887. THEN (* not at leaf DBASEIII does not store entire lastkey in non leaf
  888. nodes *)
  889. DEC(new^.node[0])
  890. END;
  891. (* force new pos array *)
  892. InHighNode:=FALSE;
  893. (* need to put writes here because the readintoarray
  894. will lose a node *)
  895. IF ndx^.Safety OR NOT ndx^.Exclusive
  896. THEN
  897. WriteNode(ndx, new^.number, new^.node);
  898. WriteNode(ndx, old^.number, old^.node);
  899. ELSE
  900. new^.NeedToWrite:=TRUE;
  901. old^.NeedToWrite:=TRUE;
  902. END;
  903. ReadIntoArray(ndx, new^.number, level);
  904. old^.Lock:=FALSE;(* unlock other buffer *)
  905. ELSE
  906. (* insert into old (high) node *)
  907. GetKey(new^.node,ndx,middle-1,key); (* get key value to
  908. insert in lowernode before we lose it *)
  909. IF lowernodenum#VAL(LONGINT,0)
  910. THEN (* not at leaf DBASEIII does not store entire lastkey in non leaf
  911. nodes *)
  912. DEC(new^.node[0])
  913. END;
  914. ndx^.posarray[level].keynum := (ndx^.posarray[level].keynum - middle) ;
  915. AddKeyTo(lowernodenum, recno, old^.node, kstr,TRUE);
  916. IF ndx^.Safety OR NOT ndx^.Exclusive
  917. THEN
  918. WriteNode(ndx, new^.number, new^.node);
  919. WriteNode(ndx, old^.number, old^.node);
  920. ELSE
  921. new^.NeedToWrite:=TRUE;
  922. old^.NeedToWrite:=TRUE;
  923. END;
  924. InHighNode:=TRUE;
  925. (* force new pos array *)
  926. (* ReadIntoArray(ndx, old^.number, level); not neeed *)
  927. new^.Lock:=FALSE;(* unlock other buffer *)
  928. END;
  929. (* in order to place new node in tree, act as if was inserting
  930. the last key in the lower(new) node, so must save info *)
  931. lowernodenum:=newnodenum;
  932. Assign(key.value,kstr);
  933. recno:=key.recordnum;
  934. END Split;
  935. PROCEDURE Balance(level:CARDINAL);
  936. VAR
  937. offset,
  938. count,
  939. insertpos,
  940. keystomove,
  941. keys,
  942. downkeys,
  943. upkeys:CARDINAL;
  944. UpBuffer,DownBuffer:IndexBuffer;
  945. upkey,tempkey:EntryType;
  946. (* found:BOOLEAN;(*diag *) *)
  947. PROCEDURE Movekeys ;
  948. BEGIN
  949. noOverflow:=TRUE;
  950. IF upkeys > downkeys
  951. THEN (* move to lower node *)
  952. keystomove:=(keys-downkeys+1) DIV 2;
  953. count:=keystomove;
  954. WHILE count>0 DO
  955. (* get key to move and save*)
  956. (* remove from bottom place on top *)
  957. GetKey(ndx^.posarray[level].buffer^.node,
  958. ndx, 0, tempkey);
  959. ndx^.posarray[level].keynum:=0;
  960. DeleteEntry(ndx, level);
  961. ndx^.posarray[level].keynum:=ORD(DownBuffer^.node[0]);
  962. DEC(ndx^.posarray[level-1].keynum);
  963. AddKeyTo(tempkey.lowernode,tempkey.recordnum,
  964. DownBuffer^.node,tempkey.value,FALSE);(* addkey does not write *)
  965. INC(ndx^.posarray[level-1].keynum);
  966. DEC(count);
  967. END (* while *);
  968. IF insertpos >= keystomove THEN
  969. (* insert into new node ( lowernode ) *)
  970. ndx^.posarray[level].keynum:=insertpos-keystomove;
  971. AddKeyTo(lowernodenum, recno, ndx^.posarray[level].buffer^.node, kstr,TRUE);
  972. (* force new pos array *)
  973. (* need to put writes here because the readintoarray
  974. will lose a node *)
  975. IF ndx^.Safety OR NOT ndx^.Exclusive
  976. THEN
  977. WriteNode(ndx, ndx^.posarray[level].buffer^.number,
  978. ndx^.posarray[level].buffer^.node);
  979. WriteNode(ndx, DownBuffer^.number, DownBuffer^.node);
  980. ELSE
  981. ndx^.posarray[level].buffer^.NeedToWrite:=TRUE;
  982. DownBuffer^.NeedToWrite:=TRUE;
  983. END;
  984. ELSE
  985. (* insert into other node *)
  986. ndx^.posarray[level].keynum :=downkeys+insertpos;
  987. DEC(ndx^.posarray[level-1].keynum);
  988. AddKeyTo(lowernodenum, recno, DownBuffer^.node, kstr,TRUE);
  989. IF ndx^.Safety OR NOT ndx^.Exclusive
  990. THEN
  991. WriteNode(ndx, ndx^.posarray[level].buffer^.number,
  992. ndx^.posarray[level].buffer^.node);
  993. WriteNode(ndx, DownBuffer^.number, DownBuffer^.node);
  994. ELSE
  995. ndx^.posarray[level].buffer^.NeedToWrite:=TRUE;
  996. DownBuffer^.NeedToWrite:=TRUE;
  997. END;
  998. (* force new pos array *)
  999. ReadIntoArray(ndx, DownBuffer^.number, level);
  1000. END;
  1001. ELSE (* move to upper *)
  1002. keystomove:=(keys-upkeys+1) DIV 2;
  1003. count:=keystomove;
  1004. WHILE count>0 DO
  1005. (* get key to move and save*)
  1006. (* remove from top place on bottom *)
  1007. GetKey(ndx^.posarray[level].buffer^.node,
  1008. ndx,ORD(ndx^.posarray[level].buffer^.node[0])-1, tempkey);
  1009. ndx^.posarray[level].keynum:=
  1010. ORD(ndx^.posarray[level].buffer^.node[0])-1;
  1011. DeleteEntry(ndx, level);
  1012. ndx^.posarray[level].keynum:=0;
  1013. AddKeyTo(tempkey.lowernode,tempkey.recordnum,
  1014. UpBuffer^.node,tempkey.value,FALSE);(* addkey does not write *)
  1015. DEC(count);
  1016. END (* while *);
  1017. (* delete key fixed upper node *)
  1018. IF insertpos < ORD(ndx^.posarray[level].buffer^.node[0]) THEN
  1019. (* insert into old node *)
  1020. ndx^.posarray[level].keynum:=insertpos;
  1021. AddKeyTo(lowernodenum, recno, ndx^.posarray[level].buffer^.node, kstr,TRUE);
  1022. (* force new pos array *)
  1023. (* need to put writes here because the readintoarray
  1024. will lose a node *)
  1025. IF ndx^.Safety OR NOT ndx^.Exclusive
  1026. THEN
  1027. WriteNode(ndx, ndx^.posarray[level].buffer^.number,
  1028. ndx^.posarray[level].buffer^.node);
  1029. WriteNode(ndx, UpBuffer^.number, UpBuffer^.node);
  1030. ELSE
  1031. ndx^.posarray[level].buffer^.NeedToWrite:=TRUE;
  1032. UpBuffer^.NeedToWrite:=TRUE;
  1033. END;
  1034. ELSE
  1035. (* insert into other node *)
  1036. ndx^.posarray[level].keynum :=
  1037. insertpos-ORD(ndx^.posarray[level].buffer^.node[0]);
  1038. INC(ndx^.posarray[level-1].keynum);
  1039. AddKeyTo(lowernodenum, recno, UpBuffer^.node, kstr,TRUE);
  1040. IF ndx^.Safety OR NOT ndx^.Exclusive
  1041. THEN
  1042. WriteNode(ndx, ndx^.posarray[level].buffer^.number,
  1043. ndx^.posarray[level].buffer^.node);
  1044. WriteNode(ndx, UpBuffer^.number, UpBuffer^.node);
  1045. ELSE
  1046. ndx^.posarray[level].buffer^.NeedToWrite:=TRUE;
  1047. UpBuffer^.NeedToWrite:=TRUE;
  1048. END;
  1049. (* force new pos array *)
  1050. ReadIntoArray(ndx, UpBuffer^.number, level);
  1051. END;
  1052. END;
  1053. END Movekeys ;
  1054. PROCEDURE CanBalance():BOOLEAN ;
  1055. BEGIN
  1056. IF (upkeys>=keys) AND (downkeys>=keys)
  1057. THEN
  1058. RETURN FALSE;
  1059. ELSIF (upkeys>downkeys) AND (keys-downkeys=1)
  1060. THEN (* downkey has only one place *)
  1061. IF ndx^.posarray[level].keynum=0 THEN
  1062. RETURN FALSE;
  1063. END;
  1064. ELSIF (upkeys<=downkeys) AND (keys-upkeys=1)
  1065. THEN (* upkey has only one place *)
  1066. IF (ndx^.posarray[level].keynum+1)>=keys THEN
  1067. RETURN FALSE;
  1068. END;
  1069. END;
  1070. RETURN TRUE;
  1071. END CanBalance;
  1072. BEGIN (*balance *)
  1073. downkeys:=65000;
  1074. upkeys:=65000;
  1075. DownBuffer:=NIL;
  1076. UpBuffer:=NIL;
  1077. IF (level=0) OR (level#ndx^.depth)
  1078. THEN
  1079. (* Because of complications do not balace nonleaf nodes *)
  1080. NewNode(ndx,NewBuffer);
  1081. Split(ndx^.posarray[level].buffer, NewBuffer);
  1082. RETURN;
  1083. END;
  1084. insertpos:=ndx^.posarray[level].keynum;
  1085. keys:=ORD(ndx^.posarray[level].buffer^.node[0]);
  1086. IF ndx^.posarray[level-1].keynum <
  1087. (ORD(ndx^.posarray[level-1].buffer^.node[0])-1)
  1088. THEN (* get uper node (same level *)
  1089. GetKey(ndx^.posarray[level-1].buffer^.node,
  1090. ndx, ndx^.posarray[level-1].keynum+1, tempkey);
  1091. ReadNode(ndx,tempkey.lowernode,UpBuffer);
  1092. UpBuffer^.Lock:=TRUE;
  1093. upkeys:=ORD(UpBuffer^.node[0]);
  1094. END;
  1095. IF ndx^.posarray[level-1].keynum > 0
  1096. THEN (* get lowernode (same level) *)
  1097. GetKey(ndx^.posarray[level-1].buffer^.node,
  1098. ndx, ndx^.posarray[level-1].keynum-1, tempkey);
  1099. ReadNode(ndx,tempkey.lowernode,DownBuffer);
  1100. DownBuffer^.Lock:=TRUE;
  1101. downkeys:=ORD(DownBuffer^.node[0]);
  1102. END;
  1103. (* determine if one can just move keys *)
  1104. IF CanBalance()
  1105. THEN
  1106. Movekeys;
  1107. (* ChkInd.NDXChk(ndx);
  1108. FindPositionCh(ndx,kstr,found);
  1109. IF NOT found THEN HALT END;*)
  1110. ELSE
  1111. NewNode(ndx,NewBuffer);
  1112. Split(ndx^.posarray[level].buffer, NewBuffer);
  1113. END;
  1114. IF DownBuffer#NIL THEN DownBuffer^.Lock:=FALSE END;
  1115. IF UpBuffer#NIL THEN UpBuffer^.Lock:=FALSE END;
  1116. END Balance;
  1117. BEGIN (* Insert Entry *)
  1118. (* update current key to keep all up to date *)
  1119. EnterLock(ndx);
  1120. (* diag:=recno (* diag *);*)
  1121. ndx^.Changed:=TRUE;
  1122. InHighNode:=FALSE;
  1123. (*IF ndx^.Header.NumType
  1124. THEN
  1125. Move(ADR(kstr),ADR(NewKey.value),8);
  1126. ELSE
  1127. Assign(kstr,NewKey.value);
  1128. END;
  1129. NewKey.lowernode:=VAL(LONGINT,0);
  1130. NewKey.recordnum:=recno; *)
  1131. level := ndx^.depth;
  1132. lowernodenum := VAL(LONGINT,0);
  1133. REPEAT
  1134. noOverflow := ORD(ndx^.posarray[level].buffer^.node[0]) < ndx^.Header.keyspernode;
  1135. IF noOverflow THEN
  1136. AddKeyTo(lowernodenum, recno, ndx^.posarray[level].buffer^.node, kstr,TRUE);
  1137. IF InHighNode
  1138. THEN
  1139. INC(ndx^.posarray[level].keynum);
  1140. END;
  1141. IF ndx^.Safety OR NOT ndx^.Exclusive
  1142. THEN
  1143. WriteNode(ndx, ndx^.posarray[level].buffer^.number,
  1144. ndx^.posarray[level].buffer^.node);
  1145. ELSE
  1146. ndx^.posarray[level].buffer^.NeedToWrite:=TRUE;
  1147. END;
  1148. ELSE
  1149. Balance(level);
  1150. (* find out where kstr belongs and insert it *)
  1151. IF level = 0 THEN (* the root node was split *)
  1152. INC(ndx^.buffsize,2);(* enlarge buffer *)
  1153. INC(ndx^.depth); (* prepare to add another level to the map *)
  1154. FOR i := ndx^.depth TO 1 BY -1 DO
  1155. ndx^.posarray[i] := ndx^.posarray[i-1]
  1156. END; (* slide all the keypositions in the map up one notch *)
  1157. NewNode(ndx,NewRoot);
  1158. (* force NewRoot into root position *)
  1159. ndx^.Header.rootptr := NewRoot^.number;
  1160. ReadIntoArray(ndx, NewRoot^.number, 0);
  1161. (* KEYNUMBERS BEGIN at ZERO *)
  1162. ndx^.posarray[0].keynum := 0;
  1163. NewBuffer^.Lock:=FALSE;
  1164. AddKeyTo(oldnodenum, VAL(LONGINT,0), NewRoot^.node, '',FALSE);
  1165. (* the new node initially contains no key but points to the
  1166. new node which was written when the old root was split *)
  1167. (* the number of entries in the node is now 1 *)
  1168. (* now a key is inserted ahead of the 'keyless' pointer *)
  1169. AddKeyTo(newnodenum, recno, NewRoot^.node, kstr,TRUE);
  1170. ndx^.posarray[0].buffer^.node[0]:= 1C;(* top key does not count *)
  1171. IF ndx^.posarray[1].buffer^.number = newnodenum THEN
  1172. ndx^.posarray[0].keynum := 0
  1173. ELSE
  1174. ndx^.posarray[0].keynum := 1
  1175. END;
  1176. IF ndx^.Safety OR NOT ndx^.Exclusive
  1177. THEN
  1178. WriteNode(ndx, ndx^.Header.rootptr, NewRoot^.node);
  1179. ELSE
  1180. NewRoot^.NeedToWrite:=TRUE;
  1181. END;
  1182. noOverflow := TRUE;
  1183. END (* if *);
  1184. (* if safety is on update header when a node splits *)
  1185. IF ndx^.Safety OR NOT ndx^.Exclusive
  1186. THEN
  1187. UpdateIndexHeader(ndx);
  1188. END;
  1189. END (* if *);
  1190. IF level > 0 THEN
  1191. DEC(level);
  1192. END (* if *);
  1193. UNTIL noOverflow;
  1194. ExitLock(ndx);
  1195. (* ChkInd.NDXChk(ndx);*)
  1196. (* !!!! diag *)
  1197. (* IF diag # ndx^.currentkey^.recordnum
  1198. THEN HALT END (*diag *); *)
  1199. END InsertEntry;
  1200. PROCEDURE BuildIndex(ndx: DBIndex;
  1201. keyexp: ARRAY OF CHAR):CARDINAL;
  1202. VAR
  1203. Fptr:DBFieldPtr;
  1204. BEGIN
  1205. IF NOT OpenDBF(ndx^.alias)
  1206. THEN (* check to make sure the file is open *)
  1207. WARN('Not able to DBFile file in BuildIndex');
  1208. END;
  1209. CrunchBlanks(keyexp);
  1210. CAPstr(keyexp);
  1211. ndx^.KeyNumber:=PosOfField(ndx^.alias,keyexp);
  1212. IF ndx^.KeyNumber=0 THEN
  1213. WARN('Bad index expression in BuildIndex');
  1214. END;
  1215. Fptr:=FieldList(ndx^.alias);
  1216. RETURN BuildCompIndex(ndx,Fptr^[ndx^.KeyNumber].fldtype,
  1217. keyexp,Fptr^[ndx^.KeyNumber].size);
  1218. END BuildIndex;
  1219. PROCEDURE BuildCompIndex( ndx: DBIndex;
  1220. type:CHAR; (* C or N *)
  1221. keyexp:ARRAY OF CHAR;
  1222. size: CARDINAL
  1223. ):CARDINAL;
  1224. VAR oldbuffsize,
  1225. olddbbuffersize,
  1226. i,
  1227. ActionTaken: CARDINAL;
  1228. worknode: NodeType;
  1229. recordnumber: LONGINT;
  1230. oldsafety,
  1231. oldexclusive:BOOLEAN;
  1232. FileError: StringIO.ErrorMessage;
  1233. BEGIN
  1234. IF NOT OpenDBF(ndx^.alias) THEN
  1235. WARN('Unable to open DBFile in BuildCompIndex');
  1236. END;
  1237. CloseIndex(ndx);
  1238. IF ndx^.Init#InitCode
  1239. THEN
  1240. WARN('Uninitalized DBIndex in BuildCompIndex');
  1241. END;
  1242. InitPosarray(ndx);
  1243. (* open exclusive *)
  1244. (* create if the file does not exist; truncate if it does exist *)
  1245. FileError := FAPI.DOSOPEN( ADR(ndx^.name),
  1246. ADR(ndx^.f), ADR(ActionTaken), VAL(LONGINT,1024),
  1247. FAPI.FILE_NORMAL,CARDINAL( {1,4}),CARDINAL( {1,4}),
  1248. VAL(LONGINT,0) );
  1249. IF FileError#0 THEN RETURN FileError END;
  1250. Fill(ADR(ndx^.Header),SIZE(ndx^.Header),0);
  1251. olddbbuffersize:=BufferSize(ndx^.alias);
  1252. oldbuffsize:=ndx^.buffsize;
  1253. oldsafety := ndx^.Safety;
  1254. oldexclusive:=ndx^.Exclusive;
  1255. SetDBBuffer( ndx^.alias,32000 );
  1256. IF ndx^.buffsize<400 THEN SetIndexBuffers(ndx,400) END;
  1257. WITH ndx^ DO
  1258. FOR i:=0 TO (Bins-1) DO
  1259. Hash[i]:=NIL;
  1260. END;
  1261. Safety:=FALSE;
  1262. Exclusive:=TRUE;
  1263. Assign(keyexp,Header.KeyExpression);
  1264. CrunchBlanks(Header.KeyExpression);
  1265. CAPstr(Header.KeyExpression);
  1266. KeyNumber := PosOfField(alias,Header.KeyExpression);
  1267. Append(Header.KeyExpression,' ');(* do this to mimic dbase3 *)
  1268. Header.rootptr := VAL(LONGINT,1); (* the root begins as the second block *)
  1269. (* The anchor node is 0 *)
  1270. Header.NumType := (type#'C');
  1271. IF type#'C'
  1272. THEN
  1273. Header.keylength:=8;
  1274. Header.entrylength :=16;
  1275. ELSE
  1276. Header.keylength := size;
  1277. Header.entrylength := Header.keylength + 2 * RecNumLen+1;
  1278. (*add 1 and make even to mimic dbase3 *)
  1279. IF ODD(Header.entrylength) THEN INC(Header.entrylength) END;
  1280. END;
  1281. Header.keyspernode := (NodeSize - 8) DIV (Header.entrylength);
  1282. (* a key 'entry' is made up of a pointer to a lower node and a
  1283. record number in addition to the key value . After the last key
  1284. entry there is a pointer to a lowerlevel node containing keys with
  1285. values greater than or equal to the the value of the key in the
  1286. last key entry *)
  1287. Header.nextfreenode := VAL(LONGINT,2);
  1288. open := TRUE;
  1289. depth := 0;
  1290. END;
  1291. InitNode(worknode);
  1292. WriteNode(ndx, VAL(LONGINT,1), worknode);
  1293. recordnumber := VAL(LONGINT,1);
  1294. WHILE recordnumber <= NumberRecords(ndx^.alias) DO
  1295. ReadDBRec(ndx^.alias, recordnumber);
  1296. (* change by ed ross*)
  1297. IF ndx^.includedeleted OR NOT Deleted(ndx^.alias)
  1298. THEN
  1299. AddRecord(ndx^.alias, ndx);
  1300. END;
  1301. (* * *End of change by ed *)
  1302. INC(recordnumber);
  1303. END;
  1304. CloseIndex(ndx);
  1305. ndx^.Exclusive:=oldexclusive;
  1306. ndx^.Safety:=oldsafety;
  1307. ndx^.buffsize:= oldbuffsize;
  1308. SetIndexBuffers(ndx,oldbuffsize);
  1309. SetDBBuffer( ndx^.alias,olddbbuffersize );
  1310. RETURN 0;
  1311. END BuildCompIndex;
  1312. PROCEDURE GoTop(ndx: DBIndex);
  1313. VAR nextnodeptr: LONGINT;
  1314. level: CARDINAL;
  1315. BEGIN
  1316. IF OpenIndex(ndx)=FALSE
  1317. THEN
  1318. WARN('Error opening Index in GoTop');
  1319. END;
  1320. EnterLock(ndx);
  1321. level := 0;
  1322. ReadIntoArray(ndx, ndx^.Header.rootptr, level);
  1323. Move(ADR(ndx^.posarray[level].buffer^.node[4]), ADR(nextnodeptr), 4);
  1324. (* all searches commence with the root *)
  1325. ndx^.posarray[level].keynum := FirstKey;
  1326. WHILE nextnodeptr#VAL(LONGINT,0) DO
  1327. INC(level);
  1328. ReadIntoArray(ndx, nextnodeptr, level);
  1329. ndx^.posarray[level].keynum := FirstKey;
  1330. Move(ADR(ndx^.posarray[level].buffer^.node[4]), ADR(nextnodeptr), 4);
  1331. END;
  1332. ndx^.currentkey:=GetKeyPtr(ndx^.posarray[level].buffer^.node, ndx, FirstKey);
  1333. ndx^.depth:=level;
  1334. ExitLock(ndx);
  1335. END GoTop;
  1336. PROCEDURE GoBottom( ndx: DBIndex);
  1337. VAR
  1338. level: CARDINAL;
  1339. BEGIN
  1340. IF OpenIndex(ndx)=FALSE
  1341. THEN
  1342. WARN('Error opening Index in GoBottom');
  1343. END;
  1344. EnterLock(ndx);
  1345. level := 0;
  1346. ReadIntoArray(ndx, ndx^.Header.rootptr, level);
  1347. ndx^.currentkey:=GetKeyPtr(ndx^.posarray[level].buffer^.node, ndx,
  1348. ORD(ndx^.posarray[level].buffer^.node[0]));
  1349. (* all searches commence with the root *)
  1350. ndx^.posarray[level].keynum := ORD(ndx^.posarray[level].buffer^.node[0]);
  1351. WHILE ndx^.currentkey^.lowernode # VAL(LONGINT,0) DO
  1352. INC(level);
  1353. ReadIntoArray(ndx, ndx^.currentkey^.lowernode, level);
  1354. ndx^.posarray[level].keynum := ORD(ndx^.posarray[level].buffer^.node[0]);
  1355. ndx^.currentkey:=GetKeyPtr(ndx^.posarray[level].buffer^.node, ndx,
  1356. ORD(ndx^.posarray[level].buffer^.node[0]));
  1357. END;
  1358. ndx^.posarray[level].keynum := ORD(ndx^.posarray[level].buffer^.node[0])-1;
  1359. ndx^.currentkey:=GetKeyPtr(ndx^.posarray[level].buffer^.node, ndx,
  1360. ORD(ndx^.posarray[level].buffer^.node[0])-1 );
  1361. ndx^.depth:=level;
  1362. ExitLock(ndx);
  1363. END GoBottom;
  1364. PROCEDURE AddToUpdateList( alias: DBFile; ndx:
  1365. DBIndex);
  1366. BEGIN
  1367. ndx^.UpdateList:=IndexList(alias);
  1368. SetIndexList(alias,ndx);
  1369. END AddToUpdateList;
  1370. PROCEDURE UpdateDBIndxes(alias:DBFile);
  1371. VAR
  1372. ndx:DBIndex;
  1373. PROCEDURE Update ;
  1374. VAR
  1375. NewKey,OldKey:ARRAY[0..MaxField-1] OF CHAR;
  1376. found:BOOLEAN;
  1377. num:LONGINT;
  1378. PROCEDURE KeyLocatedC():BOOLEAN ;
  1379. VAR
  1380. found:BOOLEAN;
  1381. BEGIN
  1382. FindPositionCh(ndx,OldKey,found);
  1383. LOOP
  1384. IF NOT Equal(OldKey,ndx^.currentkey^.value)
  1385. THEN
  1386. RETURN FALSE;
  1387. END;
  1388. IF (Record(alias) = ndx^.currentkey^.recordnum)
  1389. THEN
  1390. RETURN TRUE;
  1391. END;
  1392. IF NOT NextRecord(ndx,num)
  1393. THEN
  1394. RETURN FALSE;
  1395. END;
  1396. END;
  1397. END KeyLocatedC ;
  1398. PROCEDURE KeyLocatedN():BOOLEAN ;
  1399. VAR
  1400. found:BOOLEAN;
  1401. key:Real8;
  1402. BEGIN
  1403. IF NOT StrToReal(OldKey, 0,key) THEN key:=0.0 END;
  1404. FindPositionN(ndx,OldKey,found);
  1405. LOOP
  1406. IF ndx^.numkey^.key # key
  1407. THEN
  1408. RETURN FALSE;
  1409. END;
  1410. IF (Record(alias) = ndx^.currentkey^.recordnum)
  1411. THEN
  1412. RETURN TRUE;
  1413. END;
  1414. IF NOT NextRecord(ndx,num)
  1415. THEN
  1416. RETURN FALSE;
  1417. END;
  1418. END;
  1419. END KeyLocatedN ;
  1420. BEGIN
  1421. IF NOT Appending(alias)
  1422. THEN
  1423. SetRecordMode(alias,ModBase3.Buffer);
  1424. ndx^.KeyProc(alias,ndx,OldKey);
  1425. SetRecordMode(alias,ModBase3.CurrentRec);
  1426. ndx^.KeyProc(alias,ndx,NewKey);
  1427. (* * * * * * * * * Changed by ed - delete index if deleting record* * * *)
  1428. IF ndx^.includedeleted OR NOT Deleted(alias)
  1429. THEN IF Equal(OldKey,NewKey)
  1430. THEN
  1431. RETURN
  1432. END;
  1433. END;
  1434. IF Record(alias) # ndx^.currentkey^.recordnum
  1435. THEN
  1436. (* find and delete old key if exists *)
  1437. IF ndx^.Header.NumType
  1438. THEN
  1439. found:=KeyLocatedN();
  1440. ELSE
  1441. found:=KeyLocatedC();
  1442. END;
  1443. ELSE
  1444. found :=TRUE;
  1445. END;
  1446. IF found
  1447. THEN
  1448. DeleteCurrentEntry(ndx);
  1449. ELSE
  1450. found:=FALSE; (* debugger trap *)
  1451. END;
  1452. END;
  1453. (* * * * * * * * Changed by Ed - same as above ** ** * * * *)
  1454. IF ndx^.includedeleted OR NOT Deleted(alias)
  1455. THEN
  1456. AddRecord(alias,ndx);
  1457. END;
  1458. END Update;
  1459. BEGIN
  1460. (* Nul Value of IndexList should be checked in Modbase *)
  1461. ndx:=IndexList(alias);
  1462. WHILE ndx#NIL DO
  1463. IF OpenIndex(ndx)=FALSE
  1464. THEN
  1465. WARN('Error opening Index in UpdateDBIndxes');
  1466. END;
  1467. EnterLock(ndx);
  1468. Update;
  1469. ExitLock(ndx);
  1470. ndx:=ndx^.UpdateList;
  1471. END (* while *);
  1472. END UpdateDBIndxes;
  1473. PROCEDURE UpdateUnique( alias: DBFile; ndx: DBIndex):BOOLEAN;
  1474. VAR
  1475. NewKey,OldKey:ARRAY[0..MaxField-1] OF CHAR;
  1476. found:BOOLEAN;
  1477. BEGIN
  1478. IF OpenIndex(ndx)=FALSE
  1479. THEN
  1480. WARN('Error opening Index in UpdateUnique');
  1481. END;
  1482. EnterLock(ndx);
  1483. SetRecordMode(alias,ModBase3.CurrentRec);
  1484. ndx^.KeyProc(alias,ndx,NewKey);
  1485. IF NOT Appending(alias)
  1486. THEN
  1487. SetRecordMode(alias,ModBase3.Buffer);
  1488. ndx^.KeyProc(alias,ndx,OldKey);
  1489. SetRecordMode(alias,ModBase3.CurrentRec);
  1490. IF Equal(OldKey,NewKey)
  1491. THEN
  1492. ExitLock(ndx);
  1493. RETURN TRUE;
  1494. END;
  1495. END;
  1496. IF ndx^.Header.NumType
  1497. THEN
  1498. FindPositionN(ndx,NewKey,found);
  1499. ELSE
  1500. FindPositionCh(ndx,NewKey,found);
  1501. END;
  1502. ExitLock(ndx);
  1503. RETURN NOT found;
  1504. END UpdateUnique;
  1505. PROCEDURE SetSafetyOn( ndx: DBIndex);
  1506. BEGIN
  1507. UpdateIndex(ndx);
  1508. ndx^.Safety:=TRUE;
  1509. END SetSafetyOn;
  1510. PROCEDURE SetSafetyOff( ndx: DBIndex);
  1511. BEGIN
  1512. ndx^.Safety:=FALSE;
  1513. END SetSafetyOff;
  1514. PROCEDURE SetIndexBuffers( ndx: DBIndex;Buffers:CARDINAL);
  1515. VAR
  1516. buffer:IndexBuffer;
  1517. BEGIN
  1518. WITH ndx^ DO
  1519. buffsize := Max(Buffers,depth+6);
  1520. buffer:=last;
  1521. WHILE buffsize<currsize DO
  1522. WHILE buffer^.Lock DO
  1523. buffer:=buffer^.prev;
  1524. END;
  1525. IF buffer^.NeedToWrite
  1526. THEN
  1527. WriteNode(ndx,buffer^.number,buffer^.node);
  1528. END;
  1529. RemoveBuffer(ndx,buffer);
  1530. RemoveFromTable(ndx,buffer);
  1531. DosDealloc(buffer,SIZE(buffer^));
  1532. END;
  1533. END;
  1534. END SetIndexBuffers;
  1535. PROCEDURE DisposeIndex(VAR ndx: DBIndex);
  1536. BEGIN
  1537. CloseIndex(ndx);
  1538. DosDealloc(ndx,SIZE(ndx^));
  1539. ndx := NIL;
  1540. END DisposeIndex;
  1541. PROCEDURE CurrentKeyCh( ndx: DBIndex;VAR val:ARRAY OF CHAR);
  1542. BEGIN
  1543. Assign(ndx^.currentkey^.value,val);
  1544. END CurrentKeyCh;
  1545. PROCEDURE CurrentKeyN( ndx: DBIndex):Real8;
  1546. BEGIN
  1547. RETURN ndx^.numkey^.key;
  1548. END CurrentKeyN;
  1549. PROCEDURE CurrentRec( ndx: DBIndex):LONGINT;
  1550. BEGIN
  1551. RETURN ndx^.currentkey^.recordnum;
  1552. END CurrentRec;
  1553. PROCEDURE NumKeyType( ndx: DBIndex):BOOLEAN;
  1554. BEGIN
  1555. RETURN ndx^.Header.NumType;
  1556. END NumKeyType;
  1557. PROCEDURE KeyLength( ndx: DBIndex):CARDINAL;
  1558. BEGIN
  1559. RETURN ndx^.Header.keylength;
  1560. END KeyLength;
  1561. PROCEDURE InitCompIndex(indexname: ARRAY OF CHAR; VAR
  1562. ndx: DBIndex; alias:DBFile;Key :KeyProcedure; buffersize: CARDINAL
  1563. ; safety,IncludeDeleted,exclusive:BOOLEAN);
  1564. VAR i:CARDINAL;
  1565. BEGIN
  1566. DosAlloc(ndx,SIZE(ndx^));
  1567. ndx^.alias:=alias;
  1568. WITH ndx^ DO
  1569. Init:=InitCode;
  1570. Assign(indexname,name);
  1571. Locked:=0;
  1572. includedeleted:=IncludeDeleted;
  1573. Exclusive:=exclusive OR Locks.ExclusiveOnly;
  1574. open:=FALSE;
  1575. depth:=0;
  1576. KeyProc:=Key ;
  1577. buffsize:=buffersize;
  1578. Safety:=safety;
  1579. first:=NIL;
  1580. last:=NIL;
  1581. currsize := 0;
  1582. UpdateList:=NIL;
  1583. END;
  1584. END InitCompIndex;
  1585. PROCEDURE InitIndex(indexname: ARRAY OF CHAR; VAR
  1586. ndx: DBIndex; alias:DBFile; buffersize: CARDINAL;
  1587. safety, IncludeDeleted,exclusive:BOOLEAN);
  1588. BEGIN
  1589. InitCompIndex(indexname,ndx,alias,DefaultKeyProcedure,buffersize,safety,
  1590. IncludeDeleted,exclusive);
  1591. END InitIndex;
  1592. PROCEDURE OpenIndex( ndx: DBIndex):BOOLEAN;
  1593. VAR
  1594. str:ARRAY[0..387] OF CHAR;
  1595. ActionTaken,
  1596. i,res:CARDINAL;
  1597. filemode:BITSET;
  1598. BEGIN
  1599. IF ndx = NIL
  1600. THEN
  1601. WARN('Unititalized ndx in OpenIndex');
  1602. RETURN FALSE;
  1603. END;
  1604. IF ndx^.Init=InitCode
  1605. THEN
  1606. IF ndx^.open
  1607. THEN
  1608. RETURN TRUE;
  1609. END;
  1610. ELSE
  1611. WARN('Uninitalized ndx in OpenIndex');
  1612. END;
  1613. ndx^.open := FALSE;
  1614. (* Open if it does exist; fail if it doesn't *)
  1615. IF ndx^.Exclusive THEN
  1616. filemode:={1,4}
  1617. ELSE
  1618. filemode:={1,6} (* allow all *)
  1619. END;
  1620. res := FAPI.DOSOPEN( ADR(ndx^.name),
  1621. ADR(ndx^.f), ADR(ActionTaken), VAL(LONGINT,0), FAPI.FILE_NORMAL,
  1622. CARDINAL( {0}), CARDINAL(filemode), VAL(LONGINT,0) );
  1623. IF res # StringIO.NoError THEN
  1624. RETURN FALSE;
  1625. ELSE
  1626. IF Locks.NoLocking(ndx^.f) THEN ndx^.Exclusive:=TRUE END;
  1627. InitPosarray(ndx);
  1628. EnterLock(ndx);
  1629. IF HandleIO.BlockRead(ndx^.f,ADR(ndx^.Header),SIZE(ndx^.Header))#
  1630. StringIO.NoError
  1631. THEN
  1632. ExitLock(ndx);
  1633. StringIO.PrintMessage(HandleIO.CloseHandle(ndx^.f));
  1634. RETURN FALSE;
  1635. END;
  1636. WITH ndx^ DO
  1637. FOR i:=0 TO (Bins-1) DO
  1638. Hash[i]:=NIL;
  1639. END;
  1640. Assign(Header.KeyExpression,str);
  1641. CrunchBlanks(str);
  1642. CAPstr(str);
  1643. KeyNumber:=PosOfField(ndx^.alias,str);
  1644. SetIndexBuffers(ndx,buffsize);
  1645. open := TRUE;
  1646. END;
  1647. GoTop(ndx);
  1648. ExitLock(ndx);
  1649. END; (* IF *)
  1650. RETURN TRUE;
  1651. END OpenIndex;
  1652. PROCEDURE NextRecord( ndx: DBIndex;
  1653. VAR recno: LONGINT): BOOLEAN;
  1654. PROCEDURE NextEntry(ndx: DBIndex; level: CARDINAL): BOOLEAN;
  1655. VAR
  1656. anotherkey: BOOLEAN;
  1657. key:KeyPointer;
  1658. factor:CARDINAL;
  1659. BEGIN
  1660. key:=ADR(ndx^.currentkey);
  1661. LOOP
  1662. IF level=ndx^.depth (* ok depth becuse all of loop is in same route*)
  1663. THEN
  1664. factor:=1;
  1665. ELSE
  1666. factor:=0;
  1667. END;
  1668. anotherkey := ndx^.posarray[level].keynum <
  1669. ( ORD(ndx^.posarray[level].buffer^.node[0]) - factor);
  1670. IF anotherkey THEN
  1671. (* there is another entry in the node *)
  1672. (* note that the first entry is 0, so the number of the last entry
  1673. is one less than the number of keys in the node *)
  1674. WITH ndx^.posarray[level] DO
  1675. INC(keynum);
  1676. key:=GetKeyPtr(buffer^.node, ndx, keynum);
  1677. EXIT;
  1678. END;
  1679. ELSE
  1680. IF level = 0 THEN
  1681. EXIT
  1682. ELSE
  1683. DEC(level)
  1684. END;
  1685. END;
  1686. END; (* LOOP *)
  1687. IF anotherkey THEN
  1688. LOOP
  1689. IF key^.lowernode#VAL(LONGINT,0) THEN (* node is not a leaf node *)
  1690. INC(level);
  1691. ReadIntoArray(ndx, key^.lowernode, level);
  1692. WITH ndx^.posarray[level] DO
  1693. keynum := FirstKey;
  1694. key:=GetKeyPtr(buffer^.node, ndx, FirstKey);
  1695. END;
  1696. ELSE
  1697. EXIT
  1698. END;
  1699. END; (* LOOP2 *)
  1700. ndx^.depth:=level;
  1701. ndx^.currentkey:=key;
  1702. RETURN TRUE;
  1703. ELSE
  1704. ndx^.depth:=level;
  1705. ndx^.currentkey:=key;
  1706. RETURN FALSE;
  1707. END;
  1708. END NextEntry;
  1709. BEGIN
  1710. IF OpenIndex(ndx)=FALSE
  1711. THEN
  1712. WARN('Error opening Index in NextRecord');
  1713. END;
  1714. EnterLock(ndx);
  1715. IF NextEntry(ndx, ndx^.depth) THEN
  1716. recno := ndx^.currentkey^.recordnum;
  1717. ExitLock(ndx);
  1718. RETURN TRUE
  1719. ELSE
  1720. ExitLock(ndx);
  1721. RETURN FALSE
  1722. END;
  1723. END NextRecord;
  1724. PROCEDURE PrevRecord( ndx: DBIndex;
  1725. VAR recno: LONGINT): BOOLEAN;
  1726. PROCEDURE PrevEntry( ndx: DBIndex; level: CARDINAL): BOOLEAN;
  1727. VAR anotherkey: BOOLEAN;
  1728. lastkey: CARDINAL;
  1729. key:KeyPointer;
  1730. BEGIN
  1731. key:=ADR(ndx^.currentkey);
  1732. LOOP
  1733. anotherkey := ndx^.posarray[level].keynum > 0;
  1734. IF anotherkey THEN
  1735. (* there is another entry in the node *)
  1736. (* note that the first entry is 0, so the number of the last entry
  1737. is one less than the number of keys in the node *)
  1738. WITH ndx^.posarray[level] DO
  1739. DEC(keynum);
  1740. key:=GetKeyPtr(buffer^.node, ndx, keynum);
  1741. END;
  1742. EXIT;
  1743. ELSE
  1744. IF level = 0 THEN
  1745. EXIT
  1746. ELSE
  1747. DEC(level)
  1748. END;
  1749. END;
  1750. END; (* LOOP *)
  1751. IF anotherkey THEN
  1752. LOOP
  1753. IF key^.lowernode#VAL(LONGINT,0) THEN (* node is not a leaf node *)
  1754. INC(level);
  1755. ReadIntoArray(ndx, key^.lowernode, level);
  1756. WITH ndx^.posarray[level] DO
  1757. lastkey := ORD(buffer^.node[0]);
  1758. keynum := lastkey;
  1759. key:=GetKeyPtr(buffer^.node, ndx, lastkey);
  1760. IF key^.lowernode=VAL(LONGINT,0)
  1761. THEN (* backup one*)
  1762. DEC(lastkey);
  1763. keynum := lastkey;
  1764. key:=GetKeyPtr(buffer^.node, ndx, lastkey);
  1765. END;
  1766. END;
  1767. ELSE
  1768. EXIT
  1769. END;
  1770. END; (* LOOP2 *)
  1771. ndx^.depth:=level;
  1772. ndx^.currentkey:=key;
  1773. RETURN TRUE;
  1774. ELSE
  1775. ndx^.depth:=level;
  1776. ndx^.currentkey:=key;
  1777. RETURN FALSE;
  1778. END;
  1779. END PrevEntry;
  1780. BEGIN
  1781. IF OpenIndex(ndx)=FALSE
  1782. THEN
  1783. WARN('Error opening Index in PrevRecord');
  1784. END;
  1785. EnterLock(ndx);
  1786. IF PrevEntry(ndx, ndx^.depth) THEN
  1787. recno := ndx^.currentkey^.recordnum;
  1788. ExitLock(ndx);
  1789. RETURN TRUE
  1790. ELSE
  1791. ExitLock(ndx);
  1792. RETURN FALSE
  1793. END;
  1794. END PrevRecord;
  1795. PROCEDURE UpdateIndex( ndx :DBIndex);
  1796. BEGIN
  1797. IF ndx^.open
  1798. THEN
  1799. EnterLock(ndx);
  1800. UpdateIndexHeader(ndx);
  1801. HandleIO.UpdateDisk(ndx^.f);
  1802. ExitLock(ndx);
  1803. END;
  1804. END UpdateIndex;
  1805. PROCEDURE CloseIndex( ndx: DBIndex);
  1806. VAR
  1807. buffer:IndexBuffer;
  1808. FileError: StringIO.ErrorMessage;
  1809. BEGIN
  1810. IF ndx = NIL
  1811. THEN
  1812. RETURN;
  1813. END;
  1814. IF NOT ndx^.open
  1815. THEN
  1816. RETURN;
  1817. END;
  1818. IF ndx^.Exclusive AND NOT ndx^.Safety
  1819. THEN
  1820. UpdateIndexHeader( ndx );
  1821. END;
  1822. FileError := HandleIO.CloseHandle(ndx^.f);
  1823. ndx^.open := FALSE;
  1824. WHILE ndx^.currsize#0 DO
  1825. buffer:=ndx^.last;
  1826. RemoveBuffer(ndx,buffer);
  1827. DosDealloc(buffer,SIZE(buffer^));
  1828. END;
  1829. END CloseIndex;
  1830. (* file locking procedures start here *)
  1831. PROCEDURE EnterLock( ndx:DBIndex);
  1832. VAR
  1833. realkey:Real8;
  1834. strkey:ARRAY[0..127] OF CHAR;
  1835. buffer:IndexBuffer;
  1836. ok,found:BOOLEAN;
  1837. i,
  1838. code,
  1839. Old :CARDINAL;
  1840. key,OldRecord:LONGINT;
  1841. BEGIN
  1842. (* lock file if needed *)
  1843. INC(ndx^.Locked);
  1844. IF (ndx^.Locked>1) OR ndx^.Exclusive THEN RETURN END;
  1845. (* read Header*)
  1846. StringIO.PrintMessage(Locks.LockFileRetry(ndx^.f,100,ndx^.name));
  1847. ndx^.Changed:=FALSE;
  1848. IF ndx^.open=FALSE THEN RETURN END;(* this should only be in open index *)
  1849. Old:=ndx^.Header.Flag;
  1850. ReadHeader(ndx);
  1851. IF Old=ndx^.Header.Flag THEN RETURN END;
  1852. OldRecord:=ndx^.currentkey^.recordnum;
  1853. IF ndx^.Header.NumType
  1854. THEN
  1855. realkey:=ndx^.numkey^.key;
  1856. ELSE
  1857. Assign(ndx^.currentkey^.value,strkey);
  1858. END;
  1859. (* purge buffers*)
  1860. WHILE ndx^.currsize#0 DO
  1861. buffer:=ndx^.last; (* it is forbidden here to have unwritten data *)
  1862. IF buffer^.NeedToWrite THEN (* not needed when debugged *)
  1863. WARN('buffer not writen in EnterLock');
  1864. END;
  1865. RemoveBuffer(ndx,buffer);
  1866. RemoveFromTable(ndx,buffer);
  1867. DosDealloc(buffer,SIZE(buffer^));
  1868. END;
  1869. FOR i:= 0 TO ndx^.depth DO
  1870. ndx^.posarray[i].buffer:=NIL;
  1871. END;
  1872. IF ndx^.Header.NumType
  1873. THEN
  1874. FindPositionR(ndx,realkey,found);
  1875. ELSE
  1876. FindPositionCh(ndx,strkey,found);
  1877. END;
  1878. IF NOT found
  1879. THEN RETURN (* key must have been removed *)
  1880. END;
  1881. REPEAT
  1882. IF OldRecord=ndx^.currentkey^.recordnum
  1883. THEN RETURN END; (* we got it*)
  1884. found:=NextRecord(ndx,key);
  1885. IF ndx^.Header.NumType
  1886. THEN
  1887. ok:=(realkey=ndx^.numkey^.key);
  1888. ELSE
  1889. ok:=Equal(ndx^.currentkey^.value,strkey);
  1890. END;
  1891. UNTIL NOT found OR NOT ok;
  1892. found:=PrevRecord(ndx,key); (* goback one*)
  1893. END EnterLock;
  1894. PROCEDURE ExitLock( ndx:DBIndex);
  1895. VAR
  1896. code:CARDINAL;
  1897. BEGIN
  1898. DEC(ndx^.Locked);
  1899. (* If No change or exclusive *)
  1900. IF ndx^.Exclusive OR ( ndx^.Locked#0) THEN RETURN END;
  1901. IF ndx^.Changed
  1902. THEN
  1903. INC(ndx^.Header.Flag); (* indicate change *)
  1904. WriteHeader(ndx); (* write header *)
  1905. END; (* if ndx^ changed *)
  1906. code:=Locks.UnLockFile(ndx^.f);
  1907. IF code#0 THEN WARN('Lock error in ExitLock') END;
  1908. END ExitLock;
  1909. BEGIN;
  1910. UpDateIndexes:=UpdateDBIndxes;
  1911. END DBIndxes.
  1912.