isamaddindex.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. /*%% (c) Copyright 1993, 1994 Hewlett-Packard Company */
  24. /*%% (c) Copyright 1993, 1994 International Business Machines Corp. */
  25. /*%% (c) Copyright 1993, 1994 Sun Microsystems, Inc. */
  26. /*%% (c) Copyright 1993, 1994 Novell, Inc. */
  27. /*%% $XConsortium: isamaddindex.c /main/3 1995/10/23 11:34:12 rswiston $ */
  28. /*
  29. * Copyright (c) 1988 by Sun Microsystems, Inc.
  30. */
  31. /*
  32. * isaddindex.c
  33. *
  34. * Description:
  35. * Add secondary index
  36. *
  37. *
  38. */
  39. #include "isam_impl.h"
  40. #include <sys/types.h>
  41. #include <sys/stat.h>
  42. extern int _iskeycmp();
  43. static void _readallrecords(), _attach_dups_serial();
  44. static Blkno _buildbtree();
  45. static int _duplicate_exist();
  46. static void checkavailfd(void);
  47. /*
  48. * _amaddindex(isfhandle, keydesc, errcode)
  49. *
  50. * _amaddindex() build a secondary index
  51. *
  52. * Input params:
  53. * isfhandle Handle of ISAM file
  54. * keydesc Key descriptor
  55. *
  56. * Output params:
  57. * errcode error status of the operation
  58. *
  59. */
  60. /*ARGSUSED*/
  61. int
  62. _amaddindex(Bytearray *isfhandle, struct keydesc *keydesc, struct errcode *errcode)
  63. {
  64. Fcb *fcb = NULL;
  65. Keydesc2 keydesc2;
  66. int err;
  67. _isam_entryhook();
  68. /*
  69. * Check if 1 UNIX file decriptor is available.
  70. */
  71. checkavailfd();
  72. /*
  73. * Get FCB corresponding to the isfhandle handle.
  74. */
  75. if ((fcb = _openfcb(isfhandle, errcode)) == NULL) {
  76. _isam_exithook();
  77. return (ISERROR);
  78. }
  79. /*
  80. * Check that the limit on the number of keys is not exceeded.
  81. */
  82. if (fcb->nkeys >= MAXNKEYS) {
  83. _amseterrcode(errcode, ETOOMANY);
  84. goto ERROR;
  85. }
  86. /*
  87. * Update information in FCB from CNTL page on the disk
  88. */
  89. (void)_isfcb_cntlpg_r(fcb);
  90. /*
  91. * Check key descriptor for validity.
  92. */
  93. if (_validate_keydesc(keydesc, fcb->minreclen) != ISOK) {
  94. _amseterrcode(errcode, EBADKEY);
  95. goto ERROR;
  96. }
  97. /*
  98. * Convert key descriptor to internal form.
  99. */
  100. _iskey_xtoi (&keydesc2, keydesc);
  101. /* Check if key already exists. */
  102. if (_isfcb_findkey(fcb ,&keydesc2) != (Keydesc2 *) 0) {
  103. _amseterrcode(errcode, EKEXISTS);
  104. goto ERROR;
  105. }
  106. /*
  107. * Open (or even create) .ind file if that is not open (created) yet.
  108. */
  109. if (_open2_indfile(fcb) != ISOK) {
  110. _amseterrcode(errcode, EACCES);
  111. goto ERROR;
  112. }
  113. /*
  114. * Create index structure.
  115. */
  116. if ((err = _create_index(fcb ,&keydesc2)) != ISOK) {
  117. _amseterrcode(errcode, err);
  118. goto ERROR;
  119. }
  120. /*
  121. * Add key descriptor to FCB.
  122. */
  123. if (_isfcb_altkeyadd(fcb, &keydesc2) == ISERROR) {
  124. _amseterrcode(errcode, ETOOMANY);
  125. goto ERROR;
  126. }
  127. _amseterrcode(errcode, ISOK);
  128. _issignals_mask();
  129. _isdisk_commit();
  130. _isdisk_sync();
  131. _isdisk_inval();
  132. /*
  133. * Update CNTL Page from the FCB.
  134. */
  135. (void)_isfcb_cntlpg_w(fcb);
  136. _issignals_unmask();
  137. _isam_exithook();
  138. return (ISOK);
  139. ERROR:
  140. _isdisk_rollback();
  141. _isdisk_inval();
  142. /*
  143. * Restore FCB from CNTL page.
  144. */
  145. if (fcb) (void)_isfcb_cntlpg_r(fcb);
  146. _isam_exithook();
  147. return (ISERROR);
  148. }
  149. /*
  150. * _amaddprimary(isfhandle, keydesc, errcode)
  151. *
  152. * _amaddprimary() build primary key
  153. *
  154. * Input params:
  155. * isfhandle Handle of ISAM file
  156. * keydesc Key descriptor
  157. *
  158. * Output params:
  159. * errcode error status of the operation
  160. *
  161. */
  162. /*ARGSUSED*/
  163. int
  164. _amaddprimary(Bytearray *isfhandle, struct keydesc *keydesc, struct errcode *errcode)
  165. {
  166. Fcb *fcb = NULL;
  167. Keydesc2 keydesc2;
  168. int err;
  169. _isam_entryhook();
  170. /*
  171. * Check if 1 UNIX file decriptor is available.
  172. */
  173. checkavailfd();
  174. /*
  175. * Get FCB corresponding to the isfhandle handle.
  176. */
  177. if ((fcb = _openfcb(isfhandle, errcode)) == NULL) {
  178. _isam_exithook();
  179. return (ISERROR);
  180. }
  181. /*
  182. * Check that the limit on the numbrer of keys is not exceeded.
  183. */
  184. if (fcb->nkeys >= MAXNKEYS) {
  185. _amseterrcode(errcode, ETOOMANY);
  186. goto ERROR;
  187. }
  188. /*
  189. * Update information in FCB from CNTL page on the disk
  190. */
  191. (void)_isfcb_cntlpg_r(fcb);
  192. /*
  193. * Check key descriptor for validity.
  194. */
  195. if (_validate_keydesc(keydesc, fcb->minreclen) != ISOK) {
  196. _amseterrcode(errcode, EBADKEY);
  197. goto ERROR;
  198. }
  199. /*
  200. * Convert key descriptor to internal form.
  201. */
  202. _iskey_xtoi (&keydesc2, keydesc);
  203. /* Check if key already exists. */
  204. if (_isfcb_findkey(fcb ,&keydesc2) != (Keydesc2 *) 0) {
  205. _amseterrcode(errcode, EKEXISTS);
  206. goto ERROR;
  207. }
  208. /*
  209. * Check that primary key does not already exist.
  210. */
  211. if (!FCB_NOPRIMARY_KEY(fcb)) {
  212. _amseterrcode(errcode, EKEXISTS);
  213. goto ERROR;
  214. }
  215. /*
  216. * Open (or even create) .ind file if that is not open (created) yet.
  217. */
  218. if (_open2_indfile(fcb) != ISOK) {
  219. _amseterrcode(errcode, EACCES);
  220. goto ERROR;
  221. }
  222. /*
  223. * Create index structure.
  224. */
  225. if ((err = _create_index(fcb ,&keydesc2)) != ISOK) {
  226. _amseterrcode(errcode, err);
  227. goto ERROR;
  228. }
  229. /*
  230. * Add key descriptor to FCB.
  231. */
  232. if (_isfcb_primkeyadd(fcb, &keydesc2) == ISERROR) {
  233. _amseterrcode(errcode, ETOOMANY);
  234. goto ERROR;
  235. }
  236. _amseterrcode(errcode, ISOK);
  237. _issignals_mask();
  238. _isdisk_commit();
  239. _isdisk_sync();
  240. _isdisk_inval();
  241. /*
  242. * Update CNTL Page from the FCB.
  243. */
  244. (void)_isfcb_cntlpg_w(fcb);
  245. _issignals_unmask();
  246. _isam_exithook();
  247. return (ISOK);
  248. ERROR:
  249. _isdisk_rollback();
  250. _isdisk_inval();
  251. /*
  252. * Restore FCB from CNTL page.
  253. */
  254. if (fcb) (void)_isfcb_cntlpg_r(fcb);
  255. _isam_exithook();
  256. return (ISERROR);
  257. }
  258. /*
  259. * _create_index()
  260. *
  261. * Read data file, extract key from record, sort keys, create index.
  262. * Check unique constraint, create duplicate serial numbers.
  263. */
  264. int _create_index(Fcb *fcb, Keydesc2 *pkeydesc2)
  265. {
  266. Issort *srt;
  267. int keylength = pkeydesc2->k2_len;
  268. /*
  269. * Set comparison function for sorting.
  270. *
  271. * nparts + 1 is used to compare keys that allow duplicates.
  272. * The (nparts + 1) comparison will never be reached on ISNODUPS key.
  273. */
  274. _iskeycmp_set(pkeydesc2, pkeydesc2->k2_nparts+1);
  275. /*
  276. * Handle empty file as a special case, to avoid nasty behavior
  277. * in buildbtree() arithmetics.
  278. */
  279. if (fcb->nrecords == 0L) {
  280. pkeydesc2->k2_rootnode = _buildbtree(fcb, pkeydesc2, (Issort *) NULL);
  281. return (ISOK);
  282. }
  283. /*
  284. * Create a sorter for this key descriptor.
  285. */
  286. srt = _issort_create(keylength, (int)fcb->nrecords, _iskeycmp);
  287. /*
  288. * Read sequentially all records, extract keys, and
  289. * insert the keys into the sorter.
  290. */
  291. _readallrecords(fcb, srt, pkeydesc2);
  292. _issort_sort(srt); /* Sort the keys */
  293. /*
  294. * Check for potential duplicates if the index is ISNODUPS.
  295. */
  296. if (!ALLOWS_DUPS2(pkeydesc2)) {
  297. if (_duplicate_exist(srt, keylength)) {
  298. _issort_destroy(srt);
  299. return (EDUPL);
  300. }
  301. }
  302. /*
  303. * Attach duplicate serial numbers to the keys that are ISDUPS.
  304. */
  305. if (ALLOWS_DUPS2(pkeydesc2)) {
  306. _attach_dups_serial(srt, pkeydesc2);
  307. }
  308. /*
  309. * Allocate and build the B-tree
  310. */
  311. pkeydesc2->k2_rootnode = _buildbtree(fcb, pkeydesc2, srt);
  312. _issort_destroy(srt); /* Destroy sorter */
  313. return (ISOK);
  314. }
  315. /*
  316. * _readallrecords()
  317. *
  318. * REad all records, extract keys, and insert them into sorter.
  319. */
  320. Static void
  321. _readallrecords(Fcb *fcb, Issort *srt, Keydesc2 *pkeydesc2)
  322. {
  323. char record [ISMAXRECLEN];
  324. char keybuf [MAXKEYSIZE];
  325. Recno recnum;
  326. int reclen = 0;
  327. int (*rec_read)() = (fcb->varflag?_vlrec_read:_flrec_read);
  328. for (recnum = 1; recnum <= fcb->lastrecno; recnum++) {
  329. if (rec_read(fcb, record, recnum, &reclen) != ISOK)
  330. continue; /* Skip deleted record */
  331. /*
  332. * Zero out the entire key buffer to allow for using
  333. * memcmp() as comparison function to compare whole keys.
  334. */
  335. memset(keybuf, 0, pkeydesc2->k2_len);
  336. /*
  337. * Extract key parts from record buffer.
  338. */
  339. _iskey_extract(pkeydesc2, record, keybuf);
  340. /*
  341. * Add recno to key
  342. */
  343. strecno(recnum, keybuf + KEY_RECNO_OFF);
  344. _issort_insert(srt, keybuf); /* Insert key into sorter */
  345. }
  346. }
  347. /*
  348. * _attach_dups_serial()
  349. *
  350. * Attach serial numbers to duplicate keys
  351. */
  352. Static void
  353. _attach_dups_serial(Issort *srt, Keydesc2 *pkeydesc2)
  354. {
  355. int netkeylength = pkeydesc2->k2_len - RECNOSIZE - DUPIDSIZE;
  356. char *curkey;
  357. char *lastkey = NULL;
  358. int dup_serial = 1;
  359. _issort_rewind(srt);
  360. while ((curkey = _issort_read(srt))) {
  361. if (lastkey && memcmp(lastkey + RECNOSIZE + DUPIDSIZE,
  362. curkey + RECNOSIZE + DUPIDSIZE,
  363. netkeylength) == 0)
  364. dup_serial++;
  365. else
  366. dup_serial = 1;
  367. /*
  368. * Store serial number in the key.
  369. */
  370. stdupser(dup_serial, curkey + KEY_DUPS_OFF);
  371. lastkey = curkey;
  372. }
  373. }
  374. /*
  375. * _buildbtree()
  376. *
  377. * Create B-tree.
  378. */
  379. Static Blkno
  380. _buildbtree(Fcb *fcb, Keydesc2 *pkeydesc2, Issort *srt)
  381. {
  382. Bufhdr *_allockpage();
  383. int depth;
  384. int nrecords = fcb->nrecords;
  385. int keyspernode[ISMAXBTRLEVEL];
  386. int one_more[ISMAXBTRLEVEL];
  387. char *nodebuf[ISMAXBTRLEVEL];
  388. Bufhdr *nodebufhd[ISMAXBTRLEVEL];
  389. Blkno pageblkno[ISMAXBTRLEVEL];
  390. int curindex[ISMAXBTRLEVEL];
  391. char *keyp;
  392. int keylength = pkeydesc2->k2_len;
  393. int leafcapac = getkeysperleaf(keylength);
  394. int nleafcapac = getkeyspernode(keylength);
  395. int perleaf;
  396. int blocks_in_this_level, blocks_in_prev_level;
  397. int slack = ISLEAFSLACK;
  398. int i;
  399. /*
  400. * Handle an empty B-tree as a special case.
  401. */
  402. if (fcb->nrecords == 0) {
  403. (void)_allockpage(fcb, leafcapac, 0, pageblkno + 0);
  404. return (pageblkno[0]);
  405. }
  406. /*
  407. * COMPRESS changes the fill factor to 100%
  408. */
  409. if ((pkeydesc2->k2_flags & COMPRESS) == COMPRESS)
  410. slack = 0;
  411. /*
  412. * Figure out fill factors for each level.
  413. */
  414. perleaf = ((double)leafcapac * (100.0 - (double) slack)) / 100.0;
  415. /*
  416. * Make it more robust.
  417. */
  418. if (perleaf >= leafcapac)
  419. perleaf = leafcapac;
  420. if (perleaf < leafcapac/2 + 1)
  421. perleaf = leafcapac/2 + 1;
  422. /*
  423. * Iterativelly determince values in keyspernode[] and one_mode[]
  424. */
  425. blocks_in_this_level = nrecords / perleaf;
  426. if (blocks_in_this_level * leafcapac < nrecords)
  427. blocks_in_this_level++;
  428. keyspernode[0] = nrecords / blocks_in_this_level;
  429. one_more[0] = nrecords % blocks_in_this_level;
  430. for (depth = 1; blocks_in_this_level > 1; depth++) {
  431. blocks_in_prev_level = blocks_in_this_level;
  432. blocks_in_this_level = (blocks_in_prev_level-1) / nleafcapac + 1;
  433. keyspernode[depth] = blocks_in_prev_level / blocks_in_this_level;
  434. one_more[depth] = blocks_in_prev_level % blocks_in_this_level;
  435. }
  436. if (depth >= ISMAXBTRLEVEL)
  437. _isfatal_error("Too many levels in B-tree");
  438. /*
  439. * Make sure that we start reading keys from the beginning.
  440. */
  441. _issort_rewind(srt);
  442. /*
  443. * Boot the Main loop.
  444. */
  445. for (i = 0; i < depth ; i++) {
  446. curindex[i] = ISPAGESIZE; /* Any big number will do */
  447. one_more[i]++;
  448. nodebuf[i] = NULL;
  449. nodebufhd[i] = NULL;
  450. }
  451. /*
  452. * Main loop.
  453. */
  454. while ((keyp = _issort_read(srt)) != (char *) NULL) {
  455. if (curindex[0] >= keyspernode[0] + (one_more[0] > 0)) {
  456. /*
  457. * Commit all changed buffers here to avoid buffer pool overflow.
  458. */
  459. if (nodebufhd[0]) {
  460. _isdisk_commit1(nodebufhd[0]);
  461. _isdisk_sync();
  462. }
  463. /* Allocate new leaf. */
  464. nodebufhd[0] = _allockpage(fcb, leafcapac, 0, pageblkno+0);
  465. nodebuf[0] = nodebufhd[0]->isb_buffer;
  466. one_more[0]--;
  467. curindex[0] = 0;
  468. }
  469. /* Copy key into the page. */
  470. memcpy( nodebuf[0] + BT_KEYS_OFF + keylength * curindex[0],keyp,
  471. keylength);
  472. stshort((short) curindex[0] + 1, nodebuf[0] + BT_NKEYS_OFF);
  473. if (curindex[0]++ == 0) { /* Store first key in */
  474. /* higher levels */
  475. for (i = 1;i < depth; i++) {
  476. if (curindex[i] >= keyspernode[i] + (one_more[i] > 0)) {
  477. /* Unfix buffer. */
  478. if (nodebufhd[i])
  479. _isdisk_commit1(nodebufhd[i]);
  480. nodebufhd[i] = _allockpage(fcb, nleafcapac, i, pageblkno+i);
  481. nodebuf[i] = nodebufhd[i]->isb_buffer;
  482. one_more[i]--;
  483. curindex[i] = 0;
  484. }
  485. /* Copy key into page. */
  486. memcpy( nodebuf[i] + BT_KEYS_OFF + keylength * curindex[i],keyp,
  487. keylength);
  488. /* Store pointer to level below. */
  489. stblkno(pageblkno[i-1], nodebuf[i] + ISPAGESIZE -
  490. (curindex[i]+1) * BLKNOSIZE);
  491. stshort((short) curindex[i] + 1, nodebuf[i] + BT_NKEYS_OFF);
  492. if (curindex[i]++ > 0) {
  493. break;
  494. }
  495. }
  496. }
  497. }
  498. return (pageblkno [depth - 1]); /* Return blkno of B-tree root */
  499. }
  500. /*
  501. * _duplicate_exist()
  502. *
  503. * Return 1 if there are duplicates in the sorted key object.
  504. */
  505. Static int _duplicate_exist(Issort *srt, int keylength)
  506. {
  507. char *curkey;
  508. char *lastkey = (char *) 0;
  509. int netkeylength = keylength - RECNOSIZE;
  510. _issort_rewind(srt);
  511. while ((curkey = _issort_read(srt))) {
  512. if (lastkey && memcmp(lastkey + RECNOSIZE, curkey + RECNOSIZE,
  513. netkeylength) == 0)
  514. return 1; /* Duplicate key found */
  515. lastkey = curkey;
  516. }
  517. return 0; /* No duplicates found */
  518. }
  519. static void
  520. checkavailfd(void)
  521. {
  522. Fcb *fcb;
  523. Bytearray *isfhandle2;
  524. /*
  525. * Check that there are UNIX file descriptors available.
  526. */
  527. while (_watchfd_check() < 1) {
  528. /*
  529. * Find victim (LRU FCB) and close it.
  530. */
  531. if((isfhandle2 = _mngfcb_victim()) == NULL)
  532. _isfatal_error ("_openfcb() cannot find LRU victim");
  533. fcb = _mngfcb_find(isfhandle2);
  534. (void) _watchfd_decr(_isfcb_nfds(fcb));
  535. _isfcb_close(fcb);
  536. _mngfcb_delete(isfhandle2);
  537. }
  538. }