|
| 1 | +#ifndef SIARA_SQLITE_COMMON |
| 2 | +#define SIARA_SQLITE_COMMON |
| 3 | + |
| 4 | +#include <string> |
| 5 | +#include <stdlib.h> |
| 6 | +#include <stdint.h> |
| 7 | + |
| 8 | +#include "util.h" |
| 9 | + |
| 10 | +const int8_t col_data_lens[] = {0, 1, 2, 3, 4, 6, 8, 8, 0, 0}; |
| 11 | + |
| 12 | +enum {SQLT_TYPE_NULL = 0, SQLT_TYPE_INT8, SQLT_TYPE_INT16, SQLT_TYPE_INT24, SQLT_TYPE_INT32, SQLT_TYPE_INT48, SQLT_TYPE_INT64, |
| 13 | + SQLT_TYPE_REAL, SQLT_TYPE_INT0, SQLT_TYPE_INT1, SQLT_TYPE_BLOB = 12, SQLT_TYPE_TEXT = 13}; |
| 14 | + |
| 15 | +enum {SQLT_RES_OK = 0, SQLT_RES_ERR = -1, SQLT_RES_INV_PAGE_SZ = -2, |
| 16 | + SQLT_RES_TOO_LONG = -3, SQLT_RES_WRITE_ERR = -4, SQLT_RES_FLUSH_ERR = -5}; |
| 17 | + |
| 18 | +enum {SQLT_RES_SEEK_ERR = -6, SQLT_RES_READ_ERR = -7, |
| 19 | + SQLT_RES_INVALID_SIG = -8, SQLT_RES_MALFORMED = -9, |
| 20 | + SQLT_RES_NOT_FOUND = -10, SQLT_RES_NOT_FINALIZED = -11, |
| 21 | + SQLT_RES_TYPE_MISMATCH = -12, SQLT_RES_INV_CHKSUM = -13, |
| 22 | + SQLT_RES_NEED_1_PK = -14, SQLT_RES_NO_SPACE = -15}; |
| 23 | + |
| 24 | +class sqlite_common { |
| 25 | + |
| 26 | + public: |
| 27 | + // Writes given value at given pointer in Sqlite format |
| 28 | + static uint16_t write_data(uint8_t *data_ptr, int type, const void *val, uint16_t len) { |
| 29 | + if (val == NULL || type == SQLT_TYPE_NULL |
| 30 | + || type == SQLT_TYPE_INT0 || type == SQLT_TYPE_INT1) |
| 31 | + return 0; |
| 32 | + if (type >= SQLT_TYPE_INT8 && type <= SQLT_TYPE_INT64) { |
| 33 | + switch (type) { |
| 34 | + case SQLT_TYPE_INT8: |
| 35 | + util::write_uint8(data_ptr, *((int8_t *) val)); |
| 36 | + break; |
| 37 | + case SQLT_TYPE_INT16: |
| 38 | + util::write_uint16(data_ptr, *((int16_t *) val)); |
| 39 | + break; |
| 40 | + case SQLT_TYPE_INT24: |
| 41 | + util::write_int24(data_ptr, *((int32_t *) val)); |
| 42 | + break; |
| 43 | + case SQLT_TYPE_INT32: |
| 44 | + util::write_uint32(data_ptr, *((int32_t *) val)); |
| 45 | + break; |
| 46 | + case SQLT_TYPE_INT48: |
| 47 | + util::write_int48(data_ptr, *((int64_t *) val)); |
| 48 | + break; |
| 49 | + case SQLT_TYPE_INT64: |
| 50 | + util::write_uint64(data_ptr, *((int64_t *) val)); |
| 51 | + break; |
| 52 | + } |
| 53 | + } else |
| 54 | + if (type == SQLT_TYPE_REAL && len == 4) { |
| 55 | + // Assumes float is represented in IEEE-754 format |
| 56 | + uint64_t bytes64 = util::float_to_double(val); |
| 57 | + util::write_uint64(data_ptr, bytes64); |
| 58 | + len = 8; |
| 59 | + } else |
| 60 | + if (type == SQLT_TYPE_REAL && len == 8) { |
| 61 | + // TODO: Assumes double is represented in IEEE-754 format |
| 62 | + uint64_t bytes = *((uint64_t *) val); |
| 63 | + util::write_uint64(data_ptr, bytes); |
| 64 | + } else |
| 65 | + memcpy(data_ptr, val, len); |
| 66 | + return len; |
| 67 | + } |
| 68 | + |
| 69 | + // Initializes the buffer as a B-Tree Leaf Index |
| 70 | + static void init_bt_idx_interior(uint8_t *ptr, int block_size, int resv_bytes) { |
| 71 | + ptr[0] = 2; // Interior index b-tree page |
| 72 | + util::write_uint16(ptr + 1, 0); // No freeblocks |
| 73 | + util::write_uint16(ptr + 3, 0); // No records yet |
| 74 | + util::write_uint16(ptr + 5, (block_size - resv_bytes == 65536 ? 0 : block_size - resv_bytes)); // No records yet |
| 75 | + util::write_uint8(ptr + 7, 0); // Fragmented free bytes |
| 76 | + util::write_uint32(ptr + 8, 0); // right-most pointer |
| 77 | + } |
| 78 | + |
| 79 | + // Initializes the buffer as a B-Tree Leaf Index |
| 80 | + static void init_bt_idx_leaf(uint8_t *ptr, int block_size, int resv_bytes) { |
| 81 | + ptr[0] = 10; // Leaf index b-tree page |
| 82 | + util::write_uint16(ptr + 1, 0); // No freeblocks |
| 83 | + util::write_uint16(ptr + 3, 0); // No records yet |
| 84 | + util::write_uint16(ptr + 5, (block_size - resv_bytes == 65536 ? 0 : block_size - resv_bytes)); // No records yet |
| 85 | + util::write_uint8(ptr + 7, 0); // Fragmented free bytes |
| 86 | + } |
| 87 | + |
| 88 | + // Initializes the buffer as a B-Tree Leaf Table |
| 89 | + static void init_bt_tbl_leaf(uint8_t *ptr, int block_size, int resv_bytes) { |
| 90 | + ptr[0] = 13; // Leaf Table b-tree page |
| 91 | + util::write_uint16(ptr + 1, 0); // No freeblocks |
| 92 | + util::write_uint16(ptr + 3, 0); // No records yet |
| 93 | + util::write_uint16(ptr + 5, (block_size - resv_bytes == 65536 ? 0 : block_size - resv_bytes)); // No records yet |
| 94 | + util::write_uint8(ptr + 7, 0); // Fragmented free bytes |
| 95 | + } |
| 96 | + |
| 97 | + static int get_offset(uint8_t block[]) { |
| 98 | + return (block[0] == 2 || block[0] == 5 || block[0] == 10 || block[0] == 13 ? 0 : 100); |
| 99 | + } |
| 100 | + |
| 101 | + static int get_data_len(int i, const uint8_t types[], const void *values[]) { |
| 102 | + if (types == NULL || types[i] >= 10) |
| 103 | + return strlen((const char *) values[i]); |
| 104 | + return col_data_lens[types[i]]; |
| 105 | + } |
| 106 | + |
| 107 | + static int write_new_rec(uint8_t block[], int pos, int64_t rowid, int col_count, const void *values[], |
| 108 | + const size_t value_lens[] = NULL, const uint8_t types[] = NULL, uint8_t *ptr = NULL) { |
| 109 | + |
| 110 | + int data_len = 0; |
| 111 | + for (int i = 0; i < col_count; i++) |
| 112 | + data_len += (value_lens == NULL ? get_data_len(i, types, values) : value_lens[i]); |
| 113 | + int hdr_len = 0; |
| 114 | + for (int i = 0; i < col_count; i++) { |
| 115 | + int val_len_hdr_len = (value_lens == NULL ? get_data_len(i, types, values) : value_lens[i]); |
| 116 | + if (types == NULL || types[i] == SQLT_TYPE_TEXT || types[i] == SQLT_TYPE_BLOB) { |
| 117 | + val_len_hdr_len = val_len_hdr_len * 2 + (types == NULL ? 13 : types[i]); |
| 118 | + } |
| 119 | + hdr_len += util::get_vlen_of_uint16(val_len_hdr_len); |
| 120 | + } |
| 121 | + int offset = get_offset(block); |
| 122 | + int hdr_len_vlen = util::get_vlen_of_uint32(hdr_len); |
| 123 | + hdr_len += hdr_len_vlen; |
| 124 | + int rowid_len = 0; |
| 125 | + if (block[offset] == 10 || block[offset] == 13) |
| 126 | + rowid_len = util::get_vlen_of_uint64(rowid); |
| 127 | + int rec_len = hdr_len + data_len; |
| 128 | + int rec_len_vlen = util::get_vlen_of_uint32(rec_len); |
| 129 | + |
| 130 | + int last_pos = 0; |
| 131 | + bool is_ptr_given = true; |
| 132 | + int blk_hdr_len = (block[offset] == 10 || block[offset] == 13 ? 8 : 12); |
| 133 | + if (ptr == NULL) { |
| 134 | + is_ptr_given = false; |
| 135 | + last_pos = util::read_uint16(block + offset + 5); |
| 136 | + if (last_pos == 0) |
| 137 | + last_pos = 65536; |
| 138 | + int ptr_len = util::read_uint16(block + offset + 3) << 1; |
| 139 | + if (offset + blk_hdr_len + ptr_len + rec_len + rec_len_vlen >= last_pos) |
| 140 | + return SQLT_RES_NO_SPACE; |
| 141 | + last_pos -= rec_len; |
| 142 | + last_pos -= rec_len_vlen; |
| 143 | + last_pos -= rowid_len; |
| 144 | + ptr = block + last_pos; |
| 145 | + } |
| 146 | + |
| 147 | + if (!is_ptr_given) { |
| 148 | + ptr += util::write_vint32(ptr, rec_len); |
| 149 | + if (block[offset] == 10 || block[offset] == 13) |
| 150 | + ptr += util::write_vint64(ptr, rowid); |
| 151 | + } |
| 152 | + ptr += util::write_vint32(ptr, hdr_len); |
| 153 | + for (int i = 0; i < col_count; i++) { |
| 154 | + uint8_t type = (types == NULL ? SQLT_TYPE_TEXT : types[i]); |
| 155 | + int value_len = (value_lens == NULL ? get_data_len(i, types, values) : value_lens[i]); |
| 156 | + int col_len_in_hdr = (type == SQLT_TYPE_TEXT || type == SQLT_TYPE_BLOB) |
| 157 | + ? value_len * 2 + type : type; |
| 158 | + ptr += util::write_vint32(ptr, col_len_in_hdr); |
| 159 | + } |
| 160 | + for (int i = 0; i < col_count; i++) { |
| 161 | + if (value_lens == NULL || value_lens[i] > 0) { |
| 162 | + ptr += write_data(ptr, types == NULL ? SQLT_TYPE_TEXT : types[i], |
| 163 | + values[i], value_lens == NULL ? get_data_len(i, types, values) : value_lens[i]); |
| 164 | + } |
| 165 | + } |
| 166 | + |
| 167 | + // if pos given, update record count, last data pos and insert record |
| 168 | + if (last_pos > 0 && pos >= 0) { |
| 169 | + int rec_count = util::read_uint16(block + offset + 3); |
| 170 | + util::write_uint16(block + offset + 3, rec_count + 1); |
| 171 | + util::write_uint16(block + offset + 5, last_pos); |
| 172 | + uint8_t *ins_ptr = block + offset + blk_hdr_len + pos * 2; |
| 173 | + memmove(ins_ptr + 2, ins_ptr, (rec_count - pos) * 2); |
| 174 | + util::write_uint16(ins_ptr, last_pos); |
| 175 | + } |
| 176 | + |
| 177 | + return is_ptr_given ? rec_len : last_pos; |
| 178 | + |
| 179 | + } |
| 180 | + |
| 181 | + // Writes data into buffer to form first page of Sqlite db |
| 182 | + static void fill_page0(uint8_t master_block[], int total_col_count, int pk_col_count, int block_size, |
| 183 | + int page_resv_bytes, const std::string& col_names, const std::string& table_name) { |
| 184 | + |
| 185 | + if (block_size % 512 || block_size < 512 || block_size > 65536) |
| 186 | + throw SQLT_RES_INV_PAGE_SZ; |
| 187 | + |
| 188 | + // 100 uint8_t header - refer https://www.sqlite.org/fileformat.html |
| 189 | + memcpy(master_block, "SQLite format 3\0", 16); |
| 190 | + util::write_uint16(master_block + 16, block_size == 65536 ? 1 : (uint16_t) block_size); |
| 191 | + master_block[18] = 1; |
| 192 | + master_block[19] = 1; |
| 193 | + master_block[20] = page_resv_bytes; |
| 194 | + master_block[21] = 64; |
| 195 | + master_block[22] = 32; |
| 196 | + master_block[23] = 32; |
| 197 | + //write_uint32(master_block + 24, 0); |
| 198 | + //write_uint32(master_block + 28, 0); |
| 199 | + //write_uint32(master_block + 32, 0); |
| 200 | + //write_uint32(master_block + 36, 0); |
| 201 | + //write_uint32(master_block + 40, 0); |
| 202 | + memset(master_block + 24, '\0', 20); // Set to zero, above 5 |
| 203 | + util::write_uint32(master_block + 28, 2); // TODO: Update during finalize |
| 204 | + util::write_uint32(master_block + 44, 4); |
| 205 | + //write_uint16(master_block + 48, 0); |
| 206 | + //write_uint16(master_block + 52, 0); |
| 207 | + memset(master_block + 48, '\0', 8); // Set to zero, above 2 |
| 208 | + util::write_uint32(master_block + 56, 1); |
| 209 | + // User version initially 0, set to table leaf count |
| 210 | + // used to locate last leaf page for binary search |
| 211 | + // and move to last page. |
| 212 | + util::write_uint32(master_block + 60, 0); |
| 213 | + util::write_uint32(master_block + 64, 0); |
| 214 | + // App ID - set to 0xA5xxxxxx where A5 is signature |
| 215 | + // till it is implemented |
| 216 | + util::write_uint32(master_block + 68, 0xA5000000); |
| 217 | + memset(master_block + 72, '\0', 20); // reserved space |
| 218 | + util::write_uint32(master_block + 92, 105); |
| 219 | + util::write_uint32(master_block + 96, 3016000); |
| 220 | + memset(master_block + 100, '\0', block_size - 100); // Set remaining page to zero |
| 221 | + |
| 222 | + // master table b-tree |
| 223 | + init_bt_tbl_leaf(master_block + 100, block_size, page_resv_bytes); |
| 224 | + |
| 225 | + // write table script record |
| 226 | + std::string tbl_name = "idx1"; |
| 227 | + if (!table_name.empty()) |
| 228 | + tbl_name = table_name; |
| 229 | + // write table script record |
| 230 | + int col_count = 5; |
| 231 | + // if (table_script) { |
| 232 | + // uint16_t script_len = strlen(table_script); |
| 233 | + // if (script_len > block_size - 100 - page_resv_bytes - 8 - 10) |
| 234 | + // return SQLT_RES_TOO_LONG; |
| 235 | + // set_col_val(4, SQLT_TYPE_TEXT, table_script, script_len); |
| 236 | + // } else { |
| 237 | + int table_name_len = tbl_name.length(); |
| 238 | + // len("CREATE TABLE ") + table_name_len + len(" (") |
| 239 | + // + len("PRIMARY KEY (") + len(") WITHOUT ROWID") |
| 240 | + size_t script_len = 13 + table_name_len + 2 + 13 + 15; |
| 241 | + script_len += col_names.length(); |
| 242 | + script_len += 2; // len(", ") |
| 243 | + int pk_end_pos = 0; |
| 244 | + for (int i = 0, comma_count = 0; i < col_names.length(); i++) { |
| 245 | + if (col_names[i] == ',') |
| 246 | + comma_count++; |
| 247 | + if (comma_count == pk_col_count) { |
| 248 | + pk_end_pos = i; |
| 249 | + break; |
| 250 | + } |
| 251 | + } |
| 252 | + if (pk_end_pos == 0) |
| 253 | + pk_end_pos = col_names.length(); |
| 254 | + script_len += pk_end_pos; |
| 255 | + script_len++; // len(")") |
| 256 | + // 100 byte header, 2 byte ptr, 3 byte rec/hdr vlen, 1 byte rowid |
| 257 | + // 6 byte hdr len, 5 byte "table", twice table name, 4 byte uint32 root |
| 258 | + if (script_len > (block_size - 100 - page_resv_bytes - 8 |
| 259 | + - 2 - 3 - 1 - 6 - 5 - tbl_name.length() * 2 - 4)) |
| 260 | + throw SQLT_RES_TOO_LONG; |
| 261 | + uint8_t *script_loc = master_block + block_size - page_resv_bytes - script_len; |
| 262 | + uint8_t *script_pos = script_loc; |
| 263 | + memcpy(script_pos, "CREATE TABLE ", 13); |
| 264 | + script_pos += 13; |
| 265 | + memcpy(script_pos, tbl_name.c_str(), table_name_len); |
| 266 | + script_pos += table_name_len; |
| 267 | + *script_pos++ = ' '; |
| 268 | + *script_pos++ = '('; |
| 269 | + memcpy(script_pos, col_names.c_str(), col_names.length()); |
| 270 | + script_pos += col_names.length(); |
| 271 | + *script_pos++ = ','; |
| 272 | + *script_pos++ = ' '; |
| 273 | + memcpy(script_pos, "PRIMARY KEY (", 13); |
| 274 | + script_pos += 13; |
| 275 | + memcpy(script_pos, col_names.c_str(), pk_end_pos); |
| 276 | + script_pos += pk_end_pos; |
| 277 | + *script_pos++ = ')'; |
| 278 | + memcpy(script_pos, ") WITHOUT ROWID", 15); |
| 279 | + script_pos += 15; |
| 280 | + // } |
| 281 | + int32_t root_page_no = 2; |
| 282 | + const void *master_rec_values[] = {"table", tbl_name.c_str(), tbl_name.c_str(), &root_page_no, script_loc}; |
| 283 | + const size_t master_rec_col_lens[] = {5, table_name.length(), table_name.length(), sizeof(root_page_no), script_len}; |
| 284 | + const uint8_t master_rec_col_types[] = {SQLT_TYPE_TEXT, SQLT_TYPE_TEXT, SQLT_TYPE_TEXT, SQLT_TYPE_INT32, SQLT_TYPE_TEXT}; |
| 285 | + int res = write_new_rec(master_block, 0, 1, 5, master_rec_values, master_rec_col_lens, master_rec_col_types); |
| 286 | + if (res < 0) |
| 287 | + throw res; |
| 288 | + |
| 289 | + } |
| 290 | + |
| 291 | +}; |
| 292 | + |
| 293 | +#endif |
0 commit comments