sqliterk_btree.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. /*
  2. * Tencent is pleased to support the open source community by making
  3. * WCDB available.
  4. *
  5. * Copyright (C) 2017 THL A29 Limited, a Tencent company.
  6. * All rights reserved.
  7. *
  8. * Licensed under the BSD 3-Clause License (the "License"); you may not use
  9. * this file except in compliance with the License. You may obtain a copy of
  10. * the License at
  11. *
  12. * https://opensource.org/licenses/BSD-3-Clause
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. #include "sqliterk_btree.h"
  21. #include "SQLiteRepairKit.h"
  22. #include "sqliterk_column.h"
  23. #include "sqliterk_os.h"
  24. #include "sqliterk_pager.h"
  25. #include "sqliterk_util.h"
  26. #include "sqliterk_values.h"
  27. #include <string.h>
  28. // Declarations
  29. static int sqliterkBtreeParsePage(sqliterk_btree *btree, int pageno);
  30. static int sqliterkBtreeParseCell(sqliterk_btree *btree,
  31. sqliterk_page *page,
  32. const int *cellPointerArray,
  33. const int cellsCount);
  34. static int sqliterkBtreeParsePayload(sqliterk_btree *btree,
  35. sqliterk_page *page,
  36. int offset,
  37. int payloadSize,
  38. sqliterk_column *column);
  39. static int sqliterkBtreeGetLengthForSerialType(int serialType);
  40. struct sqliterk_btree {
  41. sqliterk *rk;
  42. char *name;
  43. sqliterk_btree_type type;
  44. sqliterk_pager *pager;
  45. sqliterk_page *rootpage;
  46. // For leaf-table. See https://www.sqlite.org/fileformat2.html#btree
  47. int maxLocal;
  48. int minLocal;
  49. int maxLeaf;
  50. int minLeaf;
  51. sqliterk_btree_notify notify;
  52. void *userInfo;
  53. };
  54. int sqliterkBtreeOpen(sqliterk *rk,
  55. sqliterk_pager *pager,
  56. int rootPageno,
  57. sqliterk_btree **btree)
  58. {
  59. if (!pager || !btree) {
  60. return SQLITERK_MISUSE;
  61. }
  62. int rc = SQLITERK_OK;
  63. sqliterk_btree *theBtree = sqliterkOSMalloc(sizeof(sqliterk_btree));
  64. if (!theBtree) {
  65. rc = SQLITERK_NOMEM;
  66. goto sqliterkBtreeOpen_Failed;
  67. }
  68. theBtree->pager = pager;
  69. rc = sqliterkPageAcquire(theBtree->pager, rootPageno, &theBtree->rootpage);
  70. if (rc != SQLITERK_OK) {
  71. goto sqliterkBtreeOpen_Failed;
  72. }
  73. if (rootPageno == 1) {
  74. rc = sqliterkBtreeSetMeta(theBtree, "sqlite_master",
  75. sqliterk_btree_type_master);
  76. if (rc != SQLITERK_OK) {
  77. goto sqliterkBtreeOpen_Failed;
  78. }
  79. } else {
  80. switch (sqliterkPageGetType(theBtree->rootpage)) {
  81. case sqliterk_page_type_interior_index:
  82. case sqliterk_page_type_leaf_index:
  83. theBtree->type = sqliterk_btree_type_index;
  84. break;
  85. case sqliterk_page_type_interior_table:
  86. case sqliterk_page_type_leaf_table:
  87. theBtree->type = sqliterk_btree_type_table;
  88. break;
  89. default:
  90. rc = SQLITERK_DAMAGED;
  91. goto sqliterkBtreeOpen_Failed;
  92. }
  93. }
  94. // Save memory
  95. sqliterkPageClearData(theBtree->rootpage);
  96. theBtree->maxLocal =
  97. (sqliterkPagerGetUsableSize(theBtree->pager) - 12) * 64 / 255 - 23;
  98. theBtree->minLocal =
  99. (sqliterkPagerGetUsableSize(theBtree->pager) - 12) * 32 / 255 - 23;
  100. theBtree->maxLeaf = sqliterkPagerGetUsableSize(theBtree->pager) - 35;
  101. theBtree->minLeaf =
  102. (sqliterkPagerGetUsableSize(theBtree->pager) - 12) * 32 / 255 - 23;
  103. theBtree->rk = rk;
  104. *btree = theBtree;
  105. return SQLITERK_OK;
  106. sqliterkBtreeOpen_Failed:
  107. if (theBtree) {
  108. sqliterkBtreeClose(theBtree);
  109. }
  110. *btree = NULL;
  111. return rc;
  112. }
  113. int sqliterkBtreeParse(sqliterk_btree *btree)
  114. {
  115. if (!btree) {
  116. return SQLITERK_MISUSE;
  117. }
  118. if (btree->notify.onBeginParseBtree) {
  119. btree->notify.onBeginParseBtree(btree->rk, btree);
  120. }
  121. int rc =
  122. sqliterkBtreeParsePage(btree, sqliterkPageGetPageno(btree->rootpage));
  123. if (btree->notify.onEndParseBtree) {
  124. btree->notify.onEndParseBtree(btree->rk, btree, rc);
  125. }
  126. return rc;
  127. }
  128. // If the page is an interior-btree, no matter is an interior-table btree
  129. // or an interior-index btree, this function will recursively parse the page
  130. // until it find the leaf page or any error occur.
  131. // A leaf-index btree will only be found but not parse, since its data make
  132. // no sense.
  133. static int sqliterkBtreeParsePage(sqliterk_btree *btree, int pageno)
  134. {
  135. int i;
  136. if (!btree || pageno > sqliterkPagerGetPageCount(btree->pager)) {
  137. return SQLITERK_MISUSE;
  138. }
  139. int rc;
  140. if (btree->notify.onBeginParsePage) {
  141. rc = btree->notify.onBeginParsePage(btree->rk, btree, pageno);
  142. if (rc != SQLITERK_OK) {
  143. return rc;
  144. }
  145. }
  146. int *cellPointerArray = NULL;
  147. sqliterk_page *page = NULL;
  148. // ahead checking type to fast up parsing
  149. rc = sqliterkPageAcquire(btree->pager, pageno, &page);
  150. if (rc != SQLITERK_OK) {
  151. goto sqliterkBtreeParsePage_End;
  152. }
  153. sqliterk_page_type type = sqliterkPageGetType(page);
  154. if (type != sqliterk_page_type_interior_index &&
  155. type != sqliterk_page_type_interior_table &&
  156. type != sqliterk_page_type_leaf_index &&
  157. type != sqliterk_page_type_leaf_table) {
  158. rc = sqliterkOSWarning(SQLITERK_DAMAGED, "Page %d has invalid type",
  159. pageno);
  160. goto sqliterkBtreeParsePage_End;
  161. }
  162. //sqliterkOSDebug(SQLITERK_OK, "Page %d is %s", pageno, sqliterkPageGetTypeName(type));
  163. // Parse cell pointer array. For further information, see [cell pointer]
  164. // at https://www.sqlite.org/fileformat2.html#btree
  165. const unsigned char *pagedata = sqliterkPageGetData(page);
  166. int offsetCellPointerArray =
  167. (type == sqliterk_page_type_interior_table) ? 12 : 8;
  168. int cellsCount;
  169. sqliterkParseInt(pagedata, 3 + sqliterkPageHeaderOffset(page), 2,
  170. &cellsCount);
  171. if (cellsCount <= 0 || cellsCount * 2 + offsetCellPointerArray >
  172. sqliterkPagerGetSize(btree->pager)) {
  173. rc = SQLITERK_DAMAGED;
  174. goto sqliterkBtreeParsePage_End;
  175. }
  176. cellPointerArray = sqliterkOSMalloc(sizeof(int) * (cellsCount + 1));
  177. if (!cellPointerArray) {
  178. rc = SQLITERK_NOMEM;
  179. goto sqliterkBtreeParsePage_End;
  180. }
  181. for (i = 0; i < cellsCount; i++) {
  182. int cellPointer;
  183. sqliterkParseInt(pagedata,
  184. sqliterkPageHeaderOffset(page) +
  185. offsetCellPointerArray + i * 2,
  186. 2, &cellPointer);
  187. cellPointerArray[i] = cellPointer;
  188. }
  189. switch (type) {
  190. case sqliterk_page_type_interior_table:
  191. case sqliterk_page_type_interior_index: {
  192. int hasRightMostPageno =
  193. (type == sqliterk_page_type_interior_table);
  194. int pagenosCount = cellsCount + hasRightMostPageno;
  195. int *pagenos = sqliterkOSMalloc(sizeof(int) * (pagenosCount + 1));
  196. if (!pagenos) {
  197. rc = SQLITERK_NOMEM;
  198. goto sqliterkBtreeParsePage_End;
  199. }
  200. for (i = 0; i < cellsCount; i++) {
  201. sqliterkParseInt(pagedata, cellPointerArray[i], 4, pagenos + i);
  202. }
  203. if (hasRightMostPageno) {
  204. sqliterkParseInt(pagedata, 8, 4, pagenos + cellsCount);
  205. }
  206. // All done for page data. Ahead release the page data to avoid memory overflow
  207. sqliterkOSFree(cellPointerArray);
  208. cellPointerArray = NULL;
  209. sqliterkPageClearData(page);
  210. // Recursively decode the page
  211. for (i = 0; i < pagenosCount; i++) {
  212. if (sqliterkBtreeParsePage(btree, pagenos[i]) ==
  213. SQLITERK_CANCELLED)
  214. break;
  215. }
  216. sqliterkOSFree(pagenos);
  217. break;
  218. }
  219. case sqliterk_page_type_leaf_table:
  220. if (sqliterkBtreeIsSystemType(sqliterkBtreeGetType(btree)) &&
  221. btree->type != sqliterk_btree_type_master) {
  222. //skip a non-master system table, since its column is generated.
  223. goto sqliterkBtreeParsePage_End;
  224. }
  225. rc = sqliterkBtreeParseCell(btree, page, cellPointerArray,
  226. cellsCount);
  227. break;
  228. case sqliterk_page_type_leaf_index:
  229. // Just skip it since the column in leaf index make no sense.
  230. break;
  231. default:
  232. break;
  233. }
  234. sqliterkBtreeParsePage_End:
  235. if (cellPointerArray) {
  236. sqliterkOSFree(cellPointerArray);
  237. }
  238. if (btree->notify.onEndParsePage) {
  239. btree->notify.onEndParsePage(btree->rk, btree, pageno, rc);
  240. }
  241. if (page) {
  242. sqliterkPageRelease(page);
  243. }
  244. if (rc != SQLITERK_OK && rc != SQLITERK_CANCELLED) {
  245. sqliterkOSDebug(rc, "Failed to parse page %d.", pageno);
  246. }
  247. return rc;
  248. }
  249. // Parse the payload data. see [B-tree Cell Format]
  250. // at https://www.sqlite.org/fileformat2.html#btree
  251. static int sqliterkBtreeParseCell(sqliterk_btree *btree,
  252. sqliterk_page *page,
  253. const int *cellPointerArray,
  254. const int cellsCount)
  255. {
  256. if (!btree || !page || !cellPointerArray || cellsCount < 0) {
  257. return SQLITERK_MISUSE;
  258. }
  259. const unsigned char *pagedata = sqliterkPageGetData(page);
  260. int rc = SQLITERK_OK;
  261. sqliterk_column *column;
  262. rc = sqliterkColumnAlloc(&column);
  263. if (rc != SQLITERK_OK) {
  264. goto sqliterkBtreeParsePayload_End;
  265. }
  266. int i;
  267. for (i = 0; i < cellsCount; i++) {
  268. sqliterkColumnClear(column);
  269. int offset = cellPointerArray[i];
  270. // Find payload
  271. int payloadSizeLength;
  272. int payloadSize;
  273. rc = sqliterkParseVarint(pagedata, offset, &payloadSizeLength,
  274. &payloadSize);
  275. if (rc != SQLITERK_OK) {
  276. goto sqliterkBtreeParsePayload_End;
  277. }
  278. offset += payloadSizeLength;
  279. int rowidLength;
  280. int64_t rowid;
  281. rc = sqliterkParseVarint64(pagedata, offset, &rowidLength, &rowid);
  282. if (rc != SQLITERK_OK) {
  283. goto sqliterkBtreeParsePayload_End;
  284. }
  285. offset += rowidLength;
  286. sqliterkColumnSetRowId(column, rowid);
  287. rc =
  288. sqliterkBtreeParsePayload(btree, page, offset, payloadSize, column);
  289. if (rc != SQLITERK_OK) {
  290. goto sqliterkBtreeParsePayload_End;
  291. }
  292. }
  293. sqliterkBtreeParsePayload_End:
  294. if (column) {
  295. sqliterkColumnFree(column);
  296. }
  297. if (rc != SQLITERK_OK && rc != SQLITERK_CANCELLED) {
  298. sqliterkOSDebug(rc, "Failed to parse payload.");
  299. }
  300. return rc;
  301. }
  302. // Parse the payload for leaf-table page only. We don't implement the parse
  303. // method for index page, since we are not concerned about the data in an
  304. // index page. See [Record Format] at https://www.sqlite.org/fileformat2.html
  305. static int sqliterkBtreeParsePayload(sqliterk_btree *btree,
  306. sqliterk_page *page,
  307. int offset,
  308. int payloadSize,
  309. sqliterk_column *column)
  310. {
  311. if (!btree || payloadSize <= 0 || !column) {
  312. return SQLITERK_MISUSE;
  313. }
  314. int rc = SQLITERK_OK;
  315. unsigned char *payloadData = sqliterkOSMalloc(payloadSize);
  316. if (!payloadData) {
  317. rc = SQLITERK_NOMEM;
  318. goto sqliterkBtreeParseColumn_End;
  319. }
  320. // Check overflow
  321. int local = 0;
  322. if (payloadSize <= btree->maxLeaf) {
  323. local = payloadSize;
  324. } else {
  325. // Since it is a leaf-table page, the max local should be equal to max leaf
  326. int maxPageLocal = btree->maxLeaf;
  327. int minPageLocal = btree->minLocal;
  328. int surplus =
  329. minPageLocal + (payloadSize - minPageLocal) %
  330. (sqliterkPagerGetUsableSize(btree->pager) - 4);
  331. if (surplus <= maxPageLocal) {
  332. local = surplus;
  333. } else {
  334. local = minPageLocal;
  335. }
  336. }
  337. // Read data
  338. int payloadPointer = 0;
  339. const unsigned char *pagedata = sqliterkPageGetData(page);
  340. if (offset + local > sqliterkPagerGetSize(btree->pager)) {
  341. rc = SQLITERK_DAMAGED;
  342. goto sqliterkBtreeParseColumn_End;
  343. }
  344. memcpy(payloadData, pagedata + offset, local);
  345. payloadPointer += local;
  346. if (payloadPointer < payloadSize) {
  347. sqliterk_values *overflowPages = sqliterkColumnGetOverflowPages(column);
  348. int overflowPageno;
  349. const unsigned char *pagedata = sqliterkPageGetData(page);
  350. sqliterkParseInt(pagedata, offset + local, 4, &overflowPageno);
  351. while (sqliterkPagerIsPagenoValid(btree->pager, overflowPageno) ==
  352. SQLITERK_OK) {
  353. sqliterkValuesAddInteger(overflowPages, overflowPageno);
  354. if (btree->notify.onBeginParsePage) {
  355. btree->notify.onBeginParsePage(btree->rk, btree,
  356. overflowPageno);
  357. }
  358. sqliterk_page *page;
  359. rc = sqliterkPageAcquireOverflow(btree->pager, overflowPageno,
  360. &page);
  361. if (btree->notify.onEndParsePage) {
  362. btree->notify.onEndParsePage(btree->rk, btree, overflowPageno,
  363. rc);
  364. }
  365. if (rc != SQLITERK_OK) {
  366. break;
  367. }
  368. // Read data
  369. int overflowSize = payloadSize - payloadPointer;
  370. int maxSize = sqliterkPagerGetUsableSize(btree->pager) - 4;
  371. if (overflowSize > maxSize) {
  372. overflowSize = maxSize;
  373. }
  374. const unsigned char *pageData = sqliterkPageGetData(page);
  375. memcpy(payloadData + payloadPointer, pageData + 4, overflowSize);
  376. payloadPointer += overflowSize;
  377. // Iterate
  378. sqliterkParseInt(pageData, 0, 4, &overflowPageno);
  379. // Clear
  380. sqliterkPageRelease(page);
  381. }
  382. }
  383. int columnOffsetValue = 0;
  384. int columnOffsetValueLength = 0;
  385. rc = sqliterkParseVarint(payloadData, 0, &columnOffsetValueLength,
  386. &columnOffsetValue);
  387. if (rc != SQLITERK_OK) {
  388. goto sqliterkBtreeParseColumn_End;
  389. }
  390. int offsetSerialType = columnOffsetValueLength;
  391. int offsetValue = columnOffsetValue;
  392. const int endSerialType = offsetValue;
  393. const int endValue = payloadSize;
  394. int serialTypeLength = 0;
  395. int serialType = 0;
  396. int valueLength = 0;
  397. sqliterk_values *values = sqliterkColumnGetValues(column);
  398. while (offsetValue < endValue || offsetSerialType < endSerialType) {
  399. rc = sqliterkParseVarint(payloadData, offsetSerialType,
  400. &serialTypeLength, &serialType);
  401. if (rc != SQLITERK_OK) {
  402. goto sqliterkBtreeParseColumn_End;
  403. }
  404. valueLength = sqliterkBtreeGetLengthForSerialType(serialType);
  405. if (serialType == 0) {
  406. rc = sqliterkValuesAddNull(values);
  407. } else if (serialType < 7) {
  408. int64_t value;
  409. sqliterkParseInt64(payloadData, offsetValue, valueLength, &value);
  410. rc = sqliterkValuesAddInteger64(values, value);
  411. } else if (serialType == 7) {
  412. double value;
  413. sqliterkParseNumber(payloadData, offsetValue, &value);
  414. rc = sqliterkValuesAddNumber(values, value);
  415. } else if (serialType == 8) {
  416. rc = sqliterkValuesAddInteger(values, 0);
  417. } else if (serialType == 9) {
  418. rc = sqliterkValuesAddInteger(values, 1);
  419. } else if (serialType >= 12) {
  420. if (serialType % 2 == 0) {
  421. rc = sqliterkValuesAddBinary(values, payloadData + offsetValue,
  422. valueLength);
  423. } else {
  424. rc = sqliterkValuesAddNoTerminatorText(
  425. values, (const char *) payloadData + offsetValue,
  426. valueLength);
  427. }
  428. } else {
  429. rc = SQLITERK_DAMAGED;
  430. }
  431. if (rc != SQLITERK_OK) {
  432. goto sqliterkBtreeParseColumn_End;
  433. }
  434. offsetValue += valueLength;
  435. offsetSerialType += serialTypeLength;
  436. }
  437. if (offsetSerialType != endSerialType || offsetValue != endValue) {
  438. rc = SQLITERK_DAMAGED;
  439. goto sqliterkBtreeParseColumn_End;
  440. }
  441. sqliterkBtreeParseColumn_End:
  442. if (rc == SQLITERK_OK && btree->notify.onParseColumn) {
  443. rc = btree->notify.onParseColumn(btree->rk, btree, page, column);
  444. }
  445. if (payloadData) {
  446. sqliterkOSFree(payloadData);
  447. }
  448. return rc;
  449. }
  450. int sqliterkBtreeClose(sqliterk_btree *btree)
  451. {
  452. if (!btree) {
  453. return SQLITERK_MISUSE;
  454. }
  455. if (btree->name) {
  456. sqliterkOSFree(btree->name);
  457. btree->name = NULL;
  458. }
  459. if (btree->rootpage) {
  460. sqliterkPageRelease(btree->rootpage);
  461. btree->rootpage = NULL;
  462. }
  463. btree->pager = NULL;
  464. btree->userInfo = NULL;
  465. btree->rk = NULL;
  466. btree->type = 0;
  467. sqliterkOSFree(btree);
  468. return SQLITERK_OK;
  469. }
  470. int sqliterkBtreeSetMeta(sqliterk_btree *btree,
  471. const char *name,
  472. sqliterk_btree_type type)
  473. {
  474. if (!btree) {
  475. return SQLITERK_MISUSE;
  476. }
  477. if (btree->name) {
  478. sqliterkOSFree(btree->name);
  479. btree->name = NULL;
  480. }
  481. if (name) {
  482. size_t length = strlen(name);
  483. btree->name = sqliterkOSMalloc(sizeof(char) * (length + 1));
  484. if (!btree->name) {
  485. return SQLITERK_NOMEM;
  486. }
  487. strncpy(btree->name, name, length);
  488. // If it's a system btree name, then setup its b-tree type.
  489. sqliterk_btree_type i;
  490. for (i = sqliterk_btree_type_system_begin;
  491. i < sqliterk_btree_type_system_end; i++) {
  492. const char *typename = sqliterkBtreeGetTypeName(i);
  493. if (strncmp(btree->name, typename, strlen(typename)) == 0) {
  494. btree->type = i;
  495. break;
  496. }
  497. }
  498. } else {
  499. btree->name = NULL;
  500. }
  501. if (!sqliterkBtreeIsSystemType(btree->type) &&
  502. type != sqliterk_btree_type_unknown) {
  503. btree->type = type;
  504. }
  505. return SQLITERK_OK;
  506. }
  507. const char *sqliterkBtreeGetName(sqliterk_btree *btree)
  508. {
  509. if (!btree) {
  510. return NULL;
  511. }
  512. return btree->name;
  513. }
  514. sqliterk_btree_type sqliterkBtreeGetType(sqliterk_btree *btree)
  515. {
  516. if (!btree) {
  517. return sqliterk_btree_type_unknown;
  518. }
  519. return btree->type;
  520. }
  521. int sqliterkBtreeSetType(sqliterk_btree *btree, sqliterk_btree_type type)
  522. {
  523. if (!btree) {
  524. return SQLITERK_MISUSE;
  525. }
  526. if (sqliterkBtreeIsSystemType(btree->type)) {
  527. // You can only set the type manually when the type is not a system type
  528. return SQLITERK_MISUSE;
  529. }
  530. btree->type = type;
  531. return SQLITERK_OK;
  532. }
  533. int sqliterkBtreeIsSystemType(sqliterk_btree_type type)
  534. {
  535. if (type >= sqliterk_btree_type_system_begin &&
  536. type < sqliterk_btree_type_system_end) {
  537. return 1;
  538. }
  539. return 0;
  540. }
  541. void sqliterkBtreeSetNotify(sqliterk_btree *btree,
  542. sqliterk_btree_notify *notify)
  543. {
  544. if (!btree || !notify) {
  545. return;
  546. }
  547. btree->notify = *notify;
  548. }
  549. void sqliterkBtreeSetUserInfo(sqliterk_btree *btree, void *userInfo)
  550. {
  551. if (!btree) {
  552. return;
  553. }
  554. btree->userInfo = userInfo;
  555. }
  556. void *sqliterkBtreeGetUserInfo(sqliterk_btree *btree)
  557. {
  558. if (!btree) {
  559. return NULL;
  560. }
  561. return btree->userInfo;
  562. }
  563. sqliterk_page *sqliterkBtreeGetRootPage(sqliterk_btree *btree)
  564. {
  565. if (!btree) {
  566. return NULL;
  567. }
  568. return btree->rootpage;
  569. }
  570. const char *sqliterkBtreeGetTypeName(sqliterk_btree_type type)
  571. {
  572. char *name;
  573. switch (type) {
  574. case sqliterk_btree_type_autoindex:
  575. name = "sqlite_autoindex";
  576. break;
  577. case sqliterk_btree_type_sequence:
  578. name = "sqlite_sequence";
  579. break;
  580. case sqliterk_btree_type_stat:
  581. name = "sqlite_stat";
  582. break;
  583. case sqliterk_btree_type_master:
  584. name = "sqlite_master";
  585. break;
  586. case sqliterk_btree_type_table:
  587. name = "table";
  588. break;
  589. case sqliterk_btree_type_index:
  590. name = "index";
  591. break;
  592. default:
  593. name = "unknown";
  594. break;
  595. }
  596. return name;
  597. }
  598. // See [Serial Type Codes Of The Record Format]
  599. // at https://www.sqlite.org/fileformat2.html
  600. static int sqliterkBtreeGetLengthForSerialType(int serialType)
  601. {
  602. if (serialType < 0) {
  603. return 0;
  604. }
  605. static int sqliterk_btree_serialtype_fixlengths[12] = {0, 1, 2, 3, 4, 6,
  606. 8, 8, 0, 0, 0, 0};
  607. if (serialType < 12) {
  608. return sqliterk_btree_serialtype_fixlengths[serialType];
  609. }
  610. return (serialType - 12 - serialType % 2) / 2;
  611. }