├── .gitignore ├── README.md └── code ├── API.cpp ├── API.h ├── Attribute.cpp ├── Attribute.h ├── BPlusTree.h ├── BufferManager.cpp ├── BufferManager.h ├── CatalogManager.cpp ├── CatalogManager.h ├── Condition.cpp ├── Condition.h ├── IndexInfo.h ├── IndexManager.cpp ├── IndexManager.h ├── Interpreter.cpp ├── Interpreter.h ├── Minisql.h ├── RecordManager.cpp ├── RecordManager.h └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Minisql 2 | A very simple local relational database implementation. A small sql server. 3 | 4 | Just for learning the details of a database. 5 | 6 | -- 7 | ##Commands 8 | To be easy, the command of Minisql is a little different from the classic sql command. Only support int \ float \ char(n). 9 | 10 | All index is for single-attribute and single-value. Minisql will create index on the primary key automatically. Users can create index on unique attribute. 11 | 12 | 13 | create table tableName( 14 | attributeName type, 15 | attributeName type, 16 | attributeName type, 17 | ... 18 | primary key (attributeName) 19 | ); 20 | 21 | drop table tableName; 22 | 23 | create index indexName on tableName(attributeName); 24 | 25 | drop index indexName; 26 | 27 | select * from tableName; 28 | select * from tableName where condition1; 29 | select * from tableName where condition1 and condition2; 30 | condition represents that attribute op value, where op is > <> = >= <=. 31 | 32 | insert into tableName values(value1, value2); 33 | 34 | delete * from tableName; 35 | delete * from tableName where condition; 36 | 37 | quit; 38 | 39 | execfile file; # exec the sql queries in the file. 40 | 41 | -- 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /code/API.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // API.cpp 3 | // minisql 4 | // 5 | // Created by 邓永辉 on 14/11/5. 6 | // Copyright (c) 2014年 邓永辉. All rights reserved. 7 | // 8 | 9 | #include "API.h" 10 | #include "RecordManager.h" 11 | #include "CatalogManager.h" 12 | #include "IndexManager.h" 13 | 14 | #define UNKNOWN_FILE 8 15 | #define TABLE_FILE 9 16 | #define INDEX_FILE 10 17 | 18 | CatalogManager *cm; 19 | IndexManager* im; 20 | 21 | /** 22 | * 23 | * drop a table 24 | * @param tableName: name of table 25 | */ 26 | void API::tableDrop(string tableName) 27 | { 28 | if (!tableExist(tableName)) return; 29 | 30 | vector indexNameVector; 31 | 32 | //get all index in the table, and drop them all 33 | indexNameListGet(tableName, &indexNameVector); 34 | for (int i = 0; i < indexNameVector.size(); i++) 35 | { 36 | printf("%s", indexNameVector[i].c_str()); 37 | 38 | indexDrop(indexNameVector[i]); 39 | } 40 | 41 | //delete a table file 42 | if(rm->tableDrop(tableName)) 43 | { 44 | //delete a table information 45 | cm->dropTable(tableName); 46 | printf("Drop table %s successfully\n", tableName.c_str()); 47 | } 48 | } 49 | 50 | /** 51 | * 52 | * drop a index 53 | * @param indexName: name of index 54 | */ 55 | void API::indexDrop(string indexName) 56 | { 57 | if (cm->findIndex(indexName) != INDEX_FILE) 58 | { 59 | printf("There is no index %s \n", indexName.c_str()); 60 | return; 61 | } 62 | 63 | //delete a index file 64 | if (rm->indexDrop(indexName)) 65 | { 66 | 67 | //get type of index 68 | int indexType = cm->getIndexType(indexName); 69 | if (indexType == -2) 70 | { 71 | printf("error\n"); 72 | return; 73 | } 74 | 75 | //delete a index information 76 | cm->dropIndex(indexName); 77 | 78 | //delete a index tree 79 | im->dropIndex(rm->indexFileNameGet(indexName), indexType); 80 | printf("Drop index %s successfully\n", indexName.c_str()); 81 | } 82 | } 83 | 84 | /** 85 | * 86 | * create a index 87 | * @param indexName: name of index 88 | * @param tableName: name of table 89 | * @param attributeName: name of attribute in a table 90 | */ 91 | void API::indexCreate(string indexName, string tableName, string attributeName) 92 | { 93 | if (cm->findIndex(indexName) == INDEX_FILE) 94 | { 95 | cout << "There is index " << indexName << " already" << endl; 96 | return; 97 | } 98 | 99 | if (!tableExist(tableName)) return; 100 | 101 | vector attributeVector; 102 | cm->attributeGet(tableName, &attributeVector); 103 | int i; 104 | int type = 0; 105 | for (i = 0; i < attributeVector.size(); i++) 106 | { 107 | if (attributeName == attributeVector[i].name) 108 | { 109 | if (!attributeVector[i].ifUnique) 110 | { 111 | cout << "the attribute is not unique" << endl; 112 | 113 | return; 114 | } 115 | type = attributeVector[i].type; 116 | break; 117 | } 118 | } 119 | 120 | if (i == attributeVector.size()) 121 | { 122 | cout << "there is not this attribute in the table" << endl; 123 | return; 124 | } 125 | 126 | //RecordManager to create a index file 127 | if (rm->indexCreate(indexName)) 128 | { 129 | //CatalogManager to add a index information 130 | cm->addIndex(indexName, tableName, attributeName, type); 131 | 132 | //get type of index 133 | int indexType = cm->getIndexType(indexName); 134 | if (indexType == -2) 135 | { 136 | cout << "error"; 137 | return; 138 | } 139 | 140 | //indexManager to create a index tress 141 | im->createIndex(rm->indexFileNameGet(indexName), indexType); 142 | 143 | //recordManager insert already record to index 144 | rm->indexRecordAllAlreadyInsert(tableName, indexName); 145 | printf("Create index %s successfully\n", indexName.c_str()); 146 | } 147 | else 148 | { 149 | cout << "Create index " << indexName << " fail" << endl; 150 | } 151 | } 152 | 153 | /** 154 | * 155 | * create a table 156 | * @param tableName: name of table 157 | * @param attributeVector: vector of attribute 158 | * @param primaryKeyName: primary key of a table (default: "") 159 | * @param primaryKeyLocation: the primary position in the table 160 | */ 161 | void API::tableCreate(string tableName, vector* attributeVector, string primaryKeyName,int primaryKeyLocation) 162 | { 163 | // cout << "=======api::tablecreate=======" << endl 164 | // << "tableName: " << tableName << "; primaryKeyName: " << primaryKeyName << "; location: " << primaryKeyLocation << endl; 165 | // for (int i = 0; i < (* attributeVector).size(); i++) 166 | // { 167 | // (* attributeVector)[i].print(); 168 | // } 169 | 170 | 171 | if(cm->findTable(tableName) == TABLE_FILE) 172 | { 173 | cout << "There is a table " << tableName << " already" << endl; 174 | return; 175 | } 176 | 177 | //RecordManager to create a table file 178 | if(rm->tableCreate(tableName)) 179 | { 180 | //CatalogManager to create a table information 181 | cm->addTable(tableName, attributeVector, primaryKeyName, primaryKeyLocation); 182 | 183 | printf("Create table %s successfully\n", tableName.c_str()); 184 | } 185 | 186 | if (primaryKeyName != "") 187 | { 188 | //get a primary key 189 | string indexName = primaryIndexNameGet(tableName); 190 | indexCreate(indexName, tableName, primaryKeyName); 191 | } 192 | } 193 | 194 | /** 195 | * 196 | * show all record of attribute in the table and the number of the record 197 | * @param tableName: name of table 198 | * @param attributeNameVector: vector of name of attribute 199 | */ 200 | void API::recordShow(string tableName, vector* attributeNameVector) 201 | { 202 | vector conditionVector; 203 | recordShow(tableName, attributeNameVector, &conditionVector); 204 | } 205 | 206 | /** 207 | * 208 | * show the record matching the coditions of attribute in the table and the number of the record 209 | * @param tableName: name of table 210 | * @param attributeNameVector: vector of name of attribute 211 | * @param conditionVector: vector of condition 212 | */ 213 | void API::recordShow(string tableName, vector* attributeNameVector, vector* conditionVector) 214 | { 215 | if (cm->findTable(tableName) == TABLE_FILE) 216 | { 217 | int num = 0; 218 | vector attributeVector; 219 | attributeGet(tableName, &attributeVector); 220 | 221 | vector allAttributeName; 222 | if (attributeNameVector == NULL) { 223 | for (Attribute attribute : attributeVector) 224 | { 225 | allAttributeName.insert(allAttributeName.end(), attribute.name); 226 | } 227 | 228 | attributeNameVector = &allAttributeName; 229 | } 230 | 231 | //print attribute name you want to show 232 | tableAttributePrint(attributeNameVector); 233 | 234 | for (string name : (*attributeNameVector)) 235 | { 236 | int i = 0; 237 | for (i = 0; i < attributeVector.size(); i++) 238 | { 239 | if (attributeVector[i].name == name) 240 | { 241 | break; 242 | } 243 | } 244 | 245 | if (i == attributeVector.size()) 246 | { 247 | cout << "the attribute which you want to print is not exist in the table" << endl; 248 | return; 249 | } 250 | } 251 | 252 | int blockOffset = -1; 253 | if (conditionVector != NULL) 254 | { 255 | for (Condition condition : *conditionVector) 256 | { 257 | int i = 0; 258 | for (i = 0; i < attributeVector.size(); i++) 259 | { 260 | if (attributeVector[i].name == condition.attributeName) 261 | { 262 | if (condition.operate == Condition::OPERATOR_EQUAL && attributeVector[i].index != "") 263 | { 264 | blockOffset = im->searchIndex(rm->indexFileNameGet(attributeVector[i].index), condition.value, attributeVector[i].type); 265 | } 266 | break; 267 | } 268 | } 269 | 270 | if (i == attributeVector.size()) 271 | { 272 | cout << "the attribute is not exist in the table" << endl; 273 | return; 274 | } 275 | } 276 | } 277 | 278 | if (blockOffset == -1) 279 | { 280 | //cout << "if we con't find the block by index,we need to find all block" << endl; 281 | num = rm->recordAllShow(tableName, attributeNameVector,conditionVector); 282 | } 283 | else 284 | { 285 | 286 | //find the block by index,search in the block 287 | num = rm->recordBlockShow(tableName, attributeNameVector, conditionVector, blockOffset); 288 | } 289 | 290 | printf("%d records selected\n", num); 291 | } 292 | else 293 | { 294 | cout << "There is no table " << tableName << endl; 295 | } 296 | } 297 | 298 | /** 299 | * 300 | * insert a record to a table 301 | * @param tableName: name of table 302 | * @param recordContent: Vector of these content of a record 303 | */ 304 | void API::recordInsert(string tableName, vector* recordContent) 305 | { 306 | if (!tableExist(tableName)) return; 307 | 308 | string indexName; 309 | 310 | //deal if the record could be insert (if index is exist) 311 | vector attributeVector; 312 | 313 | vector conditionVector; 314 | 315 | attributeGet(tableName, &attributeVector); 316 | for (int i = 0 ; i < attributeVector.size(); i++) 317 | { 318 | indexName = attributeVector[i].indexNameGet(); 319 | if (indexName != "") 320 | { 321 | //if the attribute has a index 322 | int blockoffest = im->searchIndex(rm->indexFileNameGet(indexName), (*recordContent)[i], attributeVector[i].type); 323 | 324 | if (blockoffest != -1) 325 | { 326 | //if the value has exist in index tree then fail to insert the record 327 | cout << "insert fail because index value exist" << endl; 328 | return; 329 | } 330 | } 331 | else if (attributeVector[i].ifUnique) 332 | { 333 | //if the attribute is unique but not index 334 | Condition condition(attributeVector[i].name, (*recordContent)[i], Condition::OPERATOR_EQUAL); 335 | conditionVector.insert(conditionVector.end(), condition); 336 | } 337 | } 338 | 339 | if (conditionVector.size() > 0) 340 | { 341 | for (int i = 0; i < conditionVector.size(); i++) { 342 | vector conditionTmp; 343 | conditionTmp.insert(conditionTmp.begin(), conditionVector[i]); 344 | 345 | int recordConflictNum = rm->recordAllFind(tableName, &conditionTmp); 346 | if (recordConflictNum > 0) { 347 | cout << "insert fail because unique value exist" << endl; 348 | return; 349 | } 350 | 351 | } 352 | } 353 | 354 | char recordString[2000]; 355 | memset(recordString, 0, 2000); 356 | 357 | //CatalogManager to get the record string 358 | cm->recordStringGet(tableName, recordContent, recordString); 359 | 360 | //RecordManager to insert the record into file; and get the position of block being insert 361 | int recordSize = cm->calcuteLenth(tableName); 362 | int blockOffset = rm->recordInsert(tableName, recordString, recordSize); 363 | 364 | if(blockOffset >= 0) 365 | { 366 | recordIndexInsert(recordString, recordSize, &attributeVector, blockOffset); 367 | cm->insertRecord(tableName, 1); 368 | printf("insert record into table %s successful\n", tableName.c_str()); 369 | } 370 | else 371 | { 372 | cout << "insert record into table " << tableName << " fail" << endl; 373 | } 374 | } 375 | 376 | /** 377 | * 378 | * delete all record of table 379 | * @param tableName: name of table 380 | */ 381 | void API::recordDelete(string tableName) 382 | { 383 | vector conditionVector; 384 | recordDelete(tableName, &conditionVector); 385 | } 386 | 387 | /** 388 | * 389 | * delete the record matching the coditions in the table 390 | * @param tableName: name of table 391 | * @param conditionVector: vector of condition 392 | */ 393 | void API::recordDelete(string tableName, vector* conditionVector) 394 | { 395 | if (!tableExist(tableName)) return; 396 | 397 | int num = 0; 398 | vector attributeVector; 399 | attributeGet(tableName, &attributeVector); 400 | 401 | int blockOffset = -1; 402 | if (conditionVector != NULL) 403 | { 404 | for (Condition condition : *conditionVector) 405 | { 406 | if (condition.operate == Condition::OPERATOR_EQUAL) 407 | { 408 | for (Attribute attribute : attributeVector) 409 | { 410 | if (attribute.index != "" && attribute.name == condition.attributeName) 411 | { 412 | blockOffset = im->searchIndex(rm->indexFileNameGet(attribute.index), condition.value, attribute.type); 413 | } 414 | } 415 | } 416 | } 417 | } 418 | 419 | 420 | if (blockOffset == -1) 421 | { 422 | //if we con't find the block by index,we need to find all block 423 | num = rm->recordAllDelete(tableName, conditionVector); 424 | } 425 | else 426 | { 427 | //find the block by index,search in the block 428 | num = rm->recordBlockDelete(tableName, conditionVector, blockOffset); 429 | } 430 | 431 | //delete the number of record in in the table 432 | cm->deleteValue(tableName, num); 433 | printf("delete %d record in table %s\n", num, tableName.c_str()); 434 | } 435 | 436 | /** 437 | * 438 | * get the number of the records in table 439 | * @param tableName: name of table 440 | */ 441 | int API::recordNumGet(string tableName) 442 | { 443 | if (!tableExist(tableName)) return 0; 444 | 445 | return cm->getRecordNum(tableName); 446 | } 447 | 448 | /** 449 | * 450 | * get the size of a record in table 451 | * @param tableName: name of table 452 | */ 453 | int API::recordSizeGet(string tableName) 454 | { 455 | if (!tableExist(tableName)) return 0; 456 | 457 | return cm->calcuteLenth(tableName); 458 | } 459 | 460 | /** 461 | * 462 | * get the size of a type 463 | * @param type: type of attribute 464 | */ 465 | int API::typeSizeGet(int type) 466 | { 467 | return cm->calcuteLenth2(type); 468 | } 469 | 470 | /** 471 | * 472 | * get the vector of a all name of index in the table 473 | * @param tableName: name of table 474 | * @param indexNameVector: a point to vector of indexName(which would change) 475 | */ 476 | int API::indexNameListGet(string tableName, vector* indexNameVector) 477 | { 478 | if (!tableExist(tableName)) { 479 | return 0; 480 | } 481 | return cm->indexNameListGet(tableName, indexNameVector); 482 | } 483 | 484 | /** 485 | * 486 | * get the vector of all name of index's file 487 | * @param indexNameVector: will set all index's 488 | */ 489 | void API::allIndexAddressInfoGet(vector *indexNameVector) 490 | { 491 | cm->getAllIndex(indexNameVector); 492 | for (int i = 0; i < (*indexNameVector).size(); i++) 493 | { 494 | (*indexNameVector)[i].indexName = rm->indexFileNameGet((*indexNameVector)[i].indexName); 495 | } 496 | } 497 | 498 | /** 499 | * 500 | * get the vector of a attribute‘s type in a table 501 | * @param tableName: name of table 502 | * @param attributeNameVector: a point to vector of attributeType(which would change) 503 | */ 504 | int API::attributeGet(string tableName, vector* attributeVector) 505 | { 506 | if (!tableExist(tableName)) { 507 | return 0; 508 | } 509 | return cm->attributeGet(tableName, attributeVector); 510 | } 511 | 512 | /** 513 | * 514 | * insert all index value of a record to index tree 515 | * @param recordBegin: point to record begin 516 | * @param recordSize: size of the record 517 | * @param attributeVector: a point to vector of attributeType(which would change) 518 | * @param blockOffset: the block offset num 519 | */ 520 | void API::recordIndexInsert(char* recordBegin,int recordSize, vector* attributeVector, int blockOffset) 521 | { 522 | char* contentBegin = recordBegin; 523 | for (int i = 0; i < (*attributeVector).size() ; i++) 524 | { 525 | int type = (*attributeVector)[i].type; 526 | int typeSize = typeSizeGet(type); 527 | if ((*attributeVector)[i].index != "") 528 | { 529 | indexInsert((*attributeVector)[i].index, contentBegin, type, blockOffset); 530 | } 531 | 532 | contentBegin += typeSize; 533 | } 534 | } 535 | 536 | /** 537 | * 538 | * insert a value to index tree 539 | * @param indexName: name of index 540 | * @param contentBegin: address of content 541 | * @param type: the type of content 542 | * @param blockOffset: the block offset num 543 | */ 544 | void API::indexInsert(string indexName, char* contentBegin, int type, int blockOffset) 545 | { 546 | string content= ""; 547 | stringstream tmp; 548 | //if the attribute has index 549 | ///这里传*attributeVector)[i].index这个index的名字, blockOffset,还有值 550 | if (type == Attribute::TYPE_INT) 551 | { 552 | int value = *((int*)contentBegin); 553 | tmp << value; 554 | } 555 | else if (type == Attribute::TYPE_FLOAT) 556 | { 557 | float value = *((float* )contentBegin); 558 | tmp << value; 559 | } 560 | else 561 | { 562 | char value[255]; 563 | memset(value, 0, 255); 564 | memcpy(value, contentBegin, sizeof(type)); 565 | string stringTmp = value; 566 | tmp << stringTmp; 567 | } 568 | tmp >> content; 569 | im->insertIndex(rm->indexFileNameGet(indexName), content, blockOffset, type); 570 | } 571 | 572 | /** 573 | * 574 | * delete all index value of a record to index tree 575 | * @param recordBegin: point to record begin 576 | * @param recordSize: size of the record 577 | * @param attributeVector: a point to vector of attributeType(which would change) 578 | * @param blockOffset: the block offset num 579 | */ 580 | void API::recordIndexDelete(char* recordBegin,int recordSize, vector* attributeVector, int blockOffset) 581 | { 582 | char* contentBegin = recordBegin; 583 | for (int i = 0; i < (*attributeVector).size() ; i++) 584 | { 585 | int type = (*attributeVector)[i].type; 586 | int typeSize = typeSizeGet(type); 587 | 588 | string content= ""; 589 | stringstream tmp; 590 | 591 | if ((*attributeVector)[i].index != "") 592 | { 593 | //if the attribute has index 594 | ///这里传*attributeVector)[i].index这个index的名字, blockOffset,还有值 595 | if (type == Attribute::TYPE_INT) 596 | { 597 | int value = *((int*)contentBegin); 598 | tmp << value; 599 | } 600 | else if (type == Attribute::TYPE_FLOAT) 601 | { 602 | float value = *((float* )contentBegin); 603 | tmp << value; 604 | } 605 | else 606 | { 607 | char value[255]; 608 | memset(value, 0, 255); 609 | memcpy(value, contentBegin, sizeof(type)); 610 | string stringTmp = value; 611 | tmp << stringTmp; 612 | } 613 | 614 | tmp >> content; 615 | im->deleteIndexByKey(rm->indexFileNameGet((*attributeVector)[i].index), content, type); 616 | 617 | } 618 | contentBegin += typeSize; 619 | } 620 | 621 | } 622 | 623 | /** 624 | * get if the table 625 | * @param tableName the name of the table 626 | */ 627 | int API::tableExist(string tableName) 628 | { 629 | if (cm->findTable(tableName) != TABLE_FILE) 630 | { 631 | cout << "There is no table " << tableName << endl; 632 | return 0; 633 | } 634 | else 635 | { 636 | return 1; 637 | } 638 | } 639 | 640 | /** 641 | * get the primary index Name by table 642 | * @param tableName : name of the table 643 | */ 644 | string API::primaryIndexNameGet(string tableName) 645 | { 646 | return "PRIMARY_" + tableName; 647 | } 648 | 649 | /** 650 | * printe attribute name 651 | * @param attributeNameVector: the vector of attribute's name 652 | */ 653 | void API::tableAttributePrint(vector* attributeNameVector) 654 | { 655 | int i = 0; 656 | for ( i = 0; i < (*attributeNameVector).size(); i++) 657 | { 658 | printf("%s ", (*attributeNameVector)[i].c_str()); 659 | } 660 | if (i != 0) 661 | printf("\n"); 662 | } 663 | 664 | -------------------------------------------------------------------------------- /code/API.h: -------------------------------------------------------------------------------- 1 | #ifndef API_H 2 | #define API_H 3 | 4 | #include "Attribute.h" 5 | #include "Condition.h" 6 | #include "Minisql.h" 7 | #include "IndexInfo.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class CatalogManager; 14 | class RecordManager; 15 | class IndexManager; 16 | class API{ 17 | public: 18 | RecordManager *rm; 19 | CatalogManager *cm; 20 | IndexManager *im; 21 | API(){} 22 | ~API(){} 23 | 24 | 25 | void tableDrop(string tableName); 26 | void tableCreate(string tableName, vector* attributeVector, string primaryKeyName,int primaryKeyLocation); 27 | 28 | 29 | void indexDrop(string indexName); 30 | void indexCreate(string indexName, string tableName, string attributeName); 31 | 32 | void recordShow(string tableName, vector* attributeNameVector = NULL); 33 | void recordShow(string tableName, vector* attributeNameVector, vector* conditionVector); 34 | 35 | void recordInsert(string tableName,vector* recordContent); 36 | 37 | void recordDelete(string tableName); 38 | void recordDelete(string tableName, vector* conditionVector); 39 | 40 | int recordNumGet(string tableName); 41 | int recordSizeGet(string tableName); 42 | 43 | int typeSizeGet(int type); 44 | 45 | void allIndexAddressInfoGet(vector *indexNameVector); 46 | 47 | int attributeNameGet(string tableName, vector* attributeNameVector); 48 | int attributeTypeGet(string tableName, vector* attributeTypeVector); 49 | int attributeGet(string tableName, vector* attributeVector); 50 | 51 | void indexValueInsert(string indexName, string value, int blockOffset); 52 | 53 | void indexInsert(string indexName, char* value, int type, int blockOffset); 54 | void recordIndexDelete(char* recordBegin,int recordSize, vector* attributeVector, int blockOffset); 55 | void recordIndexInsert(char* recordBegin,int recordSize, vector* attributeVector, int blockOffset); 56 | 57 | private: 58 | int tableExist(string tableName); 59 | int indexNameListGet(string tableName, vector* indexNameVector); 60 | string primaryIndexNameGet(string tableName); 61 | void tableAttributePrint(vector* name); 62 | }; 63 | 64 | struct int_t{ 65 | int value; 66 | }; 67 | 68 | struct float_t{ 69 | float value; 70 | }; 71 | #endif -------------------------------------------------------------------------------- /code/Attribute.cpp: -------------------------------------------------------------------------------- 1 | #include "Attribute.h" 2 | 3 | Attribute::Attribute(string n, int t, bool i) { 4 | name = n; 5 | type = t; 6 | ifUnique = i; 7 | index = ""; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /code/Attribute.h: -------------------------------------------------------------------------------- 1 | // 2 | // Attribute.h 3 | // minisql 4 | // 5 | // Created by 邓永辉 on 14/11/4. 6 | // Copyright (c) 2014年 邓永辉. All rights reserved. 7 | // 8 | 9 | #ifndef minisql_Attribute_h 10 | #define minisql_Attribute_h 11 | 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Attribute 17 | { 18 | public: 19 | string name; 20 | int type; //the type of the attribute,-1 represents float, 0 represents int, other positive integer represents char and the value is the number of char) 21 | bool ifUnique; 22 | string index; // default value is "", representing no index 23 | Attribute(string n, int t, bool i); 24 | 25 | public: 26 | int static const TYPE_FLOAT = -1; 27 | int static const TYPE_INT = 0; 28 | string indexNameGet(){return index;} 29 | 30 | void print() 31 | { 32 | cout << "name: " << name << ";type: " << type << ";ifUnique: " << ifUnique << ";index: " << index << endl; 33 | } 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /code/BPlusTree.h: -------------------------------------------------------------------------------- 1 | // 2 | // BPlusTree.h 3 | // Minisql 4 | // Description: The basic b+ tree implement of template. 5 | // 6 | // Created by xuyuhao on 14/11/8. 7 | // Copyright (c) 2014年 xuyuhao. All rights reserved. 8 | // 9 | 10 | #ifndef __Minisql__BPlusTree__ 11 | #define __Minisql__BPlusTree__ 12 | #include 13 | #include 14 | #include 15 | #include "BufferManager.h" 16 | #include "Minisql.h" 17 | #include 18 | using namespace std; 19 | 20 | static BufferManager bm; 21 | //**********************TreeNode***************************// 22 | typedef int offsetNumber; // the value of the tree node 23 | 24 | template 25 | class TreeNode{ 26 | public: 27 | size_t count; // the count of keys 28 | TreeNode* parent; 29 | vector keys; 30 | vector childs; 31 | vector vals; 32 | 33 | TreeNode* nextLeafNode; // point to the next leaf node 34 | 35 | bool isLeaf; // the flag whether this node is a leaf node 36 | 37 | private: 38 | int degree; 39 | 40 | public: 41 | //create a new node. if the newLeaf = false, create a branch node.Otherwise, create a leaf node 42 | TreeNode(int degree,bool newLeaf=false); 43 | ~TreeNode(); 44 | 45 | public: 46 | bool isRoot(); 47 | bool search(KeyType key,size_t &index);//search a key and return by the reference of a parameter 48 | TreeNode* splite(KeyType &key); 49 | size_t add(KeyType &key); //add the key in the branch and return the position 50 | size_t add(KeyType &key,offsetNumber val); // add a key-value in the leaf node and return the position 51 | bool removeAt(size_t index); 52 | 53 | #ifdef _DEBUG 54 | public: 55 | void debug_print(); 56 | #endif 57 | }; 58 | 59 | 60 | //**********************BplusTree***************************// 61 | 62 | template 63 | class BPlusTree 64 | { 65 | private: 66 | typedef TreeNode* Node; 67 | 68 | // a struct helping to find the node containing a specific key 69 | struct searchNodeParse 70 | { 71 | Node pNode; // a pointer pointering to the node containing the key 72 | size_t index; // the position of the key 73 | bool ifFound; // the flag that whether the key is found. 74 | }; 75 | private: 76 | string fileName; 77 | Node root; 78 | Node leafHead; // the head of the leaf node 79 | size_t keyCount; 80 | size_t level; 81 | size_t nodeCount; 82 | fileNode* file; // the filenode of this tree 83 | int keySize; // the size of key 84 | int degree; 85 | 86 | public: 87 | BPlusTree(string m_name,int keySize,int degree); 88 | ~BPlusTree(); 89 | 90 | offsetNumber search(KeyType& key); // search the value of specific key 91 | bool insertKey(KeyType &key,offsetNumber val); 92 | bool deleteKey(KeyType &key); 93 | 94 | void dropTree(Node node); 95 | 96 | void readFromDiskAll(); 97 | void writtenbackToDiskAll(); 98 | void readFromDisk(blockNode* btmp); 99 | 100 | private: 101 | void init_tree();// init the tree 102 | bool adjustAfterinsert(Node pNode); 103 | bool adjustAfterDelete(Node pNode); 104 | void findToLeaf(Node pNode,KeyType key,searchNodeParse &snp); 105 | 106 | //DEBUG 107 | #ifdef _DEBUG 108 | public: 109 | void debug_print(); 110 | 111 | void debug_print_node(Node pNode); 112 | #endif 113 | 114 | }; 115 | 116 | 117 | 118 | 119 | /* the implement of BPlusTree function */ 120 | //******** The definition of the functions of the class TreeNode ********** 121 | 122 | /** 123 | * Constructor: create the tree node. 124 | * 125 | * @param int the degreee 126 | * @param bool the flag that whether the node is a tree node or not 127 | * 128 | */ 129 | template 130 | TreeNode::TreeNode(int m_degree,bool newLeaf):count(0),parent(NULL),nextLeafNode(NULL),isLeaf(newLeaf),degree(m_degree) 131 | { 132 | for(size_t i = 0;i < degree+1;i ++) 133 | { 134 | childs.push_back(NULL); 135 | keys.push_back(KeyType()); 136 | vals.push_back(offsetNumber()); 137 | } 138 | childs.push_back(NULL); 139 | } 140 | 141 | /** 142 | * @Deconstructor 143 | * 144 | */ 145 | template 146 | TreeNode::~TreeNode() 147 | { 148 | 149 | } 150 | 151 | /** 152 | * Test if this node is the root or not. 153 | * 154 | * @return bool the flag that whether this node is the root or not 155 | * 156 | */ 157 | template 158 | bool TreeNode::isRoot() 159 | { 160 | if(parent != NULL) return false; 161 | else return true; 162 | } 163 | 164 | /** 165 | * Search the key in the node 166 | * 167 | * @param KeyType 168 | * @param size_t return the position of the node by reference 169 | * 170 | * @return bool the flag that whether the key exists in the node 171 | * 172 | */ 173 | template 174 | bool TreeNode::search(KeyType key,size_t &index) 175 | { 176 | if(count == 0 ) // no values in the node 177 | { 178 | index = 0; 179 | return false; 180 | } 181 | else 182 | { 183 | // test if key are beyond the area of the keys array 184 | if(keys[count-1] < key) 185 | { 186 | index = count; 187 | return false; 188 | } 189 | else if(keys[0] > key) 190 | { 191 | index = 0; 192 | return false; 193 | } // end of test 194 | else if(count <= 20) // sequential search 195 | { 196 | for(size_t i = 0;i < count;i ++) 197 | { 198 | if(keys[i] == key) 199 | { 200 | index = i; 201 | return true; 202 | } 203 | else if(keys[i] < key) 204 | { 205 | continue; 206 | } 207 | else if(keys[i] > key) 208 | { 209 | index = i; 210 | return false; 211 | } 212 | } 213 | } // end sequential search 214 | else if(count > 20) // too many keys, binary search. 2* log(n,2) < (1+n)/2 215 | { 216 | size_t left = 0, right = count - 1, pos = 0; 217 | while(right>left+1) 218 | { 219 | pos = (right + left) / 2; 220 | if(keys[pos] == key) 221 | { 222 | index = pos; 223 | return true; 224 | } 225 | else if(keys[pos] < key) 226 | { 227 | left = pos; 228 | } 229 | else if(keys[pos] > key) 230 | { 231 | right = pos; 232 | } 233 | } // end while 234 | 235 | // right == left + 1 236 | if(keys[left] >= key) 237 | { 238 | index = left; 239 | return (keys[left] == key); 240 | } 241 | else if(keys[right] >= key) 242 | { 243 | index = right; 244 | return (keys[right] == key); 245 | } 246 | else if(keys[right] < key) 247 | { 248 | index = right ++; 249 | return false; 250 | } 251 | } // end binary search 252 | } 253 | return false; 254 | } 255 | 256 | /** 257 | * Splite this node to two and the new node will be the next node. 258 | * 259 | * @param KeyType & the key reference returns the key that will go to the upper level 260 | * 261 | * @return TreeNode * 262 | * 263 | */ 264 | template 265 | TreeNode* TreeNode::splite(KeyType &key) 266 | { 267 | size_t minmumNode = (degree - 1) / 2; 268 | TreeNode* newNode = new TreeNode(degree,this->isLeaf); 269 | if(newNode == NULL) 270 | { 271 | cout << "Problems in allocate momeory of TreeNode in splite node of " << key << endl; 272 | exit(2); 273 | } 274 | 275 | if(isLeaf) // this is a leaf node 276 | { 277 | key = keys[minmumNode + 1]; 278 | for(size_t i = minmumNode + 1;i < degree;i ++) // copy the right hand of the keys to the new node 279 | { 280 | newNode->keys[i-minmumNode-1] = keys[i]; 281 | keys[i] = KeyType(); 282 | newNode->vals[i-minmumNode-1] = vals[i]; 283 | vals[i] = offsetNumber(); 284 | } 285 | newNode->nextLeafNode = this->nextLeafNode; 286 | this->nextLeafNode = newNode; 287 | 288 | newNode->parent = this->parent; 289 | newNode->count = minmumNode; 290 | this->count = minmumNode + 1; 291 | } // end leaf 292 | else if(!isLeaf) 293 | { 294 | key = keys[minmumNode]; 295 | for(size_t i = minmumNode + 1;i < degree+1;i ++) 296 | { 297 | newNode->childs[i-minmumNode-1] = this->childs[i]; 298 | newNode->childs[i-minmumNode-1]->parent = newNode; 299 | this->childs[i] = NULL; 300 | } 301 | for(size_t i = minmumNode + 1;i < degree;i ++) 302 | { 303 | newNode->keys[i-minmumNode-1] = this->keys[i]; 304 | this->keys[i] = KeyType(); 305 | } 306 | this->keys[minmumNode] = KeyType(); 307 | newNode->parent = this->parent; 308 | newNode->count = minmumNode; 309 | this->count = minmumNode; 310 | } 311 | return newNode; 312 | } 313 | 314 | /** 315 | * Add the key in the branch node and return the position added. 316 | * 317 | * @param KeyType & 318 | * 319 | * @return size_t the position to insert 320 | * 321 | */ 322 | template 323 | size_t TreeNode::add(KeyType &key) 324 | { 325 | if(count == 0) 326 | { 327 | keys[0] = key; 328 | count ++; 329 | return 0; 330 | } 331 | else //count > 0 332 | { 333 | size_t index = 0; // record the index of the tree 334 | bool exist = search(key, index); 335 | if(exist) 336 | { 337 | cout << "Error:In add(Keytype &key),key has already in the tree!" << endl; 338 | exit(3); 339 | } 340 | else // add the key into the node 341 | { 342 | for(size_t i = count;i > index;i --) 343 | keys[i] = keys[i-1]; 344 | keys[index] = key; 345 | 346 | for(size_t i = count + 1;i > index+1;i --) 347 | childs[i] = childs[i-1]; 348 | childs[index+1] = NULL; // this child will link to another node 349 | count ++; 350 | 351 | return index; 352 | } 353 | } 354 | } 355 | 356 | /** 357 | * Add the key in the leaf node and return the position added. 358 | * 359 | * @param Keytype & 360 | * @param offsetNumber the value 361 | * 362 | * @return size_t the position to insert 363 | * 364 | */ 365 | template 366 | size_t TreeNode::add(KeyType &key,offsetNumber val) 367 | { 368 | if(!isLeaf) 369 | { 370 | cout << "Error:add(KeyType &key,offsetNumber val) is a function for leaf nodes" << endl; 371 | return -1; 372 | } 373 | if(count == 0) 374 | { 375 | keys[0] = key; 376 | vals[0] = val; 377 | count ++; 378 | return 0; 379 | } 380 | else //count > 0 381 | { 382 | size_t index = 0; // record the index of the tree 383 | bool exist = search(key, index); 384 | if(exist) 385 | { 386 | cout << "Error:In add(Keytype &key, offsetNumber val),key has already in the tree!" << endl; 387 | exit(3); 388 | } 389 | else // add the key into the node 390 | { 391 | for(size_t i = count;i > index;i --) 392 | { 393 | keys[i] = keys[i-1]; 394 | vals[i] = vals[i-1]; 395 | } 396 | keys[index] = key; 397 | vals[index] = val; 398 | count ++; 399 | return index; 400 | } 401 | } 402 | } 403 | 404 | /** 405 | * Delete the key-value or key-child by the position. 406 | * 407 | * @param size_t the position to delete 408 | * 409 | * @return bool the falg that delation successes or not 410 | * 411 | */ 412 | template 413 | bool TreeNode::removeAt(size_t index) 414 | { 415 | if(index > count) 416 | { 417 | cout << "Error:In removeAt(size_t index), index is more than count!" << endl; 418 | return false; 419 | } 420 | else 421 | { 422 | if(isLeaf) 423 | { 424 | for(size_t i = index;i < count-1;i ++) 425 | { 426 | keys[i] = keys[i+1]; 427 | vals[i] = vals[i+1]; 428 | } 429 | keys[count-1] = KeyType(); 430 | vals[count-1] = offsetNumber(); 431 | } 432 | else // is nonleaf 433 | { 434 | for(size_t i = index;i < count-1;i ++) 435 | keys[i] = keys[i+1]; 436 | 437 | for(size_t i = index+1;i < count;i ++) 438 | childs[i] = childs[i+1]; 439 | 440 | keys[count-1] = KeyType(); 441 | childs[count] = NULL; 442 | } 443 | 444 | count --; 445 | return true; 446 | } 447 | } 448 | 449 | #ifdef _DEBUG 450 | /** 451 | * For debug, print the whole tree 452 | * 453 | * @param 454 | * 455 | * @return void 456 | * 457 | */ 458 | template 459 | void TreeNode::debug_print() 460 | { 461 | cout << "############DEBUG for node###############" << endl; 462 | 463 | cout << "Address: " << (void*)this << ",count: " << count << ",Parent: " << (void*)parent << ",isleaf: " << isLeaf << ",nextNode: " << (void*)nextLeafNode << endl; 464 | cout << "KEYS:{"; 465 | for(size_t i = 0;i < count;i ++) 466 | { 467 | cout << keys[i] << " "; 468 | } 469 | cout << "}" << endl; 470 | if(isLeaf) 471 | { 472 | cout << "VALS:{"; 473 | for(size_t i = 0;i < count;i ++) 474 | { 475 | cout << vals[i] << " "; 476 | } 477 | cout << "}" << endl; 478 | } 479 | else // nonleaf node 480 | { 481 | cout << "CHILDREN:{"; 482 | for(size_t i = 0;i < count + 1;i ++) 483 | { 484 | cout << (void*)childs[i] << " "; 485 | } 486 | 487 | cout << "}" << endl; 488 | } 489 | cout << "#############END OF DEBUG IN NODE########"<< endl; 490 | } 491 | #endif 492 | 493 | //******** The definition of the functions of the class BPlusTree ********** 494 | 495 | 496 | /** 497 | * Constructor: init the tree, allocate the memory of the root and then,if users have created the tree before, read from disk and rebuild it. 498 | * 499 | * @param string m_name 500 | * @param int keysize 501 | * @param int m_degree 502 | * 503 | */ 504 | template 505 | BPlusTree::BPlusTree(string m_name,int keysize,int m_degree):fileName(m_name),keyCount(0),level(0),nodeCount(0),root(NULL),leafHead(NULL),keySize(keysize),file(NULL),degree(m_degree) 506 | { 507 | init_tree(); 508 | readFromDiskAll(); 509 | } 510 | 511 | /** 512 | * Deconstrucor: free the allocated memory and write back to the disk if required. 513 | * 514 | */ 515 | template 516 | BPlusTree:: ~BPlusTree() 517 | { 518 | dropTree(root); 519 | keyCount = 0; 520 | root = NULL; 521 | level = 0; 522 | } 523 | 524 | /** 525 | * Init the tree,allocate memory for the root node. 526 | * 527 | * @return void 528 | * 529 | */ 530 | template 531 | void BPlusTree::init_tree() 532 | { 533 | root = new TreeNode(degree,true); 534 | keyCount = 0; 535 | level = 1; 536 | nodeCount = 1; 537 | leafHead = root; 538 | } 539 | 540 | /** 541 | * Search the node to its leaf level to find the node contains the key 542 | * 543 | * @param Node 544 | * @param KeyType& 545 | * @param searchNodeParse& return the searching information by reference 546 | * 547 | * @return 548 | * 549 | */ 550 | template 551 | void BPlusTree::findToLeaf(Node pNode,KeyType key,searchNodeParse & snp) 552 | { 553 | size_t index = 0; 554 | if(pNode->search(key,index)) // find the key in the node 555 | { 556 | if(pNode->isLeaf) 557 | { 558 | snp.pNode = pNode; 559 | snp.index = index; 560 | snp.ifFound = true; 561 | } 562 | else // the node is not a leaf, continue search until the leaf level 563 | { 564 | pNode = pNode -> childs[index + 1]; 565 | while(!pNode->isLeaf) 566 | { 567 | pNode = pNode->childs[0]; 568 | } 569 | snp.pNode = pNode; 570 | snp.index = 0; 571 | snp.ifFound = true; 572 | } 573 | 574 | } 575 | else // can not find the key in the node 576 | { 577 | if(pNode->isLeaf) 578 | { 579 | snp.pNode = pNode; 580 | snp.index = index; 581 | snp.ifFound = false; 582 | } 583 | else 584 | { 585 | findToLeaf(pNode->childs[index],key,snp); 586 | } 587 | } 588 | } 589 | 590 | /** 591 | * Insert the key in right position.Then, adjust the whole tree for the rules of b+ tree. 592 | * 593 | * @param KeyType& 594 | * @param offsetNumber the value 595 | * 596 | * @return bool the flag that the insertion successes or not 597 | * 598 | */ 599 | template 600 | bool BPlusTree::insertKey(KeyType &key,offsetNumber val) 601 | { 602 | searchNodeParse snp; 603 | if(!root) init_tree(); 604 | findToLeaf(root,key,snp); 605 | if(snp.ifFound) 606 | { 607 | cout << "Error:in insert key to index: the duplicated key!" << endl; 608 | return false; 609 | } 610 | else 611 | { 612 | snp.pNode->add(key,val); 613 | if(snp.pNode->count == degree) 614 | { 615 | adjustAfterinsert(snp.pNode); 616 | } 617 | keyCount ++; 618 | return true; 619 | } 620 | } 621 | 622 | /** 623 | * Adjust the node after insertion. Rrecursively call this function itself if the father node contradicts the rules. 624 | * 625 | * @param Node the pointer pointing to the node 626 | * 627 | * @return bool the flag 628 | * 629 | */ 630 | template 631 | bool BPlusTree::adjustAfterinsert(Node pNode) 632 | { 633 | KeyType key; 634 | Node newNode = pNode->splite(key); 635 | nodeCount ++; 636 | 637 | if(pNode->isRoot()) // the node is the root 638 | { 639 | Node root = new TreeNode(degree,false); 640 | if(root == NULL) 641 | { 642 | cout << "Error: can not allocate memory for the new root in adjustAfterinsert" << endl; 643 | exit(1); 644 | } 645 | else 646 | { 647 | level ++; 648 | nodeCount ++; 649 | this->root = root; 650 | pNode->parent = root; 651 | newNode->parent = root; 652 | root->add(key); 653 | root->childs[0] = pNode; 654 | root->childs[1] = newNode; 655 | return true; 656 | } 657 | }// end root 658 | else // if it is not the root 659 | { 660 | Node parent = pNode->parent; 661 | size_t index = parent->add(key); 662 | 663 | parent->childs[index+1] = newNode; 664 | newNode->parent = parent; 665 | if(parent->count == degree) 666 | return adjustAfterinsert(parent); 667 | 668 | return true; 669 | } 670 | } 671 | 672 | /** 673 | * Search the tree to find the value of specific key 674 | * 675 | * @param KeyType& 676 | * 677 | * @return offsetnumber. The value of the key, -1 means not found in the tree 678 | * 679 | */ 680 | template 681 | offsetNumber BPlusTree::search(KeyType& key) 682 | { 683 | if(!root) return -1; 684 | searchNodeParse snp; 685 | findToLeaf(root, key, snp); 686 | if(!snp.ifFound) 687 | { 688 | return -1; // Don't find the key in the tree; 689 | } 690 | else 691 | { 692 | return snp.pNode->vals[snp.index]; 693 | } 694 | 695 | } 696 | 697 | /** 698 | * Delete the key-value or key-child by the inputed key.Then adjust the whole tree if required. 699 | * 700 | * @param KeyType& 701 | * 702 | * @return bool the flag 703 | * 704 | */ 705 | template 706 | bool BPlusTree::deleteKey(KeyType &key) 707 | { 708 | searchNodeParse snp; 709 | if(!root) 710 | { 711 | cout << "ERROR: In deleteKey, no nodes in the tree " << fileName << "!" << endl; 712 | return false; 713 | } 714 | else 715 | { 716 | findToLeaf(root, key, snp); 717 | if(!snp.ifFound) 718 | { 719 | cout << "ERROR: In deleteKey, no keys in the tree " << fileName << "!" << endl; 720 | return false; 721 | } 722 | else // find the key in the leaf node 723 | { 724 | if(snp.pNode->isRoot()) 725 | { 726 | snp.pNode->removeAt(snp.index); 727 | keyCount --; 728 | return adjustAfterDelete(snp.pNode); 729 | } 730 | else 731 | { 732 | if(snp.index == 0 && leafHead != snp.pNode) // the key exist in the branch. 733 | { 734 | // go to upper level to update the branch level 735 | size_t index = 0; 736 | 737 | Node now_parent = snp.pNode->parent; 738 | bool if_found_inBranch = now_parent->search(key,index); 739 | while(!if_found_inBranch) 740 | { 741 | if(now_parent->parent) 742 | now_parent = now_parent->parent; 743 | else 744 | { 745 | break; 746 | } 747 | if_found_inBranch = now_parent->search(key,index); 748 | }// end of search in the branch 749 | 750 | now_parent -> keys[index] = snp.pNode->keys[1]; 751 | 752 | snp.pNode->removeAt(snp.index); 753 | keyCount--; 754 | return adjustAfterDelete(snp.pNode); 755 | 756 | } 757 | else //this key must just exist in the leaf too. 758 | { 759 | snp.pNode->removeAt(snp.index); 760 | keyCount--; 761 | return adjustAfterDelete(snp.pNode); 762 | } 763 | } 764 | } 765 | } 766 | } 767 | 768 | /** 769 | * Adjust the node after deletion. Rrecursively call this function itself if the father node contradicts the rules. 770 | * 771 | * @param Node the pointer pointing to the node 772 | * 773 | * @return bool the flag 774 | * 775 | */ 776 | template 777 | bool BPlusTree::adjustAfterDelete(Node pNode) 778 | { 779 | size_t minmumKey = (degree - 1) / 2; 780 | if(((pNode->isLeaf)&&(pNode->count >= minmumKey)) || ((degree != 3)&&(!pNode->isLeaf)&&(pNode->count >= minmumKey - 1)) || ((degree ==3)&&(!pNode->isLeaf)&&(pNode->count < 0))) // do not need to adjust 781 | { 782 | return true; 783 | } 784 | if(pNode->isRoot()) 785 | { 786 | if(pNode->count > 0) //do not need to adjust 787 | { 788 | return true; 789 | } 790 | else 791 | { 792 | if(root->isLeaf) //the true will be an empty tree 793 | { 794 | delete pNode; 795 | root = NULL; 796 | leafHead = NULL; 797 | level --; 798 | nodeCount --; 799 | } 800 | else // root will be the leafhead 801 | { 802 | root = pNode -> childs[0]; 803 | root -> parent = NULL; 804 | delete pNode; 805 | level --; 806 | nodeCount --; 807 | } 808 | } 809 | }// end root 810 | else 811 | { 812 | Node parent = pNode->parent,brother = NULL; 813 | if(pNode->isLeaf) 814 | { 815 | size_t index = 0; 816 | parent->search(pNode->keys[0],index); 817 | 818 | if((parent->childs[0] != pNode) && (index + 1 == parent->count)) //choose the left brother to merge or replace 819 | { 820 | brother = parent->childs[index]; 821 | if(brother->count > minmumKey) // choose the most right key of brother to add to the left hand of the pnode 822 | { 823 | for(size_t i = pNode->count;i > 0;i --) 824 | { 825 | pNode->keys[i] = pNode->keys[i-1]; 826 | pNode->vals[i] = pNode->vals[i-1]; 827 | } 828 | pNode->keys[0] = brother->keys[brother->count-1]; 829 | pNode->vals[0] = brother->vals[brother->count-1]; 830 | brother->removeAt(brother->count-1); 831 | 832 | pNode->count ++; 833 | parent->keys[index] = pNode->keys[0]; 834 | return true; 835 | 836 | } // end add 837 | else // merge the node with its brother 838 | { 839 | parent->removeAt(index); 840 | 841 | for(int i = 0;i < pNode->count;i ++) 842 | { 843 | brother->keys[i+brother->count] = pNode->keys[i]; 844 | brother->vals[i+brother->count] = pNode->vals[i]; 845 | } 846 | brother->count += pNode->count; 847 | brother->nextLeafNode = pNode->nextLeafNode; 848 | 849 | delete pNode; 850 | nodeCount --; 851 | 852 | return adjustAfterDelete(parent); 853 | }// end merge 854 | 855 | }// end of the left brother 856 | else // choose the right brother 857 | { 858 | if(parent->childs[0] == pNode) 859 | brother = parent->childs[1]; 860 | else 861 | brother = parent->childs[index+2]; 862 | if(brother->count > minmumKey)//// choose the most left key of brother to add to the right hand of the node 863 | { 864 | pNode->keys[pNode->count] = brother->keys[0]; 865 | pNode->vals[pNode->count] = brother->vals[0]; 866 | pNode->count ++; 867 | brother->removeAt(0); 868 | if(parent->childs[0] == pNode) 869 | parent->keys[0] = brother->keys[0]; 870 | else 871 | parent->keys[index+1] = brother->keys[0]; 872 | return true; 873 | 874 | }// end add 875 | else // merge the node with its brother 876 | { 877 | for(int i = 0;i < brother->count;i ++) 878 | { 879 | pNode->keys[pNode->count+i] = brother->keys[i]; 880 | pNode->vals[pNode->count+i] = brother->vals[i]; 881 | } 882 | if(pNode == parent->childs[0]) 883 | parent->removeAt(0); 884 | else 885 | parent->removeAt(index+1); 886 | pNode->count += brother->count; 887 | pNode->nextLeafNode = brother->nextLeafNode; 888 | delete brother; 889 | nodeCount --; 890 | 891 | return adjustAfterDelete(parent); 892 | }//end merge 893 | }// end of the right brother 894 | 895 | }// end leaf 896 | else // branch node 897 | { 898 | size_t index = 0; 899 | parent->search(pNode->childs[0]->keys[0],index); 900 | if((parent->childs[0] != pNode) && (index + 1 == parent->count)) // choose the left brother to merge or replace 901 | { 902 | brother = parent->childs[index]; 903 | if(brother->count > minmumKey - 1) // choose the most right key and child to add to the left hand of the pnode 904 | { 905 | //modify the pnode 906 | pNode->childs[pNode->count+1] = pNode->childs[pNode->count]; 907 | for(size_t i = pNode->count;i > 0;i --) 908 | { 909 | pNode->childs[i] = pNode->childs[i-1]; 910 | pNode->keys[i] = pNode->keys[i-1]; 911 | } 912 | pNode->childs[0] = brother->childs[brother->count]; 913 | pNode->keys[0] = parent->keys[index]; 914 | pNode->count ++; 915 | //modify the father 916 | parent->keys[index]= brother->keys[brother->count-1]; 917 | //modify the brother and child 918 | if(brother->childs[brother->count]) 919 | { 920 | brother->childs[brother->count]->parent = pNode; 921 | } 922 | brother->removeAt(brother->count-1); 923 | 924 | return true; 925 | 926 | }// end add 927 | else // merge the node with its brother 928 | { 929 | //modify the brother and child 930 | brother->keys[brother->count] = parent->keys[index]; 931 | parent->removeAt(index); 932 | brother->count ++; 933 | 934 | for(int i = 0;i < pNode->count;i ++) 935 | { 936 | brother->childs[brother->count+i] = pNode->childs[i]; 937 | brother->keys[brother->count+i] = pNode->keys[i]; 938 | brother->childs[brother->count+i]-> parent= brother; 939 | } 940 | brother->childs[brother->count+pNode->count] = pNode->childs[pNode->count]; 941 | brother->childs[brother->count+pNode->count]->parent = brother; 942 | 943 | brother->count += pNode->count; 944 | 945 | 946 | delete pNode; 947 | nodeCount --; 948 | 949 | return adjustAfterDelete(parent); 950 | } 951 | 952 | }// end of the left brother 953 | else // choose the right brother 954 | { 955 | if(parent->childs[0] == pNode) 956 | brother = parent->childs[1]; 957 | else 958 | brother = parent->childs[index+2]; 959 | if(brother->count > minmumKey - 1)// choose the most left key and child to add to the right hand of the pnode 960 | { 961 | //modifty the pnode and child 962 | pNode->childs[pNode->count+1] = brother->childs[0]; 963 | pNode->keys[pNode->count] = brother->keys[0]; 964 | pNode->childs[pNode->count+1]->parent = pNode; 965 | pNode->count ++; 966 | //modify the fater 967 | if(pNode == parent->childs[0]) 968 | parent->keys[0] = brother->keys[0]; 969 | else 970 | parent->keys[index+1] = brother->keys[0]; 971 | //modify the brother 972 | brother->childs[0] = brother->childs[1]; 973 | brother->removeAt(0); 974 | 975 | return true; 976 | } 977 | else // merge the node with its brother 978 | { 979 | //modify the pnode and child 980 | pNode->keys[pNode->count] = parent->keys[index]; 981 | 982 | if(pNode == parent->childs[0]) 983 | parent->removeAt(0); 984 | else 985 | parent->removeAt(index+1); 986 | 987 | pNode->count ++; 988 | 989 | for(int i = 0;i < brother->count;i++) 990 | { 991 | pNode->childs[pNode->count+i] = brother->childs[i]; 992 | pNode->keys[pNode->count+i] = brother->keys[i]; 993 | pNode->childs[pNode->count+i]->parent = pNode; 994 | } 995 | pNode->childs[pNode->count+brother->count] = brother->childs[brother->count]; 996 | pNode->childs[pNode->count+brother->count]->parent = pNode; 997 | 998 | pNode->count += brother->count; 999 | 1000 | 1001 | delete brother; 1002 | nodeCount --; 1003 | 1004 | return adjustAfterDelete(parent); 1005 | 1006 | } 1007 | 1008 | } 1009 | 1010 | } 1011 | 1012 | } 1013 | return false; 1014 | } 1015 | 1016 | /** 1017 | * Drop the tree whose root is the inputed node. 1018 | * 1019 | * @param Node the pointer pointing to the node 1020 | * 1021 | * @return void 1022 | * 1023 | */ 1024 | template 1025 | void BPlusTree::dropTree(Node node) 1026 | { 1027 | if(!node) return; 1028 | if(!node->isLeaf) //if it has child 1029 | { 1030 | for(size_t i=0;i <= node->count;i++) 1031 | { 1032 | dropTree(node->childs[i] ); 1033 | node->childs[i] = NULL; 1034 | } 1035 | } 1036 | delete node; 1037 | nodeCount --; 1038 | return; 1039 | } 1040 | 1041 | /** 1042 | * Read the whole existing tree from the disk. 1043 | * 1044 | * @return void 1045 | * 1046 | */ 1047 | template 1048 | void BPlusTree::readFromDiskAll() 1049 | { 1050 | file = bm.getFile(fileName.c_str()); 1051 | blockNode* btmp = bm.getBlockHead(file); 1052 | while (true) 1053 | { 1054 | if (btmp == NULL) 1055 | { 1056 | return; 1057 | } 1058 | 1059 | readFromDisk(btmp); 1060 | if(btmp->ifbottom) break; 1061 | btmp = bm.getNextBlock(file, btmp); 1062 | } 1063 | 1064 | } 1065 | 1066 | /** 1067 | * Read a node from the disk. 1068 | * 1069 | * @param blockNode* 1070 | * 1071 | * @return void 1072 | * 1073 | */ 1074 | template 1075 | void BPlusTree::readFromDisk(blockNode* btmp) 1076 | { 1077 | int valueSize = sizeof(offsetNumber); 1078 | char* indexBegin = bm.get_content(*btmp); 1079 | char* valueBegin = indexBegin + keySize; 1080 | KeyType key; 1081 | offsetNumber value; 1082 | 1083 | while(valueBegin - bm.get_content(*btmp) < bm.get_usingSize(*btmp)) 1084 | // there are available position in the block 1085 | { 1086 | key = *(KeyType*)indexBegin; 1087 | value = *(offsetNumber*)valueBegin; 1088 | insertKey(key, value); 1089 | valueBegin += keySize + valueSize; 1090 | indexBegin += keySize + valueSize; 1091 | } 1092 | 1093 | } 1094 | 1095 | /** 1096 | * Write the whole tree data to the disk. 1097 | * 1098 | * @return void 1099 | * 1100 | */ 1101 | template 1102 | void BPlusTree::writtenbackToDiskAll() 1103 | { 1104 | blockNode* btmp = bm.getBlockHead(file); 1105 | Node ntmp = leafHead; 1106 | int valueSize = sizeof(offsetNumber); 1107 | while(ntmp != NULL) 1108 | { 1109 | bm.set_usingSize(*btmp, 0); 1110 | bm.set_dirty(*btmp); 1111 | for(int i = 0;i < ntmp->count;i ++) 1112 | { 1113 | char* key = (char*)&(ntmp->keys[i]); 1114 | char* value = (char*)&(ntmp->vals[i]); 1115 | memcpy(bm.get_content(*btmp)+bm.get_usingSize(*btmp),key,keySize); 1116 | bm.set_usingSize(*btmp, bm.get_usingSize(*btmp) + keySize); 1117 | memcpy(bm.get_content(*btmp)+bm.get_usingSize(*btmp),value,valueSize); 1118 | bm.set_usingSize(*btmp, bm.get_usingSize(*btmp) + valueSize); 1119 | } 1120 | 1121 | btmp = bm.getNextBlock(file, btmp); 1122 | ntmp = ntmp->nextLeafNode; 1123 | } 1124 | while(1)// delete the empty node 1125 | { 1126 | if(btmp->ifbottom) 1127 | break; 1128 | bm.set_usingSize(*btmp, 0); 1129 | bm.set_dirty(*btmp); 1130 | btmp = bm.getNextBlock(file, btmp); 1131 | } 1132 | 1133 | } 1134 | 1135 | #ifdef _DEBUG 1136 | 1137 | template 1138 | void BPlusTree::debug_print() 1139 | { 1140 | cout << "############DEBUG FOR THE TREE############" << endl; 1141 | cout << "name:" << fileName << " root:" << (void*)root << " leafHead:" << (void * )leafHead << " keycount:" << keyCount << " level:" << level << " nodeCount:" << nodeCount << endl; 1142 | 1143 | if(root) 1144 | debug_print_node(root); 1145 | cout << "#############END OF DEBUG FOR TREE########" << endl; 1146 | 1147 | } 1148 | 1149 | 1150 | template 1151 | void BPlusTree::debug_print_node(Node pNode) 1152 | { 1153 | pNode->debug_print(); 1154 | if(!pNode->isLeaf) 1155 | for(int i = 0 ;i < pNode->count+1;i ++) 1156 | debug_print_node(pNode->childs[i]); 1157 | 1158 | } 1159 | #endif 1160 | 1161 | 1162 | #endif /* defined(__Minisql__BPlusTree__) */ 1163 | -------------------------------------------------------------------------------- /code/BufferManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // BufferManager.cpp 3 | // Minisql 4 | // 5 | // Created by xuyuhao on 14/11/1. 6 | // Copyright (c) 2014年 xuyuhao. All rights reserved. 7 | // 8 | 9 | #include "BufferManager.h" 10 | #include "Minisql.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /** 17 | * Constructor Function: allocate memories for the pools 18 | * and init the variable values 19 | * 20 | */ 21 | BufferManager::BufferManager():total_block(0),total_file(0),fileHead(NULL) 22 | { 23 | for (int i = 0; i < MAX_FILE_NUM; i ++) 24 | { 25 | file_pool[i].fileName = new char[MAX_FILE_NAME]; 26 | if(file_pool[i].fileName == NULL) 27 | { 28 | printf("Can not allocate memory in initing the file pool!\n"); 29 | exit (1); 30 | } 31 | init_file(file_pool[i]); 32 | } 33 | for (int i = 0; i < MAX_BLOCK_NUM; i ++) { 34 | block_pool[i].address = new char[BLOCK_SIZE]; 35 | if(block_pool[i].address == NULL) 36 | { 37 | printf("Can not allocate memory in initing the block pool!\n"); 38 | exit (1); 39 | } 40 | block_pool[i].fileName = new char[MAX_FILE_NAME]; 41 | if(block_pool[i].fileName == NULL) 42 | { 43 | printf("Can not allocate memory in initing the block pool!\n"); 44 | exit (1); 45 | } 46 | init_block(block_pool[i]); 47 | } 48 | } 49 | 50 | /** 51 | * Destructor Function: free memories of the pools 52 | * and write back the buffer content if required 53 | * 54 | */ 55 | BufferManager::~BufferManager() 56 | { 57 | writtenBackToDiskAll(); 58 | for (int i = 0; i < MAX_FILE_NUM; i ++) 59 | { 60 | delete [] file_pool[i].fileName; 61 | } 62 | for (int i = 0; i < MAX_BLOCK_NUM; i ++) 63 | { 64 | delete [] block_pool[i].address; 65 | } 66 | } 67 | 68 | /** 69 | * init the fileNode values 70 | * 71 | * @param fileNode& the file your want to init 72 | * 73 | * @return void 74 | * 75 | */ 76 | void BufferManager::init_file(fileNode &file) 77 | { 78 | file.nextFile = NULL; 79 | file.preFile = NULL; 80 | file.blockHead = NULL; 81 | file.pin = false; 82 | memset(file.fileName,0,MAX_FILE_NAME); 83 | } 84 | 85 | /** 86 | * init the blockNode values 87 | * 88 | * @param blockNode& the block your want to init 89 | * 90 | * @return void 91 | * 92 | */ 93 | void BufferManager::init_block(blockNode &block) 94 | { 95 | memset(block.address,0,BLOCK_SIZE); 96 | size_t init_usage = 0; 97 | memcpy(block.address, (char*)&init_usage, sizeof(size_t)); // set the block head 98 | block.using_size = sizeof(size_t); 99 | block.dirty = false; 100 | block.nextBlock = NULL; 101 | block.preBlock = NULL; 102 | block.offsetNum = -1; 103 | block.pin = false; 104 | block.reference = false; 105 | block.ifbottom = false; 106 | memset(block.fileName,0,MAX_FILE_NAME); 107 | } 108 | 109 | /** 110 | * Get a fileNode 111 | * 1.If the file is already in the list, return this fileNode 112 | * 2.If the file is not in the list, replace some fileNode, if required, to make more space 113 | * 114 | * @param const char* the file name 115 | * @param bool if true, the file will be locked. 116 | * 117 | * @return fileNode* 118 | * 119 | */ 120 | fileNode* BufferManager::getFile(const char * fileName, bool if_pin) 121 | { 122 | blockNode * btmp = NULL; 123 | fileNode * ftmp = NULL; 124 | if(fileHead != NULL) 125 | { 126 | for(ftmp = fileHead;ftmp != NULL;ftmp = ftmp->nextFile) 127 | { 128 | if(!strcmp(fileName, ftmp->fileName)) //the fileNode is already in the list 129 | { 130 | ftmp->pin = if_pin; 131 | return ftmp; 132 | } 133 | } 134 | } 135 | // The fileNode is not in the list 136 | if(total_file == 0) // No file in the list now 137 | { 138 | ftmp = &file_pool[total_file]; 139 | total_file ++; 140 | fileHead = ftmp; 141 | } 142 | else if(total_file < MAX_FILE_NUM) // There are empty fileNode in the pool 143 | { 144 | ftmp = &file_pool[total_file]; 145 | // add this fileNode into the tail of the list 146 | file_pool[total_file-1].nextFile = ftmp; 147 | ftmp->preFile = &file_pool[total_file-1]; 148 | total_file ++; 149 | } 150 | else // if total_file >= MAX_FILE_NUM, find one fileNode to replace, write back the block node belonging to the fileNode 151 | { 152 | ftmp = fileHead; 153 | while(ftmp->pin) 154 | { 155 | if(ftmp -> nextFile)ftmp = ftmp->nextFile; 156 | else //no enough file node in the pool 157 | { 158 | printf("There are no enough file node in the pool!"); 159 | exit(2); 160 | } 161 | } 162 | for(btmp = ftmp->blockHead;btmp != NULL;btmp = btmp->nextBlock) 163 | { 164 | if(btmp->preBlock) 165 | { 166 | init_block(*(btmp->preBlock)); 167 | total_block --; 168 | } 169 | writtenBackToDisk(btmp->fileName,btmp); 170 | } 171 | init_file(*ftmp); 172 | } 173 | if(strlen(fileName) + 1 > MAX_FILE_NAME) 174 | { 175 | printf("文件名长度过长,最高不能超过%d\n",MAX_FILE_NAME); 176 | exit(3); 177 | } 178 | strncpy(ftmp->fileName, fileName,MAX_FILE_NAME); 179 | set_pin(*ftmp, if_pin); 180 | return ftmp; 181 | } 182 | 183 | /** 184 | * Get a block node 185 | * 1.If the block is already in the list, return this blockNode 186 | * 2.If the block is not in the list, replace some fileNode, using LRU replacement, if required, to make more space. 187 | * 3.Only if the block is dirty, he will be written back to disk when been reaplaced. 188 | * 189 | * @param fileNode * the file you want to add the block into. 190 | * @param blockNode the position that the Node will added to. 191 | * @param bool if true, the block will be locked. 192 | * 193 | * @return blockNode* 194 | * 195 | */ 196 | blockNode* BufferManager::getBlock(fileNode * file,blockNode *position, bool if_pin) 197 | { 198 | const char * fileName = file->fileName; 199 | blockNode * btmp = NULL; 200 | if(total_block == 0) 201 | { 202 | btmp = &block_pool[0]; 203 | total_block ++; 204 | } 205 | else if(total_block < MAX_BLOCK_NUM) // there are empty blocks in the block pool 206 | { 207 | for(int i = 0 ;i < MAX_BLOCK_NUM;i ++) 208 | { 209 | if(block_pool[i].offsetNum == -1) 210 | { 211 | btmp = &block_pool[i]; 212 | total_block ++; 213 | break; 214 | } 215 | else 216 | continue; 217 | } 218 | } 219 | else // total_block >= MAX_BLOCK_NUM,which means that there are no empty block so we must replace one. 220 | { 221 | int i = replaced_block; 222 | while (true) 223 | { 224 | i ++; 225 | if(i >= total_block) i = 0; 226 | if(!block_pool[i].pin) 227 | { 228 | if(block_pool[i].reference == true) 229 | block_pool[i].reference = false; 230 | else //choose this block for replacement 231 | { 232 | btmp = &block_pool[i]; 233 | if(btmp->nextBlock) btmp -> nextBlock -> preBlock = btmp -> preBlock; 234 | if(btmp->preBlock) btmp -> preBlock -> nextBlock = btmp -> nextBlock; 235 | if(file->blockHead == btmp) file->blockHead = btmp->nextBlock; 236 | replaced_block = i; //record the replaced block and begin from the next block the next time. 237 | 238 | writtenBackToDisk(btmp->fileName, btmp); 239 | init_block(*btmp); 240 | break; 241 | } 242 | } 243 | else // this block is been locked 244 | continue; 245 | } 246 | } 247 | //add the block into the block list 248 | if(position != NULL && position->nextBlock == NULL) 249 | { 250 | btmp -> preBlock = position; 251 | position -> nextBlock = btmp; 252 | btmp -> offsetNum = position -> offsetNum + 1; 253 | } 254 | else if (position !=NULL && position->nextBlock != NULL) 255 | { 256 | btmp->preBlock=position; 257 | btmp->nextBlock=position->nextBlock; 258 | position->nextBlock->preBlock=btmp; 259 | position->nextBlock=btmp; 260 | btmp -> offsetNum = position -> offsetNum + 1; 261 | } 262 | else // the block will be the head of the list 263 | { 264 | btmp -> offsetNum = 0; 265 | if(file->blockHead) // if the file has a wrong block head 266 | { 267 | file->blockHead -> preBlock = btmp; 268 | btmp->nextBlock = file->blockHead; 269 | } 270 | file->blockHead = btmp; 271 | } 272 | set_pin(*btmp, if_pin); 273 | if(strlen(fileName) + 1 > MAX_FILE_NAME) 274 | { 275 | printf("文件名长度过长,最高不能超过%d\n",MAX_FILE_NAME); 276 | exit(3); 277 | } 278 | strncpy(btmp->fileName, fileName, MAX_FILE_NAME); 279 | 280 | //read the file content to the block 281 | FILE * fileHandle; 282 | if((fileHandle = fopen(fileName, "ab+")) != NULL) 283 | { 284 | if(fseek(fileHandle, btmp->offsetNum*BLOCK_SIZE, 0) == 0) 285 | { 286 | if(fread(btmp->address, 1, BLOCK_SIZE, fileHandle)==0) 287 | btmp->ifbottom = true; 288 | btmp ->using_size = getUsingSize(btmp); 289 | } 290 | else 291 | { 292 | printf("Problem seeking the file %s in reading",fileName); 293 | exit(1); 294 | } 295 | fclose(fileHandle); 296 | } 297 | else 298 | { 299 | printf("Problem opening the file %s in reading",fileName); 300 | exit(1); 301 | } 302 | return btmp; 303 | } 304 | 305 | /** 306 | * Flush the block node to the disk, if the block is not dirty, do nothing. 307 | * 308 | * @param const char* the file name 309 | * @param blockNode& the block your want to flush 310 | * 311 | * @return void 312 | * 313 | */ 314 | void BufferManager::writtenBackToDisk(const char* fileName,blockNode* block) 315 | { 316 | if(!block->dirty) // this block is not been modified, so it do not need to written back to files 317 | { 318 | return; 319 | } 320 | else // written back to the file 321 | { 322 | FILE * fileHandle = NULL; 323 | if((fileHandle = fopen(fileName, "rb+")) != NULL) 324 | { 325 | if(fseek(fileHandle, block->offsetNum*BLOCK_SIZE, 0) == 0) 326 | { 327 | if(fwrite(block->address, block->using_size+sizeof(size_t), 1, fileHandle) != 1) 328 | { 329 | printf("Problem writing the file %s in writtenBackToDisking",fileName); 330 | exit(1); 331 | } 332 | } 333 | else 334 | { 335 | printf("Problem seeking the file %s in writtenBackToDisking",fileName); 336 | exit(1); 337 | } 338 | fclose(fileHandle); 339 | } 340 | else 341 | { 342 | printf("Problem opening the file %s in writtenBackToDisking",fileName); 343 | exit(1); 344 | } 345 | } 346 | } 347 | 348 | /** 349 | * Flush all block node in the list to the disk 350 | * 351 | * @return void 352 | * 353 | */ 354 | void BufferManager::writtenBackToDiskAll() 355 | { 356 | blockNode *btmp = NULL; 357 | fileNode *ftmp = NULL; 358 | if(fileHead) 359 | { 360 | for(ftmp = fileHead;ftmp != NULL;ftmp = ftmp ->nextFile) 361 | { 362 | if(ftmp->blockHead) 363 | { 364 | for(btmp = ftmp->blockHead;btmp != NULL;btmp = btmp->nextBlock) 365 | { 366 | if(btmp->preBlock)init_block(*(btmp -> preBlock)); 367 | writtenBackToDisk(btmp->fileName, btmp); 368 | } 369 | } 370 | } 371 | } 372 | } 373 | 374 | /** 375 | * Get the next block node of the node inputed 376 | * 377 | * @param fileNode* the file you want to add a block 378 | * @param blockNode& the block your want to be added to 379 | * 380 | * @return blockNode* 381 | * 382 | */ 383 | blockNode* BufferManager::getNextBlock(fileNode* file,blockNode* block) 384 | { 385 | if(block->nextBlock == NULL) 386 | { 387 | if(block->ifbottom) block->ifbottom = false; 388 | return getBlock(file, block); 389 | } 390 | else //block->nextBlock != NULL 391 | { 392 | if(block->offsetNum == block->nextBlock->offsetNum - 1) 393 | { 394 | return block->nextBlock; 395 | } 396 | else //the block list is not in the right order 397 | { 398 | return getBlock(file, block); 399 | } 400 | } 401 | } 402 | 403 | /** 404 | * Get the head block of the file 405 | * 406 | * @param fileNode* 407 | * 408 | * @return blockNode* 409 | * 410 | */ 411 | blockNode* BufferManager::getBlockHead(fileNode* file) 412 | { 413 | blockNode* btmp = NULL; 414 | if(file->blockHead != NULL) 415 | { 416 | if(file->blockHead->offsetNum == 0) //The right offset of the first block 417 | { 418 | btmp = file->blockHead; 419 | } 420 | else 421 | { 422 | btmp = getBlock(file, NULL); 423 | } 424 | } 425 | else// If the file have no block head, get a new block node for it 426 | { 427 | btmp = getBlock(file,NULL); 428 | } 429 | return btmp; 430 | } 431 | 432 | /** 433 | * Get the block of the file by offset number 434 | * 435 | * @param fileNode* 436 | * @param int offsetNumber 437 | * 438 | * @return blockNode* 439 | * 440 | */ 441 | blockNode* BufferManager::getBlockByOffset(fileNode* file, int offsetNumber) 442 | { 443 | blockNode* btmp = NULL; 444 | if(offsetNumber == 0) return getBlockHead(file); 445 | else 446 | { 447 | btmp = getBlockHead(file); 448 | while( offsetNumber > 0) 449 | { 450 | btmp = getNextBlock(file, btmp); 451 | offsetNumber --; 452 | } 453 | return btmp; 454 | } 455 | } 456 | 457 | /** 458 | * delete the file node and its block node 459 | * 460 | * @param const char * fileName 461 | * 462 | * @return 463 | */ 464 | void BufferManager::delete_fileNode(const char * fileName) 465 | { 466 | fileNode* ftmp = getFile(fileName); 467 | blockNode* btmp = getBlockHead(ftmp); 468 | queue blockQ; 469 | while (true) { 470 | if(btmp == NULL) return; 471 | blockQ.push(btmp); 472 | if(btmp->ifbottom) break; 473 | btmp = getNextBlock(ftmp,btmp); 474 | } 475 | total_block -= blockQ.size(); 476 | while(!blockQ.empty()) 477 | { 478 | init_block(*blockQ.back()); 479 | blockQ.pop(); 480 | } 481 | if(ftmp->preFile) ftmp->preFile->nextFile = ftmp->nextFile; 482 | if(ftmp->nextFile) ftmp->nextFile->preFile = ftmp->preFile; 483 | if(fileHead == ftmp) fileHead = ftmp->nextFile; 484 | init_file(*ftmp); 485 | total_file --; 486 | } 487 | 488 | 489 | void BufferManager::set_pin(blockNode &block,bool pin) 490 | { 491 | block.pin = pin; 492 | if(!pin) 493 | block.reference = true; 494 | } 495 | 496 | void BufferManager::set_pin(fileNode &file,bool pin) 497 | { 498 | file.pin = pin; 499 | } 500 | 501 | /** 502 | * Set the block a dirty node 503 | * Must call this function if modify the block node 504 | * 505 | * @param blockNode& the block your want to be added modify 506 | * 507 | * @return void 508 | * 509 | */ 510 | void BufferManager::set_dirty(blockNode &block) 511 | { 512 | block.dirty = true; 513 | } 514 | 515 | void BufferManager::clean_dirty(blockNode &block) 516 | { 517 | block.dirty = false; 518 | } 519 | 520 | 521 | size_t BufferManager::getUsingSize(blockNode* block) 522 | { 523 | return *(size_t*)block->address; 524 | } 525 | 526 | void BufferManager::set_usingSize(blockNode & block,size_t usage) 527 | { 528 | block.using_size = usage; 529 | memcpy(block.address,(char*)&usage,sizeof(size_t)); 530 | } 531 | 532 | size_t BufferManager::get_usingSize(blockNode & block) 533 | { 534 | return block.using_size; 535 | } 536 | 537 | /** 538 | * 539 | * Get the content of the block except the block head 540 | * 541 | * @param blockNode& 542 | * 543 | * @return 544 | * 545 | */ 546 | char* BufferManager::get_content(blockNode& block) 547 | { 548 | return block.address + sizeof(size_t); 549 | } 550 | 551 | 552 | 553 | 554 | 555 | -------------------------------------------------------------------------------- /code/BufferManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // BufferManager.h 3 | // Minisql 4 | // Description: Provide funtions to manage the buffers. 5 | // 6 | // Created by xuyuhao on 14/11/1. 7 | // Copyright (c) 2014年 xuyuhao. All rights reserved. 8 | // 9 | 10 | #ifndef __Minisql__BufferManager__ 11 | #define __Minisql__BufferManager__ 12 | #include "Minisql.h" 13 | #include 14 | 15 | static int replaced_block = -1; 16 | 17 | 18 | class BufferManager 19 | { 20 | private: 21 | fileNode *fileHead; 22 | fileNode file_pool[MAX_FILE_NUM]; 23 | blockNode block_pool[MAX_BLOCK_NUM]; 24 | int total_block; // the number of block that have been used, which means the block is in the list. 25 | int total_file; // the number of file that have been used, which means the file is in the list. 26 | void init_block(blockNode & block); 27 | void init_file(fileNode & file); 28 | blockNode* getBlock(fileNode * file,blockNode* position,bool if_pin = false); 29 | void writtenBackToDiskAll(); 30 | void writtenBackToDisk(const char* fileName,blockNode* block); 31 | void clean_dirty(blockNode &block); 32 | size_t getUsingSize(blockNode* block); 33 | static const int BLOCK_SIZE = 4096; 34 | 35 | public: 36 | BufferManager(); 37 | ~BufferManager(); 38 | void delete_fileNode(const char * fileName); 39 | fileNode* getFile(const char* fileName,bool if_pin = false); 40 | void set_dirty(blockNode & block); 41 | void set_pin(blockNode & block,bool pin); 42 | void set_pin(fileNode & file,bool pin); 43 | void set_usingSize(blockNode & block,size_t usage); 44 | size_t get_usingSize(blockNode & block); 45 | char* get_content(blockNode& block); 46 | static int getBlockSize() //Get the size of the block that others can use.Others cannot use the block head 47 | { 48 | return BLOCK_SIZE - sizeof(size_t); 49 | } 50 | 51 | 52 | blockNode* getNextBlock(fileNode * file,blockNode* block); 53 | blockNode* getBlockHead(fileNode* file); 54 | blockNode* getBlockByOffset(fileNode* file, int offestNumber); 55 | 56 | }; 57 | 58 | #endif /* defined(__Minisql__BufferManager__) */ 59 | 60 | 61 | -------------------------------------------------------------------------------- /code/CatalogManager.cpp: -------------------------------------------------------------------------------- 1 | #include "CatalogManager.h" 2 | #include "BufferManager.h" 3 | #include "IndexInfo.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Attribute.h" 10 | #define UNKNOWN_FILE 8 11 | #define TABLE_FILE 9 12 | #define INDEX_FILE 10 13 | CatalogManager::CatalogManager() { 14 | // TODO Auto-generated constructor stub 15 | 16 | } 17 | 18 | CatalogManager::~CatalogManager() { 19 | // TODO Auto-generated destructor stub 20 | } 21 | 22 | int CatalogManager::dropTable(string tableName) 23 | { 24 | bm.delete_fileNode(tableName.c_str()); 25 | if (remove(tableName.c_str())) 26 | { 27 | return 0; 28 | } 29 | return 1; 30 | } 31 | int CatalogManager::getIndexType(string indexName) 32 | { 33 | fileNode *ftmp = bm.getFile("Indexs"); 34 | blockNode *btmp = bm.getBlockHead(ftmp); 35 | if (btmp ) 36 | { 37 | char* addressBegin; 38 | addressBegin = bm.get_content(*btmp); 39 | IndexInfo * i = (IndexInfo *)addressBegin; 40 | for(int j = 0 ;j<(bm.get_usingSize(*btmp)/sizeof(IndexInfo));j++) 41 | { 42 | if((*i).indexName==indexName) 43 | { 44 | return i->type; 45 | } 46 | i ++; 47 | } 48 | return -2; 49 | } 50 | 51 | return -2; 52 | } 53 | 54 | int CatalogManager::getAllIndex(vector * indexs) 55 | { 56 | fileNode *ftmp = bm.getFile("Indexs"); 57 | blockNode *btmp = bm.getBlockHead(ftmp); 58 | if (btmp ) 59 | { 60 | char* addressBegin; 61 | addressBegin = bm.get_content(*btmp); 62 | IndexInfo * i = (IndexInfo *)addressBegin; 63 | for(int j = 0 ;j<(bm.get_usingSize(*btmp)/sizeof(IndexInfo));j++) 64 | { 65 | indexs->push_back((*i)); 66 | i ++; 67 | } 68 | } 69 | 70 | return 1; 71 | } 72 | int CatalogManager::addIndex(string indexName,string tableName,string Attribute,int type) 73 | { 74 | fileNode *ftmp = bm.getFile("Indexs"); 75 | blockNode *btmp = bm.getBlockHead(ftmp); 76 | IndexInfo i(indexName,tableName,Attribute,type); 77 | while (true) 78 | { 79 | if (btmp == NULL) 80 | { 81 | return 0; 82 | } 83 | if (bm.get_usingSize(*btmp) <= bm.getBlockSize() - sizeof(IndexInfo)) 84 | { 85 | 86 | char* addressBegin; 87 | addressBegin = bm.get_content(*btmp) + bm.get_usingSize(*btmp); 88 | memcpy(addressBegin, &i, sizeof(IndexInfo)); 89 | bm.set_usingSize(*btmp, bm.get_usingSize(*btmp) + sizeof(IndexInfo)); 90 | bm.set_dirty(*btmp); 91 | 92 | 93 | return this->setIndexOnAttribute(tableName,Attribute,indexName); 94 | } 95 | else 96 | { 97 | btmp = bm.getNextBlock(ftmp, btmp); 98 | } 99 | } 100 | 101 | return 0; 102 | } 103 | int CatalogManager::findTable(string tableName) 104 | { 105 | FILE *fp; 106 | fp = fopen(tableName.c_str(), "r"); 107 | if (fp == NULL) 108 | { 109 | return 0; 110 | } 111 | else 112 | { 113 | fclose(fp); 114 | return TABLE_FILE; 115 | } 116 | 117 | } 118 | int CatalogManager::findIndex(string fileName) 119 | { 120 | fileNode *ftmp = bm.getFile("Indexs"); 121 | blockNode *btmp = bm.getBlockHead(ftmp); 122 | if (btmp ) 123 | { 124 | char* addressBegin; 125 | addressBegin = bm.get_content(*btmp); 126 | IndexInfo * i = (IndexInfo *)addressBegin; 127 | int flag = UNKNOWN_FILE; 128 | for(int j = 0 ;j<(bm.get_usingSize(*btmp)/sizeof(IndexInfo));j++) 129 | { 130 | if((*i).indexName==fileName) 131 | { 132 | flag = INDEX_FILE; 133 | break; 134 | } 135 | i ++; 136 | } 137 | return flag; 138 | } 139 | 140 | return 0; 141 | } 142 | int CatalogManager::dropIndex(string index) 143 | { 144 | fileNode *ftmp = bm.getFile("Indexs"); 145 | blockNode *btmp = bm.getBlockHead(ftmp); 146 | if (btmp) 147 | { 148 | char* addressBegin; 149 | addressBegin = bm.get_content(*btmp); 150 | IndexInfo * i = (IndexInfo *)addressBegin; 151 | int j = 0; 152 | for(j = 0 ;j<(bm.get_usingSize(*btmp)/sizeof(IndexInfo));j++) 153 | { 154 | if((*i).indexName==index) 155 | { 156 | break; 157 | } 158 | i ++; 159 | } 160 | this->revokeIndexOnAttribute((*i).tableName,(*i).Attribute,(*i).indexName); 161 | for(;j<(bm.get_usingSize(*btmp)/sizeof(IndexInfo)-1);j++) 162 | { 163 | (*i) = *(i + sizeof(IndexInfo)); 164 | i ++; 165 | } 166 | bm.set_usingSize(*btmp, bm.get_usingSize(*btmp) - sizeof(IndexInfo)); 167 | bm.set_dirty(*btmp); 168 | 169 | return 1; 170 | } 171 | 172 | return 0; 173 | } 174 | int CatalogManager::revokeIndexOnAttribute(string tableName,string AttributeName,string indexName) 175 | { 176 | fileNode *ftmp = bm.getFile(tableName.c_str()); 177 | blockNode *btmp = bm.getBlockHead(ftmp); 178 | 179 | if (btmp) 180 | { 181 | 182 | char* addressBegin = bm.get_content(*btmp) ; 183 | addressBegin += (1+sizeof(int)); 184 | int size = *addressBegin; 185 | addressBegin++; 186 | Attribute *a = (Attribute *)addressBegin; 187 | int i; 188 | for(i =0;iname == AttributeName) 191 | { 192 | if(a->index == indexName) 193 | { 194 | a->index = ""; 195 | bm.set_dirty(*btmp); 196 | } 197 | else 198 | { 199 | cout<<"revoke unknown index: "<index<<"!"<* indexNameVector) 214 | { 215 | fileNode *ftmp = bm.getFile("Indexs"); 216 | blockNode *btmp = bm.getBlockHead(ftmp); 217 | if (btmp ) 218 | { 219 | char* addressBegin; 220 | addressBegin = bm.get_content(*btmp); 221 | IndexInfo * i = (IndexInfo *)addressBegin; 222 | for(int j = 0 ;j<(bm.get_usingSize(*btmp)/sizeof(IndexInfo));j++) 223 | { 224 | if((*i).tableName==tableName) 225 | { 226 | (*indexNameVector).push_back((*i).indexName); 227 | } 228 | i ++; 229 | } 230 | return 1; 231 | } 232 | 233 | return 0; 234 | } 235 | 236 | int CatalogManager::deleteValue(string tableName, int deleteNum) 237 | { 238 | fileNode *ftmp = bm.getFile(tableName.c_str()); 239 | blockNode *btmp = bm.getBlockHead(ftmp); 240 | 241 | if (btmp) 242 | { 243 | 244 | char* addressBegin = bm.get_content(*btmp) ; 245 | int * recordNum = (int*)addressBegin; 246 | if((*recordNum) * attributeVector, string primaryKeyName = "",int primaryKeyLocation = 0) 291 | { 292 | FILE *fp; 293 | fp = fopen(tableName.c_str(), "w+"); 294 | if (fp == NULL) 295 | { 296 | return 0; 297 | } 298 | fclose(fp); 299 | fileNode *ftmp = bm.getFile(tableName.c_str()); 300 | blockNode *btmp = bm.getBlockHead(ftmp); 301 | 302 | if (btmp ) 303 | { 304 | char* addressBegin = bm.get_content(*btmp) ; 305 | int * size = (int*)addressBegin; 306 | *size = 0;// 0 record number 307 | addressBegin += sizeof(int); 308 | *addressBegin = primaryKeyLocation;//1 as what it says 309 | addressBegin++; 310 | *addressBegin = (*attributeVector).size();// 2 attribute number 311 | addressBegin++; 312 | //memcpy(addressBegin, attributeVector, (*attributeVector).size()*sizeof(Attribute)); 313 | for(int i= 0;i<(*attributeVector).size();i++) 314 | { 315 | memcpy(addressBegin, &((*attributeVector)[i]), sizeof(Attribute)); 316 | addressBegin += sizeof(Attribute); 317 | } 318 | bm.set_usingSize(*btmp, bm.get_usingSize(*btmp) + (*attributeVector).size()*sizeof(Attribute)+2+sizeof(int)); 319 | bm.set_dirty(*btmp); 320 | return 1; 321 | } 322 | return 0; 323 | } 324 | int CatalogManager::setIndexOnAttribute(string tableName,string AttributeName,string indexName) 325 | { 326 | fileNode *ftmp = bm.getFile(tableName.c_str()); 327 | blockNode *btmp = bm.getBlockHead(ftmp); 328 | 329 | if (btmp) 330 | { 331 | 332 | char* addressBegin = bm.get_content(*btmp) ; 333 | addressBegin += 1+sizeof(int); 334 | int size = *addressBegin; 335 | addressBegin++; 336 | Attribute *a = (Attribute *)addressBegin; 337 | int i; 338 | for(i =0;iname == AttributeName) 341 | { 342 | a->index = indexName; 343 | bm.set_dirty(*btmp); 344 | break; 345 | } 346 | a ++; 347 | } 348 | if(i* attributeVector) 356 | { 357 | fileNode *ftmp = bm.getFile(tableName.c_str()); 358 | blockNode *btmp = bm.getBlockHead(ftmp); 359 | 360 | if (btmp) 361 | { 362 | 363 | char* addressBegin = bm.get_content(*btmp) ; 364 | addressBegin += 1+sizeof(int); 365 | int size = *addressBegin; 366 | addressBegin++; 367 | Attribute *a = (Attribute *)addressBegin; 368 | for(int i =0;ipush_back(*a); 371 | a ++; 372 | } 373 | 374 | return 1; 375 | } 376 | return 0; 377 | } 378 | 379 | int CatalogManager::calcuteLenth(string tableName) 380 | { 381 | fileNode *ftmp = bm.getFile(tableName.c_str()); 382 | blockNode *btmp = bm.getBlockHead(ftmp); 383 | 384 | if (btmp) 385 | { 386 | int singleRecordSize = 0; 387 | char* addressBegin = bm.get_content(*btmp) ; 388 | addressBegin += 1+sizeof(int); 389 | int size = *addressBegin; 390 | addressBegin++; 391 | Attribute *a = (Attribute *)addressBegin; 392 | for(int i =0;i0) 403 | { 404 | singleRecordSize += (*a).type * sizeof(char); 405 | } 406 | else 407 | { 408 | cout<<"Catalog data damaged!"<* recordContent, char* recordResult) 434 | { 435 | vector attributeVector; 436 | attributeGet(tableName, &attributeVector); 437 | char * contentBegin = recordResult; 438 | 439 | for(int i = 0; i < attributeVector.size(); i++) 440 | { 441 | Attribute attribute = attributeVector[i]; 442 | string content = (*recordContent)[i]; 443 | int type = attribute.type; 444 | int typeSize = calcuteLenth2(type); 445 | stringstream ss; 446 | ss << content; 447 | if (type == Attribute::TYPE_INT) 448 | { 449 | //if the content is a int 450 | int intTmp; 451 | ss >> intTmp; 452 | memcpy(contentBegin, ((char*)&intTmp), typeSize); 453 | } 454 | else if (type == Attribute::TYPE_FLOAT) 455 | { 456 | //if the content is a float 457 | float floatTmp; 458 | ss >> floatTmp; 459 | memcpy(contentBegin, ((char*)&floatTmp), typeSize); 460 | } 461 | else 462 | { 463 | //if the content is a string 464 | memcpy(contentBegin, content.c_str(), typeSize); 465 | } 466 | 467 | contentBegin += typeSize; 468 | } 469 | return ; 470 | } 471 | -------------------------------------------------------------------------------- /code/CatalogManager.h: -------------------------------------------------------------------------------- 1 | #ifndef CATALOGMANAGER_H 2 | #define CATALOGMANAGER_H 3 | 4 | #include 5 | #include 6 | #include "Attribute.h" 7 | #include "BufferManager.h" 8 | #include "IndexInfo.h" 9 | using namespace std; 10 | class CatalogManager { 11 | public: 12 | BufferManager bm; 13 | CatalogManager(); 14 | virtual ~CatalogManager(); 15 | int addIndex(string indexName,string tableName,string attributeName,int type); 16 | int revokeIndexOnAttribute(string tableName,string AttributeName,string indexName); 17 | int findTable(string tableName); 18 | int findIndex(string indexName); 19 | int dropTable(string tableName); 20 | int dropIndex(string index); 21 | int deleteValue(string tableName, int deleteNum);// delete the number of record 22 | int insertRecord(string tableName, int recordNum); // increment the number of record 23 | int getRecordNum(string tableName); 24 | int indexNameListGet(string tableName, vector* indexNameVector); 25 | int getAllIndex(vector * indexs); 26 | int setIndexOnAttribute(string tableName,string AttributeName,string indexName); 27 | int addTable(string tableName, vector* attributeVector, string primaryKeyName ,int primaryKeyLocation ); 28 | int getIndexType(string indexName); 29 | int attributeGet(string tableName, vector* attributeVector); 30 | int calcuteLenth(string tableName); 31 | int calcuteLenth2(int type); 32 | void recordStringGet(string tableName, vector* recordContent, char* recordResult); 33 | }; 34 | 35 | 36 | 37 | 38 | #endif -------------------------------------------------------------------------------- /code/Condition.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Condition.cpp 3 | // minisql 4 | // 5 | // Created by 邓永辉 on 14/11/10. 6 | // Copyright (c) 2014年 邓永辉. All rights reserved. 7 | // 8 | 9 | #include "Condition.h" 10 | bool Condition::ifRight(int content) 11 | { 12 | stringstream ss; 13 | ss << value; 14 | int myContent; 15 | ss >> myContent; 16 | 17 | switch (operate) 18 | { 19 | case Condition::OPERATOR_EQUAL: 20 | return content == myContent; 21 | break; 22 | case Condition::OPERATOR_NOT_EQUAL: 23 | return content != myContent; 24 | break; 25 | case Condition::OPERATOR_LESS: 26 | return content < myContent; 27 | break; 28 | case Condition::OPERATOR_MORE: 29 | return content > myContent; 30 | break; 31 | case Condition::OPERATOR_LESS_EQUAL: 32 | return content <= myContent; 33 | break; 34 | case Condition::OPERATOR_MORE_EQUAL: 35 | return content >= myContent; 36 | break; 37 | default: 38 | return true; 39 | break; 40 | } 41 | } 42 | 43 | bool Condition::ifRight(float content) 44 | { 45 | stringstream ss; 46 | ss << value; 47 | float myContent; 48 | ss >> myContent; 49 | 50 | switch (operate) 51 | { 52 | case Condition::OPERATOR_EQUAL: 53 | return content == myContent; 54 | break; 55 | case Condition::OPERATOR_NOT_EQUAL: 56 | return content != myContent; 57 | break; 58 | case Condition::OPERATOR_LESS: 59 | return content < myContent; 60 | break; 61 | case Condition::OPERATOR_MORE: 62 | return content > myContent; 63 | break; 64 | case Condition::OPERATOR_LESS_EQUAL: 65 | return content <= myContent; 66 | break; 67 | case Condition::OPERATOR_MORE_EQUAL: 68 | return content >= myContent; 69 | break; 70 | default: 71 | return true; 72 | break; 73 | } 74 | } 75 | 76 | bool Condition::ifRight(string content) 77 | { 78 | string myContent = value; 79 | switch (operate) 80 | { 81 | case Condition::OPERATOR_EQUAL: 82 | return content == myContent; 83 | break; 84 | case Condition::OPERATOR_NOT_EQUAL: 85 | return content != myContent; 86 | break; 87 | case Condition::OPERATOR_LESS: 88 | return content < myContent; 89 | break; 90 | case Condition::OPERATOR_MORE: 91 | return content > myContent; 92 | break; 93 | case Condition::OPERATOR_LESS_EQUAL: 94 | return content <= myContent; 95 | break; 96 | case Condition::OPERATOR_MORE_EQUAL: 97 | return content >= myContent; 98 | break; 99 | default: 100 | return true; 101 | break; 102 | } 103 | } 104 | 105 | Condition::Condition(string a,string v,int o) { 106 | attributeName = a; 107 | value = v; 108 | operate = o; 109 | } 110 | 111 | -------------------------------------------------------------------------------- /code/Condition.h: -------------------------------------------------------------------------------- 1 | // 2 | // Condition.h 3 | // minisql 4 | // 5 | // Created by 邓永辉 on 14/11/4. 6 | // Copyright (c) 2014年 邓永辉. All rights reserved. 7 | // 8 | 9 | #ifndef minisql_Condition_h 10 | #define minisql_Condition_h 11 | #include 12 | #include 13 | using namespace std; 14 | 15 | class Condition 16 | { 17 | 18 | public: 19 | const static int OPERATOR_EQUAL = 0; // "=" 20 | const static int OPERATOR_NOT_EQUAL = 1; // "<>" 21 | const static int OPERATOR_LESS = 2; // "<" 22 | const static int OPERATOR_MORE = 3; // ">" 23 | const static int OPERATOR_LESS_EQUAL = 4; // "<=" 24 | const static int OPERATOR_MORE_EQUAL = 5; // ">=" 25 | 26 | Condition(string a,string v,int o); 27 | 28 | string attributeName; 29 | string value; // the value to be compared 30 | int operate; // the type to be compared 31 | 32 | bool ifRight(int content); 33 | bool ifRight(float content); 34 | bool ifRight(string content); 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /code/IndexInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef minisql_IndexInfo_h 2 | #define minisql_IndexInfo_h 3 | 4 | #include 5 | using namespace std; 6 | 7 | class IndexInfo 8 | { 9 | public: 10 | IndexInfo(string i,string t,string a,int ty) 11 | {indexName = i;tableName = t;Attribute = a;type = ty;} 12 | string indexName; 13 | string tableName; 14 | string Attribute; 15 | int type; 16 | }; 17 | 18 | #endif -------------------------------------------------------------------------------- /code/IndexManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // IndexManager.cpp 3 | // Minisql 4 | // 5 | // Created by xuyuhao on 14/11/14. 6 | // Copyright (c) 2014年 xuyuhao. All rights reserved. 7 | // 8 | 9 | #include "IndexManager.h" 10 | #include 11 | #include "API.h" 12 | #include "IndexInfo.h" 13 | #include 14 | using namespace std; 15 | 16 | 17 | /** 18 | * Constructor Function: create the existing index by the index files. 19 | * 20 | * @param API* 21 | */ 22 | IndexManager::IndexManager(API *m_api) 23 | { 24 | api = m_api; 25 | vector allIndexInfo; 26 | api->allIndexAddressInfoGet(&allIndexInfo); 27 | for(vector::iterator i = allIndexInfo.begin();i != allIndexInfo.end();i ++) 28 | { 29 | createIndex(i->indexName, i->type); 30 | } 31 | } 32 | 33 | 34 | /** 35 | * Destructor Function: write the dirty indexs back to the disk. 36 | * 37 | */ 38 | IndexManager::~IndexManager() 39 | { 40 | //write back to the disk 41 | for(intMap::iterator itInt = indexIntMap.begin();itInt != indexIntMap.end(); itInt ++) 42 | { 43 | if(itInt->second) 44 | { 45 | itInt -> second->writtenbackToDiskAll(); 46 | delete itInt->second; 47 | } 48 | } 49 | for(stringMap::iterator itString = indexStringMap.begin();itString != indexStringMap.end(); itString ++) 50 | { 51 | if(itString->second) 52 | { 53 | itString ->second-> writtenbackToDiskAll(); 54 | delete itString->second; 55 | } 56 | } 57 | for(floatMap::iterator itFloat = indexFloatMap.begin();itFloat != indexFloatMap.end(); itFloat ++) 58 | { 59 | if(itFloat->second) 60 | { 61 | itFloat ->second-> writtenbackToDiskAll(); 62 | delete itFloat->second; 63 | } 64 | } 65 | } 66 | 67 | 68 | /** 69 | * Create index on the specific type. 70 | * If there exists the index before, read data from file path and then rebuild the b+ tree. 71 | * 72 | * @param string the file path 73 | * @param int type 74 | * 75 | * @return void 76 | * 77 | */ 78 | void IndexManager::createIndex(string filePath,int type) 79 | { 80 | int keySize = getKeySize(type); 81 | int degree = getDegree(type); 82 | if(type == TYPE_INT) 83 | { 84 | BPlusTree *tree = new BPlusTree(filePath,keySize,degree); 85 | indexIntMap.insert(intMap::value_type(filePath, tree)); 86 | } 87 | else if(type == TYPE_FLOAT) 88 | { 89 | BPlusTree *tree = new BPlusTree(filePath,keySize,degree); 90 | indexFloatMap.insert(floatMap::value_type(filePath, tree)); 91 | } 92 | else // string 93 | { 94 | BPlusTree *tree = new BPlusTree(filePath,keySize,degree); 95 | indexStringMap.insert(stringMap::value_type(filePath, tree)); 96 | } 97 | 98 | } 99 | 100 | /** 101 | * Drop the specific index. 102 | * 103 | * @param 104 | * 105 | * @return void 106 | * 107 | */ 108 | void IndexManager::dropIndex(string filePath,int type) 109 | { 110 | if(type == TYPE_INT) 111 | { 112 | intMap::iterator itInt = indexIntMap.find(filePath); 113 | if(itInt == indexIntMap.end()) 114 | { 115 | cout << "Error:in drop index, no index " << filePath <<" exits" << endl; 116 | return; 117 | } 118 | else 119 | { 120 | delete itInt->second; 121 | indexIntMap.erase(itInt); 122 | } 123 | } 124 | else if(type == TYPE_FLOAT) 125 | { 126 | floatMap::iterator itFloat = indexFloatMap.find(filePath); 127 | if(itFloat == indexFloatMap.end()) 128 | { 129 | cout << "Error:in drop index, no index " << filePath <<" exits" << endl; 130 | return; 131 | } 132 | else 133 | { 134 | delete itFloat->second; 135 | indexFloatMap.erase(itFloat); 136 | } 137 | } 138 | else // string 139 | { 140 | stringMap::iterator itString = indexStringMap.find(filePath); 141 | if(itString == indexStringMap.end()) 142 | { 143 | cout << "Error:in drop index, no index " << filePath <<" exits" << endl; 144 | return; 145 | } 146 | else 147 | { 148 | delete itString->second; 149 | indexStringMap.erase(itString); 150 | } 151 | } 152 | 153 | } 154 | 155 | /** 156 | * Search the b+ tree by the key and return the value of that key. 157 | * If the key is not in the index, return -1. 158 | * 159 | * @param string the file path. 160 | * @param string key. 161 | * @param int type 162 | * 163 | * @return void 164 | * 165 | */ 166 | offsetNumber IndexManager::searchIndex(string filePath,string key,int type) 167 | { 168 | setKey(type, key); 169 | 170 | if(type == TYPE_INT) 171 | { 172 | intMap::iterator itInt = indexIntMap.find(filePath); 173 | if(itInt == indexIntMap.end()) 174 | { 175 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 176 | return -1; 177 | } 178 | else 179 | { 180 | return itInt->second->search(kt.intTmp); 181 | } 182 | } 183 | else if(type == TYPE_FLOAT) 184 | { 185 | floatMap::iterator itFloat = indexFloatMap.find(filePath); 186 | if(itFloat == indexFloatMap.end()) 187 | { 188 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 189 | return -1; 190 | } 191 | else 192 | { 193 | return itFloat->second->search(kt.floatTmp); 194 | 195 | } 196 | } 197 | else // string 198 | { 199 | stringMap::iterator itString = indexStringMap.find(filePath); 200 | if(itString == indexStringMap.end()) 201 | { 202 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 203 | return -1; 204 | } 205 | else 206 | { 207 | return itString->second->search(key); 208 | } 209 | } 210 | } 211 | 212 | /** 213 | * Insert the key and value to the b+ tree. 214 | * 215 | * @param string the file path 216 | * @param string key 217 | * @param offsetNumber the block offset number 218 | * @param int type 219 | * 220 | * @return void 221 | * 222 | */ 223 | void IndexManager::insertIndex(string filePath,string key,offsetNumber blockOffset,int type) 224 | { 225 | setKey(type, key); 226 | 227 | if(type == TYPE_INT) 228 | { 229 | intMap::iterator itInt = indexIntMap.find(filePath); 230 | if(itInt == indexIntMap.end()) 231 | { 232 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 233 | return; 234 | } 235 | else 236 | { 237 | itInt->second->insertKey(kt.intTmp,blockOffset); 238 | } 239 | } 240 | else if(type == TYPE_FLOAT) 241 | { 242 | floatMap::iterator itFloat = indexFloatMap.find(filePath); 243 | if(itFloat == indexFloatMap.end()) 244 | { 245 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 246 | return; 247 | } 248 | else 249 | { 250 | itFloat->second->insertKey(kt.floatTmp,blockOffset); 251 | 252 | } 253 | } 254 | else // string 255 | { 256 | stringMap::iterator itString = indexStringMap.find(filePath); 257 | if(itString == indexStringMap.end()) 258 | { 259 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 260 | return; 261 | } 262 | else 263 | { 264 | itString->second->insertKey(key,blockOffset); 265 | } 266 | } 267 | } 268 | 269 | /** 270 | * Delete the key and its value 271 | * 272 | * @param string file path 273 | * @param int type 274 | * 275 | * @return void 276 | * 277 | */ 278 | void IndexManager::deleteIndexByKey(string filePath,string key,int type) 279 | { 280 | setKey(type, key); 281 | 282 | if(type == TYPE_INT) 283 | { 284 | intMap::iterator itInt = indexIntMap.find(filePath); 285 | if(itInt == indexIntMap.end()) 286 | { 287 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 288 | return; 289 | } 290 | else 291 | { 292 | itInt->second->deleteKey(kt.intTmp); 293 | } 294 | } 295 | else if(type == TYPE_FLOAT) 296 | { 297 | floatMap::iterator itFloat = indexFloatMap.find(filePath); 298 | if(itFloat == indexFloatMap.end()) 299 | { 300 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 301 | return; 302 | } 303 | else 304 | { 305 | itFloat->second->deleteKey(kt.floatTmp); 306 | 307 | } 308 | } 309 | else // string 310 | { 311 | stringMap::iterator itString = indexStringMap.find(filePath); 312 | if(itString == indexStringMap.end()) 313 | { 314 | cout << "Error:in search index, no index " << filePath <<" exits" << endl; 315 | return; 316 | } 317 | else 318 | { 319 | itString->second->deleteKey(key); 320 | } 321 | } 322 | } 323 | 324 | /** 325 | * Get the degree by the type. 326 | * The tree node size equals to the block size. 327 | * 328 | * @param int type 329 | * 330 | * @return int the degree 331 | * 332 | */ 333 | int IndexManager::getDegree(int type) 334 | { 335 | int degree = bm.getBlockSize()/(getKeySize(type)+sizeof(offsetNumber)); 336 | if(degree %2 == 0) degree -= 1; 337 | return degree; 338 | } 339 | 340 | /** 341 | * Get the key size by the type 342 | * 343 | * @param int type 344 | * 345 | * @return int the key size 346 | * 347 | */ 348 | int IndexManager::getKeySize(int type) 349 | { 350 | if(type == TYPE_FLOAT) 351 | return sizeof(float); 352 | else if(type == TYPE_INT) 353 | return sizeof(int); 354 | else if(type > 0) 355 | return type + 1; 356 | else 357 | { 358 | cout << "ERROR: in getKeySize: invalid type" << endl; 359 | return -100; 360 | } 361 | } 362 | 363 | /** 364 | * Get the key of its type by the inputed string. 365 | * Users input string whatever type the key is. 366 | * 367 | * @param int type 368 | * @param string key 369 | * 370 | * @return void 371 | * 372 | */ 373 | void IndexManager::setKey(int type,string key) 374 | { 375 | stringstream ss; 376 | ss << key; 377 | if(type == this->TYPE_INT) 378 | ss >> this->kt.intTmp; 379 | else if(type == this->TYPE_FLOAT) 380 | ss >> this->kt.floatTmp; 381 | else if(type > 0) 382 | ss >> this->kt.stringTmp; 383 | else 384 | cout << "Error: in getKey: invalid type" << endl; 385 | 386 | } 387 | 388 | 389 | 390 | 391 | 392 | 393 | -------------------------------------------------------------------------------- /code/IndexManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // IndexManager.h 3 | // Minisql 4 | // Description: Provide functions to manage the indexs. 5 | // 6 | // Created by xuyuhao on 14/11/14. 7 | // Copyright (c) 2014年 xuyuhao. All rights reserved. 8 | // 9 | 10 | #ifndef __Minisql__IndexManager__ 11 | #define __Minisql__IndexManager__ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "Attribute.h" 18 | #include "BPlusTree.h" 19 | 20 | class API; 21 | 22 | class IndexManager{ 23 | private: 24 | typedef map *> intMap; 25 | typedef map *> stringMap; 26 | typedef map *> floatMap; 27 | 28 | int static const TYPE_FLOAT = Attribute::TYPE_FLOAT; 29 | int static const TYPE_INT = Attribute::TYPE_INT; 30 | // other values mean the size of the char.Eg, 4 means char(4); 31 | 32 | API *api; // to call the functions of API 33 | 34 | intMap indexIntMap; 35 | stringMap indexStringMap; 36 | floatMap indexFloatMap; 37 | struct keyTmp{ 38 | int intTmp; 39 | float floatTmp; 40 | string stringTmp; 41 | }; // the struct to help to convert the inputed string to specfied type 42 | struct keyTmp kt; 43 | 44 | int getDegree(int type); 45 | 46 | int getKeySize(int type); 47 | 48 | void setKey(int type,string key); 49 | 50 | 51 | public: 52 | IndexManager(API *api); 53 | ~IndexManager(); 54 | 55 | void createIndex(string filePath,int type); 56 | 57 | void dropIndex(string filePath,int type); 58 | 59 | offsetNumber searchIndex(string filePath,string key,int type); 60 | 61 | void insertIndex(string filePath,string key,offsetNumber blockOffset,int type); 62 | 63 | void deleteIndexByKey(string filePath,string key,int type); 64 | }; 65 | 66 | 67 | 68 | #endif /* defined(__Minisql__IndexManager__) */ 69 | -------------------------------------------------------------------------------- /code/Interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include "Interpreter.h" 2 | #include "Condition.h" 3 | #include "Attribute.h" 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | class SyntaxException{}; 10 | 11 | int Interpreter::interpreter(string s) 12 | { 13 | 14 | int tmp=0; 15 | string word; 16 | 17 | word = getWord(s, &tmp); 18 | if (strcmp(word.c_str(), "create") == 0) 19 | { 20 | word = getWord(s,&tmp); 21 | 22 | if (strcmp(word.c_str(), "table") == 0) 23 | { 24 | string primaryKey = ""; 25 | string tableName = ""; 26 | word = getWord(s,&tmp); 27 | if (!word.empty()) //create table tablename 28 | tableName = word; 29 | else 30 | { 31 | cout<<"Syntax Error for no table name"< attributeVector; 45 | while (!word.empty() && strcmp(word.c_str(),"primary") != 0 && strcmp(word.c_str(),")") != 0) 46 | { 47 | string attributeName = word; 48 | int type = 0; 49 | bool ifUnique = false; 50 | // deal with the data type 51 | word = getWord(s,&tmp); 52 | if (strcmp(word.c_str(), "int") == 0) 53 | type = 0; 54 | else if(strcmp(word.c_str(), "float") == 0) 55 | type = -1; 56 | else if(strcmp(word.c_str(), "char") == 0) 57 | { 58 | word = getWord(s,&tmp); 59 | if(strcmp(word.c_str(),"(")) 60 | { 61 | cout<<"Syntax Error: unknown data type"<> type) ) 67 | { 68 | cout<<"Syntax error : illegal number in char()"<tableCreate(tableName,&attributeVector,primaryKey,primaryKeyLocation); 162 | return 1; 163 | } 164 | } 165 | else if (strcmp(word.c_str(), "index") == 0) 166 | { 167 | string indexName = ""; 168 | string tableName = ""; 169 | string attributeName = ""; 170 | word = getWord(s,&tmp); 171 | if (!word.empty()) //create index indexname 172 | indexName = word; 173 | else 174 | { 175 | cout<<"Error in syntax!"<indexCreate(indexName,tableName,attributeName); 198 | return 1; 199 | } catch(SyntaxException&) { 200 | cout<<"Syntax Error!"< attrSelected; 217 | string tableName = ""; 218 | word = getWord(s,&tmp); 219 | if (strcmp(word.c_str(), "*") != 0) // only accept select * 220 | { 221 | while(strcmp(word.c_str(), "from") != 0) 222 | { 223 | attrSelected.push_back(word); 224 | word = getWord(s,&tmp); 225 | } 226 | } 227 | else 228 | { 229 | word = getWord(s,&tmp); 230 | } 231 | if (strcmp(word.c_str(), "from") != 0) 232 | { 233 | cout<<"Error in syntax!"<recordShow(tableName); 252 | } 253 | else 254 | ap->recordShow(tableName,&attrSelected); 255 | return 1; 256 | } 257 | else if (strcmp(word.c_str(),"where") == 0) 258 | { 259 | string attributeName = ""; 260 | string value = ""; 261 | int operate = Condition::OPERATOR_EQUAL; 262 | std::vector conditionVector; 263 | word = getWord(s,&tmp); //col1 264 | while(1){ 265 | try { 266 | if(word.empty()) 267 | throw SyntaxException(); 268 | attributeName = word ; 269 | word = getWord(s,&tmp); 270 | if (strcmp(word.c_str(),"<=") == 0) 271 | operate = Condition::OPERATOR_LESS_EQUAL; 272 | else if (strcmp(word.c_str(),">=") == 0) 273 | operate = Condition::OPERATOR_MORE_EQUAL; 274 | else if (strcmp(word.c_str(),"<") == 0) 275 | operate = Condition::OPERATOR_LESS; 276 | else if (strcmp(word.c_str(),">") == 0) 277 | operate = Condition::OPERATOR_MORE; 278 | else if (strcmp(word.c_str(),"=") == 0) 279 | operate = Condition::OPERATOR_EQUAL; 280 | else if (strcmp(word.c_str(),"<>") == 0) 281 | operate = Condition::OPERATOR_NOT_EQUAL; 282 | else 283 | throw SyntaxException(); 284 | word = getWord(s,&tmp); 285 | if(word.empty()) // no condition 286 | throw SyntaxException(); 287 | value = word; 288 | Condition c(attributeName,value,operate); 289 | conditionVector.push_back(c); 290 | word = getWord(s,&tmp); 291 | if(word.empty()) // no condition 292 | break; 293 | if (strcmp(word.c_str(),"and") != 0) 294 | throw SyntaxException(); 295 | word = getWord(s,&tmp); 296 | } catch (SyntaxException&) { 297 | cout<<"Syntax Error!"<recordShow(tableName,NULL,&conditionVector); 303 | else 304 | ap->recordShow(tableName, &attrSelected,&conditionVector); 305 | 306 | return 1; 307 | } 308 | } 309 | 310 | 311 | 312 | else if (strcmp(word.c_str(), "drop")==0) 313 | { 314 | word = getWord(s,&tmp); 315 | 316 | if (strcmp(word.c_str(), "table") == 0) 317 | { 318 | word = getWord(s,&tmp); 319 | if (!word.empty()) 320 | { 321 | ap->tableDrop(word); 322 | return 1; 323 | } 324 | else 325 | { 326 | cout<<"Error in syntax!"<indexDrop(word); 336 | return 1; 337 | } 338 | else 339 | { 340 | cout<<"Error in syntax!"<recordDelete(tableName); 376 | return 1; 377 | } 378 | else if (strcmp(word.c_str(),"where") == 0) 379 | { 380 | string attributeName = ""; 381 | string value = ""; 382 | int operate = Condition::OPERATOR_EQUAL; 383 | std::vector conditionVector; 384 | word = getWord(s,&tmp); //col1 385 | while(1){ 386 | try { 387 | if(word.empty()) 388 | throw SyntaxException(); 389 | attributeName = word ; 390 | word = getWord(s,&tmp); 391 | if (strcmp(word.c_str(),"<=") == 0) 392 | operate = Condition::OPERATOR_LESS_EQUAL; 393 | else if (strcmp(word.c_str(),">=") == 0) 394 | operate = Condition::OPERATOR_MORE_EQUAL; 395 | else if (strcmp(word.c_str(),"<") == 0) 396 | operate = Condition::OPERATOR_LESS; 397 | else if (strcmp(word.c_str(),">") == 0) 398 | operate = Condition::OPERATOR_MORE; 399 | else if (strcmp(word.c_str(),"=") == 0) 400 | operate = Condition::OPERATOR_EQUAL; 401 | else if (strcmp(word.c_str(),"<>") == 0) 402 | operate = Condition::OPERATOR_NOT_EQUAL; 403 | else 404 | throw SyntaxException(); 405 | word = getWord(s,&tmp); 406 | if(word.empty()) // no condition 407 | throw SyntaxException(); 408 | value = word; 409 | word = getWord(s,&tmp); 410 | Condition c(attributeName,value,operate); 411 | conditionVector.push_back(c); 412 | if(word.empty()) // no condition 413 | break; 414 | if (strcmp(word.c_str(),"and") != 0) 415 | throw SyntaxException(); 416 | word = getWord(s,&tmp); 417 | } catch (SyntaxException&) { 418 | cout<<"Syntax Error!"<recordDelete(tableName, &conditionVector); 423 | return 1; 424 | } 425 | } 426 | 427 | 428 | 429 | else if (strcmp(word.c_str(), "insert") == 0) 430 | { 431 | string tableName = ""; 432 | std::vector valueVector; 433 | word = getWord(s,&tmp); 434 | try { 435 | if (strcmp(word.c_str(),"into") != 0) 436 | throw SyntaxException(); 437 | word = getWord(s,&tmp); 438 | if (word.empty()) 439 | throw SyntaxException(); 440 | tableName = word; 441 | word = getWord(s,&tmp); 442 | if (strcmp(word.c_str(),"values") != 0) 443 | throw SyntaxException(); 444 | word = getWord(s,&tmp); 445 | if (strcmp(word.c_str(),"(") != 0) 446 | throw SyntaxException(); 447 | word = getWord(s,&tmp); 448 | while (!word.empty() && strcmp(word.c_str(),")") != 0) 449 | { 450 | valueVector.push_back(word); 451 | word = getWord(s,&tmp); 452 | if (strcmp(word.c_str(),",") == 0) // bug here 453 | word = getWord(s,&tmp); 454 | } 455 | if (strcmp(word.c_str(),")") != 0) 456 | throw SyntaxException(); 457 | } catch (SyntaxException&){ 458 | cout<<"Syntax Error!"<recordInsert(tableName,&valueVector); 462 | return 1; 463 | } 464 | 465 | 466 | else if (strcmp(word.c_str(), "quit") == 0) 467 | { return 587;} 468 | 469 | else if (strcmp(word.c_str(), "commit") == 0) 470 | { return 1;} 471 | else if (strcmp(word.c_str(), "execfile") == 0) 472 | { 473 | fileName = getWord(s,&tmp); 474 | cout<<"try to open "< 7 | #include 8 | #include "API.h" 9 | using namespace std; 10 | class Interpreter{ 11 | public: 12 | 13 | API* ap; 14 | string fileName ; 15 | int interpreter(string s); 16 | 17 | string getWord(string s, int *st); 18 | 19 | Interpreter(){} 20 | ~Interpreter(){} 21 | }; 22 | 23 | #endif -------------------------------------------------------------------------------- /code/Minisql.h: -------------------------------------------------------------------------------- 1 | // 2 | // Minisql.h 3 | // Minisql 4 | // 5 | // Created by xuyuhao on 14/11/2. 6 | // Copyright (c) 2014年 xuyuhao. All rights reserved. 7 | // 8 | 9 | #ifndef Minisql_Minisql_h 10 | #define Minisql_Minisql_h 11 | #include 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | struct blockNode 17 | { 18 | int offsetNum; // the offset number in the block list 19 | bool pin; // the flag that this block is locked 20 | bool ifbottom; // flag that this is the end of the file node 21 | char* fileName; // the file which the block node belongs to 22 | friend class BufferManager; 23 | 24 | private: 25 | char *address; // the content address 26 | blockNode * preBlock; 27 | blockNode * nextBlock; 28 | bool reference; // the LRU replacement flag 29 | bool dirty; // the flag that this block is dirty, which needs to written back to the disk later 30 | size_t using_size; // the byte size that the block have used. The total size of the block is BLOCK_SIZE . This value is stored in the block head. 31 | 32 | }; 33 | 34 | struct fileNode 35 | { 36 | char *fileName; 37 | bool pin; // the flag that this file is locked 38 | blockNode *blockHead; 39 | fileNode * nextFile; 40 | fileNode * preFile; 41 | }; 42 | 43 | extern clock_t start; 44 | extern void print(); 45 | 46 | #define MAX_FILE_NUM 40 47 | #define MAX_BLOCK_NUM 300 48 | #define MAX_FILE_NAME 100 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /code/RecordManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RecordManager.cpp 3 | // minisql 4 | // 5 | // Created by 邓永辉 on 14/11/5. 6 | // Copyright (c) 2014年 邓永辉. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "RecordManager.h" 11 | #include 12 | #include "API.h" 13 | 14 | /** 15 | * 16 | * create a table 17 | * @param tableName: name of table 18 | */ 19 | int RecordManager::tableCreate(string tableName) 20 | { 21 | string tableFileName = tableFileNameGet(tableName); 22 | 23 | FILE *fp; 24 | fp = fopen(tableFileName.c_str(), "w+"); 25 | if (fp == NULL) 26 | { 27 | return 0; 28 | } 29 | fclose(fp); 30 | return 1; 31 | } 32 | 33 | /** 34 | * 35 | * drop a table 36 | * @param tableName: name of table 37 | */ 38 | int RecordManager::tableDrop(string tableName) 39 | { 40 | string tableFileName = tableFileNameGet(tableName); 41 | bm.delete_fileNode(tableFileName.c_str()); 42 | if (remove(tableFileName.c_str())) 43 | { 44 | return 0; 45 | } 46 | return 1; 47 | } 48 | 49 | /** 50 | * 51 | * create a index 52 | * @param indexName: name of index 53 | */ 54 | int RecordManager::indexCreate(string indexName) 55 | { 56 | string indexFileName = indexFileNameGet(indexName); 57 | 58 | FILE *fp; 59 | fp = fopen(indexFileName.c_str(), "w+"); 60 | if (fp == NULL) 61 | { 62 | return 0; 63 | } 64 | fclose(fp); 65 | return 1; 66 | } 67 | 68 | /** 69 | * 70 | * drop a index 71 | * @param indexName: name of index 72 | */ 73 | int RecordManager::indexDrop(string indexName) 74 | { 75 | string indexFileName = indexFileNameGet(indexName); 76 | bm.delete_fileNode(indexFileName.c_str()); 77 | if (remove(indexFileName.c_str())) 78 | { 79 | return 0; 80 | } 81 | return 1; 82 | } 83 | 84 | /** 85 | * 86 | * insert a record to table 87 | * @param tableName: name of table 88 | * @param record: value of record 89 | * @param recordSize: size of the record 90 | * @return the position of block in the file(-1 represent error) 91 | */ 92 | int RecordManager::recordInsert(string tableName,char* record, int recordSize) 93 | { 94 | fileNode *ftmp = bm.getFile(tableFileNameGet(tableName).c_str()); 95 | blockNode *btmp = bm.getBlockHead(ftmp); 96 | while (true) 97 | { 98 | if (btmp == NULL) 99 | { 100 | return -1; 101 | } 102 | if (bm.get_usingSize(*btmp) <= bm.getBlockSize() - recordSize) 103 | { 104 | 105 | char* addressBegin; 106 | addressBegin = bm.get_content(*btmp) + bm.get_usingSize(*btmp); 107 | memcpy(addressBegin, record, recordSize); 108 | bm.set_usingSize(*btmp, bm.get_usingSize(*btmp) + recordSize); 109 | bm.set_dirty(*btmp); 110 | return btmp->offsetNum; 111 | } 112 | else 113 | { 114 | btmp = bm.getNextBlock(ftmp, btmp); 115 | } 116 | } 117 | 118 | return -1; 119 | } 120 | 121 | /** 122 | * 123 | * print all record of a table meet requirement 124 | * @param tableName: name of table 125 | * @param attributeNameVector: the attribute list 126 | * @param conditionVector: the conditions list 127 | * @return int: the number of the record meet requirements(-1 represent error) 128 | */ 129 | int RecordManager::recordAllShow(string tableName, vector* attributeNameVector, vector* conditionVector) 130 | { 131 | fileNode *ftmp = bm.getFile(tableFileNameGet(tableName).c_str()); 132 | blockNode *btmp = bm.getBlockHead(ftmp); 133 | int count = 0; 134 | while (true) 135 | { 136 | if (btmp == NULL) 137 | { 138 | return -1; 139 | } 140 | if (btmp->ifbottom) 141 | { 142 | int recordBlockNum = recordBlockShow(tableName,attributeNameVector, conditionVector, btmp); 143 | count += recordBlockNum; 144 | return count; 145 | } 146 | else 147 | { 148 | int recordBlockNum = recordBlockShow(tableName, attributeNameVector, conditionVector, btmp); 149 | count += recordBlockNum; 150 | btmp = bm.getNextBlock(ftmp, btmp); 151 | } 152 | } 153 | 154 | return -1; 155 | } 156 | 157 | /** 158 | * 159 | * print record of a table in a block 160 | * @param tableName: name of table 161 | * @param attributeNameVector: the attribute list 162 | * @param conditionVector: the conditions list 163 | * @param blockOffset: the block's offsetNum 164 | * @return int: the number of the record meet requirements in the block(-1 represent error) 165 | */ 166 | int RecordManager::recordBlockShow(string tableName, vector* attributeNameVector, vector* conditionVector, int blockOffset) 167 | { 168 | fileNode *ftmp = bm.getFile(tableFileNameGet(tableName).c_str()); 169 | blockNode* block = bm.getBlockByOffset(ftmp, blockOffset); 170 | if (block == NULL) 171 | { 172 | return -1; 173 | } 174 | else 175 | { 176 | return recordBlockShow(tableName, attributeNameVector, conditionVector, block); 177 | } 178 | } 179 | 180 | /** 181 | * 182 | * print record of a table in a block 183 | * @param tableName: name of table 184 | * @param attributeNameVector: the attribute list 185 | * @param conditionVector: the conditions list 186 | * @param block: search record in the block 187 | * @return int: the number of the record meet requirements in the block(-1 represent error) 188 | */ 189 | int RecordManager::recordBlockShow(string tableName, vector* attributeNameVector, vector* conditionVector, blockNode* block) 190 | { 191 | 192 | //if block is null, return -1 193 | if (block == NULL) 194 | { 195 | return -1; 196 | } 197 | 198 | int count = 0; 199 | 200 | char* recordBegin = bm.get_content(*block); 201 | vector attributeVector; 202 | int recordSize = api->recordSizeGet(tableName); 203 | 204 | api->attributeGet(tableName, &attributeVector); 205 | char* blockBegin = bm.get_content(*block); 206 | size_t usingSize = bm.get_usingSize(*block); 207 | 208 | while (recordBegin - blockBegin < usingSize) 209 | { 210 | //if the recordBegin point to a record 211 | 212 | if(recordConditionFit(recordBegin, recordSize, &attributeVector, conditionVector)) 213 | { 214 | count ++; 215 | recordPrint(recordBegin, recordSize, &attributeVector, attributeNameVector); 216 | printf("\n"); 217 | } 218 | 219 | recordBegin += recordSize; 220 | } 221 | 222 | return count; 223 | } 224 | 225 | /** 226 | * 227 | * find the number of all record of a table meet requirement 228 | * @param tableName: name of table 229 | * @param conditionVector: the conditions list 230 | * @return int: the number of the record meet requirements(-1 represent error) 231 | */ 232 | int RecordManager::recordAllFind(string tableName, vector* conditionVector) 233 | { 234 | fileNode *ftmp = bm.getFile(tableFileNameGet(tableName).c_str()); 235 | blockNode *btmp = bm.getBlockHead(ftmp); 236 | int count = 0; 237 | while (true) 238 | { 239 | if (btmp == NULL) 240 | { 241 | return -1; 242 | } 243 | if (btmp->ifbottom) 244 | { 245 | int recordBlockNum = recordBlockFind(tableName, conditionVector, btmp); 246 | count += recordBlockNum; 247 | return count; 248 | } 249 | else 250 | { 251 | int recordBlockNum = recordBlockFind(tableName, conditionVector, btmp); 252 | count += recordBlockNum; 253 | btmp = bm.getNextBlock(ftmp, btmp); 254 | } 255 | } 256 | 257 | return -1; 258 | } 259 | 260 | /** 261 | * 262 | * find the number of record of a table in a block 263 | * @param tableName: name of table 264 | * @param block: search record in the block 265 | * @param conditionVector: the conditions list 266 | * @return int: the number of the record meet requirements in the block(-1 represent error) 267 | */ 268 | int RecordManager::recordBlockFind(string tableName, vector* conditionVector, blockNode* block) 269 | { 270 | //if block is null, return -1 271 | if (block == NULL) 272 | { 273 | return -1; 274 | } 275 | int count = 0; 276 | 277 | char* recordBegin = bm.get_content(*block); 278 | vector attributeVector; 279 | int recordSize = api->recordSizeGet(tableName); 280 | 281 | api->attributeGet(tableName, &attributeVector); 282 | 283 | while (recordBegin - bm.get_content(*block) < bm.get_usingSize(*block)) 284 | { 285 | //if the recordBegin point to a record 286 | 287 | if(recordConditionFit(recordBegin, recordSize, &attributeVector, conditionVector)) 288 | { 289 | count++; 290 | } 291 | 292 | recordBegin += recordSize; 293 | 294 | } 295 | 296 | return count; 297 | } 298 | 299 | /** 300 | * 301 | * delete all record of a table meet requirement 302 | * @param tableName: name of table 303 | * @param conditionVector: the conditions list 304 | * @return int: the number of the record meet requirements(-1 represent error) 305 | */ 306 | int RecordManager::recordAllDelete(string tableName, vector* conditionVector) 307 | { 308 | fileNode *ftmp = bm.getFile(tableFileNameGet(tableName).c_str()); 309 | blockNode *btmp = bm.getBlockHead(ftmp); 310 | 311 | int count = 0; 312 | while (true) 313 | { 314 | if (btmp == NULL) 315 | { 316 | return -1; 317 | } 318 | if (btmp->ifbottom) 319 | { 320 | int recordBlockNum = recordBlockDelete(tableName, conditionVector, btmp); 321 | count += recordBlockNum; 322 | return count; 323 | } 324 | else 325 | { 326 | int recordBlockNum = recordBlockDelete(tableName, conditionVector, btmp); 327 | count += recordBlockNum; 328 | btmp = bm.getNextBlock(ftmp, btmp); 329 | } 330 | } 331 | 332 | return -1; 333 | } 334 | 335 | /** 336 | * 337 | * delete record of a table in a block 338 | * @param tableName: name of table 339 | * @param conditionVector: the conditions list 340 | * @param blockOffset: the block's offsetNum 341 | * @return int: the number of the record meet requirements in the block(-1 represent error) 342 | */ 343 | int RecordManager::recordBlockDelete(string tableName, vector* conditionVector, int blockOffset) 344 | { 345 | fileNode *ftmp = bm.getFile(tableFileNameGet(tableName).c_str()); 346 | blockNode* block = bm.getBlockByOffset(ftmp, blockOffset); 347 | if (block == NULL) 348 | { 349 | return -1; 350 | } 351 | else 352 | { 353 | return recordBlockDelete(tableName, conditionVector, block); 354 | } 355 | } 356 | 357 | /** 358 | * 359 | * delete record of a table in a block 360 | * @param tableName: name of table 361 | * @param conditionVector: the conditions list 362 | * @param block: search record in the block 363 | * @return int: the number of the record meet requirements in the block(-1 represent error) 364 | */ 365 | int RecordManager::recordBlockDelete(string tableName, vector* conditionVector, blockNode* block) 366 | { 367 | //if block is null, return -1 368 | if (block == NULL) 369 | { 370 | return -1; 371 | } 372 | int count = 0; 373 | 374 | char* recordBegin = bm.get_content(*block); 375 | vector attributeVector; 376 | int recordSize = api->recordSizeGet(tableName); 377 | 378 | api->attributeGet(tableName, &attributeVector); 379 | 380 | while (recordBegin - bm.get_content(*block) < bm.get_usingSize(*block)) 381 | { 382 | //if the recordBegin point to a record 383 | 384 | if(recordConditionFit(recordBegin, recordSize, &attributeVector, conditionVector)) 385 | { 386 | count ++; 387 | 388 | api->recordIndexDelete(recordBegin, recordSize, &attributeVector, block->offsetNum); 389 | int i = 0; 390 | for (i = 0; i + recordSize + recordBegin - bm.get_content(*block) < bm.get_usingSize(*block); i++) 391 | { 392 | recordBegin[i] = recordBegin[i + recordSize]; 393 | } 394 | memset(recordBegin + i, 0, recordSize); 395 | bm.set_usingSize(*block, bm.get_usingSize(*block) - recordSize); 396 | bm.set_dirty(*block); 397 | } 398 | else 399 | { 400 | recordBegin += recordSize; 401 | } 402 | } 403 | 404 | return count; 405 | } 406 | 407 | /** 408 | * 409 | * insert the index of all record of the table 410 | * @param tableName: name of table 411 | * @param indexName: name of index 412 | * @return int: the number of the record meet requirements(-1 represent error) 413 | */ 414 | int RecordManager::indexRecordAllAlreadyInsert(string tableName,string indexName) 415 | { 416 | fileNode *ftmp = bm.getFile(tableFileNameGet(tableName).c_str()); 417 | blockNode *btmp = bm.getBlockHead(ftmp); 418 | int count = 0; 419 | while (true) 420 | { 421 | if (btmp == NULL) 422 | { 423 | return -1; 424 | } 425 | if (btmp->ifbottom) 426 | { 427 | int recordBlockNum = indexRecordBlockAlreadyInsert(tableName, indexName, btmp); 428 | count += recordBlockNum; 429 | return count; 430 | } 431 | else 432 | { 433 | int recordBlockNum = indexRecordBlockAlreadyInsert(tableName, indexName, btmp); 434 | count += recordBlockNum; 435 | btmp = bm.getNextBlock(ftmp, btmp); 436 | } 437 | } 438 | 439 | return -1; 440 | } 441 | 442 | 443 | /** 444 | * 445 | * insert the index of a record of a table in a block 446 | * @param tableName: name of table 447 | * @param indexName: name of index 448 | * @param block: search record in the block 449 | * @return int: the number of the record meet requirements in the block(-1 represent error) 450 | */ 451 | int RecordManager::indexRecordBlockAlreadyInsert(string tableName,string indexName, blockNode* block) 452 | { 453 | //if block is null, return -1 454 | if (block == NULL) 455 | { 456 | return -1; 457 | } 458 | int count = 0; 459 | 460 | char* recordBegin = bm.get_content(*block); 461 | vector attributeVector; 462 | int recordSize = api->recordSizeGet(tableName); 463 | 464 | api->attributeGet(tableName, &attributeVector); 465 | 466 | int type; 467 | int typeSize; 468 | char * contentBegin; 469 | 470 | while (recordBegin - bm.get_content(*block) < bm.get_usingSize(*block)) 471 | { 472 | contentBegin = recordBegin; 473 | //if the recordBegin point to a record 474 | for (int i = 0; i < attributeVector.size(); i++) 475 | { 476 | type = attributeVector[i].type; 477 | typeSize = api->typeSizeGet(type); 478 | 479 | //find the index of the record, and insert it to index tree 480 | if (attributeVector[i].index == indexName) 481 | { 482 | api->indexInsert(indexName, contentBegin, type, block->offsetNum); 483 | count++; 484 | } 485 | 486 | contentBegin += typeSize; 487 | } 488 | recordBegin += recordSize; 489 | } 490 | 491 | return count; 492 | } 493 | 494 | /** 495 | * 496 | * judge if the record meet the requirement 497 | * @param recordBegin: point to a record 498 | * @param recordSize: size of the record 499 | * @param attributeVector: the attribute list of the record 500 | * @param conditionVector: the conditions 501 | * @return bool: if the record fit the condition 502 | */ 503 | bool RecordManager::recordConditionFit(char* recordBegin,int recordSize, vector* attributeVector,vector* conditionVector) 504 | { 505 | if (conditionVector == NULL) { 506 | return true; 507 | } 508 | int type; 509 | string attributeName; 510 | int typeSize; 511 | char content[255]; 512 | 513 | char *contentBegin = recordBegin; 514 | for(int i = 0; i < attributeVector->size(); i++) 515 | { 516 | type = (*attributeVector)[i].type; 517 | attributeName = (*attributeVector)[i].name; 518 | typeSize = api->typeSizeGet(type); 519 | 520 | //init content (when content is string , we can get a string easily) 521 | memset(content, 0, 255); 522 | memcpy(content, contentBegin, typeSize); 523 | for(int j = 0; j < (*conditionVector).size(); j++) 524 | { 525 | if ((*conditionVector)[j].attributeName == attributeName) 526 | { 527 | //if this attribute need to deal about the condition 528 | if(!contentConditionFit(content, type, &(*conditionVector)[j])) 529 | { 530 | //if this record is not fit the conditon 531 | return false; 532 | } 533 | } 534 | } 535 | 536 | contentBegin += typeSize; 537 | } 538 | return true; 539 | } 540 | 541 | /** 542 | * 543 | * print value of record 544 | * @param recordBegin: point to a record 545 | * @param recordSize: size of the record 546 | * @param attributeVector: the attribute list of the record 547 | * @param attributeVector: the name list of all attribute you want to print 548 | */ 549 | void RecordManager::recordPrint(char* recordBegin, int recordSize, vector* attributeVector, vector *attributeNameVector) 550 | { 551 | int type; 552 | string attributeName; 553 | int typeSize; 554 | char content[255]; 555 | 556 | char *contentBegin = recordBegin; 557 | for(int i = 0; i < attributeVector->size(); i++) 558 | { 559 | type = (*attributeVector)[i].type; 560 | typeSize = api->typeSizeGet(type); 561 | 562 | //init content (when content is string , we can get a string easily) 563 | memset(content, 0, 255); 564 | 565 | memcpy(content, contentBegin, typeSize); 566 | 567 | for(int j = 0; j < (*attributeNameVector).size(); j++) 568 | { 569 | if ((*attributeNameVector)[j] == (*attributeVector)[i].name) 570 | { 571 | contentPrint(content, type); 572 | break; 573 | } 574 | } 575 | 576 | contentBegin += typeSize; 577 | } 578 | } 579 | 580 | /** 581 | * 582 | * print value of content 583 | * @param content: point to content 584 | * @param type: type of content 585 | */ 586 | void RecordManager::contentPrint(char * content, int type) 587 | { 588 | if (type == Attribute::TYPE_INT) 589 | { 590 | //if the content is a int 591 | int tmp = *((int *) content); //get content value by point 592 | printf("%d ", tmp); 593 | } 594 | else if (type == Attribute::TYPE_FLOAT) 595 | { 596 | //if the content is a float 597 | float tmp = *((float *) content); //get content value by point 598 | printf("%f ", tmp); 599 | } 600 | else 601 | { 602 | //if the content is a string 603 | string tmp = content; 604 | printf("%s ", tmp.c_str()); 605 | } 606 | 607 | } 608 | 609 | /** 610 | * 611 | * judge if the content meet the requirement 612 | * @param content: point to content 613 | * @param type: type of content 614 | * @param condition: condition 615 | * @return bool: the content if meet 616 | */ 617 | bool RecordManager::contentConditionFit(char* content,int type,Condition* condition) 618 | { 619 | if (type == Attribute::TYPE_INT) 620 | { 621 | //if the content is a int 622 | int tmp = *((int *) content); //get content value by point 623 | return condition->ifRight(tmp); 624 | } 625 | else if (type == Attribute::TYPE_FLOAT) 626 | { 627 | //if the content is a float 628 | float tmp = *((float *) content); //get content value by point 629 | return condition->ifRight(tmp); 630 | } 631 | else 632 | { 633 | //if the content is a string 634 | return condition->ifRight(content); 635 | } 636 | return true; 637 | } 638 | 639 | /** 640 | * 641 | * get a index's file name 642 | * @param indexName: name of index 643 | */ 644 | string RecordManager::indexFileNameGet(string indexName) 645 | { 646 | string tmp = ""; 647 | return "INDEX_FILE_"+indexName; 648 | } 649 | 650 | /** 651 | * 652 | * get a table's file name 653 | * @param tableName: name of table 654 | */ 655 | string RecordManager::tableFileNameGet(string tableName) 656 | { 657 | string tmp = ""; 658 | return tmp + "TABLE_FILE_" + tableName; 659 | } 660 | -------------------------------------------------------------------------------- /code/RecordManager.h: -------------------------------------------------------------------------------- 1 | #ifndef RECORDMANAGER_H 2 | #define RECORDMANAGER_H 3 | //#include "stdafx.h" 4 | #include "Condition.h" 5 | #include "Attribute.h" 6 | #include "RecordManager.h" 7 | #include "BufferManager.h" 8 | #include "Minisql.h" 9 | #include 10 | #include 11 | using namespace std; 12 | class API; 13 | class RecordManager{ 14 | public: 15 | RecordManager(){} 16 | BufferManager bm; 17 | API *api; 18 | 19 | int tableCreate(string tableName); 20 | int tableDrop(string tableName); 21 | 22 | int indexDrop(string indexName); 23 | int indexCreate(string indexName); 24 | 25 | int recordInsert(string tableName, char* record, int recordSize); 26 | 27 | int recordAllShow(string tableName, vector* attributeNameVector, vector* conditionVector); 28 | int recordBlockShow(string tableName, vector* attributeNameVector, vector* conditionVector, int blockOffset); 29 | 30 | int recordAllFind(string tableName, vector* conditionVector); 31 | 32 | int recordAllDelete(string tableName, vector* conditionVector); 33 | int recordBlockDelete(string tableName, vector* conditionVector, int blockOffset); 34 | 35 | int indexRecordAllAlreadyInsert(string tableName,string indexName); 36 | 37 | string tableFileNameGet(string tableName); 38 | string indexFileNameGet(string indexName); 39 | private: 40 | int recordBlockShow(string tableName, vector* attributeNameVector, vector* conditionVector, blockNode* block); 41 | int recordBlockFind(string tableName, vector* conditionVector, blockNode* block); 42 | int recordBlockDelete(string tableName, vector* conditionVector, blockNode* block); 43 | int indexRecordBlockAlreadyInsert(string tableName,string indexName, blockNode* block); 44 | 45 | bool recordConditionFit(char* recordBegin,int recordSize, vector* attributeVector,vector* conditionVector); 46 | void recordPrint(char* recordBegin, int recordSize, vector* attributeVector, vector *attributeNameVector); 47 | bool contentConditionFit(char* content, int type, Condition* condition); 48 | void contentPrint(char * content, int type); 49 | }; 50 | #endif -------------------------------------------------------------------------------- /code/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Interpreter.h" 3 | #include "CatalogManager.h" 4 | #include "RecordManager.h" 5 | #include "IndexManager.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "API.h" 12 | 13 | 14 | 15 | void init() 16 | { 17 | FILE *fp; 18 | fp = fopen("Indexs", "r"); 19 | if (fp == NULL) 20 | { 21 | fp = fopen("Indexs", "w+"); 22 | return; 23 | } 24 | fclose(fp); 25 | } 26 | 27 | void print() 28 | { 29 | clock_t finish = clock(); 30 | double duration = (double)(finish - start) / CLOCKS_PER_SEC; 31 | duration *= 1000; 32 | printf( "now time is %2.1f milliseconds\n", duration * 1000); 33 | } 34 | 35 | clock_t start; 36 | 37 | int main(int argc,char * argv[]) 38 | { 39 | init(); 40 | 41 | API api; 42 | CatalogManager cm; 43 | RecordManager rm; 44 | 45 | api.rm = &rm; 46 | api.cm = &cm; 47 | IndexManager im(&api); 48 | 49 | api.im = &im; 50 | rm.api = &api; 51 | 52 | start = 0; 53 | clock_t finish; 54 | 55 | cout<<"*******************Welcome to use our MiniSQL**********************"<>"; 91 | getline(cin,s,';'); 92 | start = clock(); 93 | status = in.interpreter(s); 94 | if(status==2) 95 | { 96 | fileRead = 1; 97 | } 98 | else if(status==587) 99 | { 100 | break; 101 | } 102 | else{ 103 | finish = clock(); 104 | double duration = (double)(finish - start) / CLOCKS_PER_SEC; 105 | duration *= 1000; 106 | printf( "The duration is %2.1f milliseconds\n", duration); 107 | } 108 | } 109 | 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | --------------------------------------------------------------------------------