├── include ├── cli │ └── sqleast.h ├── debug.h ├── sm │ ├── exception.h │ ├── catalog.h │ ├── dbhandle.h │ └── systemmanager.h ├── ql │ ├── printer.h │ ├── parser.h │ └── query.h ├── rm │ ├── exception.h │ ├── filescan.h │ ├── recordmanager.h │ ├── bitmaputil.h │ └── filehandle.h ├── ix │ ├── indexscan.h │ └── index.h ├── pagefs │ ├── exception.h │ └── pagefs.h ├── sqleast.h └── frontend │ └── parser_internal.h ├── test ├── test.h ├── testql.cpp ├── testix.cpp ├── CMakeLists.txt ├── testsm.cpp ├── testfrontend.cpp ├── testrm.cpp └── testpagefs.cpp ├── src ├── frontend │ ├── parser_internal.h │ ├── CMakeLists.txt │ ├── lexer.l │ ├── parser.hpp │ ├── lexerhelp.cpp │ ├── nodes.cpp │ ├── parser.y │ └── interp.c ├── ix │ ├── CMakeLists.txt │ ├── indexscan.cpp │ └── index.cpp ├── sm │ ├── CMakeLists.txt │ ├── systemmanager.cpp │ └── dbhandle.cpp ├── pagefs │ ├── CMakeLists.txt │ └── pagefs.cpp ├── CMakeLists.txt ├── rm │ ├── CMakeLists.txt │ ├── recordmanager.cpp │ ├── filescan.cpp │ └── filehandle.cpp ├── cli │ ├── sqleast.cpp │ └── CMakeLists.txt └── ql │ ├── CMakeLists.txt │ ├── query.cpp.bak │ └── parser.cpp.bak ├── .gitignore └── CMakeLists.txt /include/cli/sqleast.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #include "ql/printer.h" 2 | 3 | Printer printer = std::cout; 4 | -------------------------------------------------------------------------------- /src/frontend/parser_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_INTERNAL_H 2 | #define PARSER_INTERNAL_H 3 | 4 | #include "frontend/parser_internal.h" 5 | 6 | #endif -------------------------------------------------------------------------------- /src/ix/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | include_directories(../../include) 4 | 5 | aux_source_directory(. SRC_LIST) 6 | 7 | message("IX: ${SRC_LIST}") 8 | add_library(sqleast-ix STATIC ${SRC_LIST}) 9 | -------------------------------------------------------------------------------- /src/sm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | include_directories(../../include) 4 | 5 | aux_source_directory(. SRC_LIST) 6 | 7 | message("SM: ${SRC_LIST}") 8 | add_library(sqleast-sm STATIC ${SRC_LIST}) 9 | -------------------------------------------------------------------------------- /src/pagefs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | include_directories(../../include) 4 | 5 | aux_source_directory(. SRC_LIST) 6 | 7 | message("PAGEFS: ${SRC_LIST}") 8 | add_library(pagefs STATIC ${SRC_LIST}) 9 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | add_subdirectory(pagefs) 4 | add_subdirectory(rm) 5 | add_subdirectory(ix) 6 | add_subdirectory(sm) 7 | add_subdirectory(ql) 8 | add_subdirectory(cli) 9 | add_subdirectory(frontend) 10 | 11 | -------------------------------------------------------------------------------- /src/rm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | include_directories(../../include) 4 | 5 | aux_source_directory(. SRC_LIST) 6 | 7 | message("RM: ${SRC_LIST}") 8 | add_library(sqleast-rm STATIC ${SRC_LIST}) 9 | target_link_libraries(sqleast-rm pagefs) 10 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef SQLEAST_DEBUG_H 2 | #define SQLEAST_DEBUG_H 3 | 4 | #include 5 | 6 | class Debug { 7 | public: 8 | static void info(const char *msg) { 9 | std::cout << "[INFO]" << msg << std::endl; 10 | } 11 | 12 | }; 13 | 14 | #endif -------------------------------------------------------------------------------- /test/testql.cpp: -------------------------------------------------------------------------------- 1 | #include "ql/parser.h" 2 | #include "ql/query.h" 3 | 4 | int main() { 5 | std::string s; 6 | s = "DELETE FROM addbc WHERE book.publisher_id>=publisher.id OR publisher.nation='PRC'"; 7 | sqleast::ql::Parser p; 8 | p.parse(s); 9 | 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /src/cli/sqleast.cpp: -------------------------------------------------------------------------------- 1 | #include "frontend/parser_internal.h" 2 | #include "ql/printer.h" 3 | 4 | using namespace std; 5 | 6 | Printer printer = cout; 7 | Printer err = cerr; 8 | 9 | int main() { 10 | 11 | sqleast_parse(); 12 | 13 | printer << "bye" << endl; 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /include/sm/exception.h: -------------------------------------------------------------------------------- 1 | #ifndef SM_EXCEPTION_H 2 | #define SM_EXCEPTION_H 3 | 4 | namespace sqleast { 5 | namespace sm { 6 | 7 | class SMException {}; 8 | 9 | class DBExistsException: public SMException {}; 10 | 11 | class DBNotExistsException: public SMException {}; 12 | } 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /src/ql/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | include_directories(../../include) 4 | 5 | # aux_source_directory(. SRC_LIST) 6 | set(SRC_LIST interpreter.cpp) 7 | 8 | message("QL: ${SRC_LIST}") 9 | add_library(sqleast-ql STATIC ${SRC_LIST}) 10 | target_link_libraries(sqleast-ql 11 | sqleast-rm sqleast-ix sqleast-sm 12 | boost_system boost_filesystem) 13 | -------------------------------------------------------------------------------- /test/testix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sqleast.h" 3 | #include "ix/index.h" 4 | #include "ix/indexscan.h" 5 | 6 | using namespace std; 7 | 8 | int page = 9; 9 | int slot = 5; 10 | 11 | void generateRID(sqleast::RID &rid){ 12 | rid.pageNum = page++; 13 | rid.slotNum = slot++; 14 | } 15 | 16 | int main() 17 | { 18 | using namespace sqleast; 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | include_directories(../../include) 4 | 5 | aux_source_directory(. SRC_LIST) 6 | 7 | message("CLI: ${SRC_LIST}") 8 | add_executable(sqleast ${SRC_LIST}) 9 | target_link_libraries(sqleast 10 | pagefs 11 | sqleast-rm 12 | sqleast-ix 13 | sqleast-sm 14 | sqleast-ql 15 | sqleast-frontend 16 | boost_system 17 | boost_filesystem) 18 | -------------------------------------------------------------------------------- /include/ql/printer.h: -------------------------------------------------------------------------------- 1 | #ifndef QL_PRINTER_H 2 | #define QL_PRINTER_H 3 | 4 | #include 5 | // TODO stronger printer 6 | typedef std::ostream& Printer; 7 | 8 | #define DEBUG1 "publisher" 9 | #define DEBUG2 "orders" 10 | #define OUT1 "/Users/badpoet/Downloads/something/dataset_small/output.txt" 11 | #define OUT2 "/Users/badpoet/Downloads/something/dataset_small/output2.txt" 12 | #define OUT3 "/Users/badpoet/Downloads/something/dataset_small/output3.txt" 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/rm/exception.h: -------------------------------------------------------------------------------- 1 | #ifndef RM_EXCEPTION_H 2 | #define RM_EXCEPTION_H 3 | 4 | namespace sqleast { 5 | namespace rm { 6 | 7 | class RMException {}; 8 | 9 | class RecordTooLargeException : public RMException {}; 10 | 11 | class EOFException : public RMException {}; 12 | 13 | class InvalidRecordException : public RMException {}; 14 | 15 | class RMError {}; 16 | 17 | class RecordSizeError: public RMError {}; 18 | } 19 | } 20 | 21 | #endif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | lib 3 | build 4 | 5 | # Created by .ignore support plugin (hsz.mobi) 6 | # C++ template 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | .idea 37 | .DS_Store 38 | -------------------------------------------------------------------------------- /src/frontend/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(BISON) 2 | find_package(FLEX) 3 | 4 | 5 | BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_SOURCE_DIR}/parser.cpp) 6 | FLEX_TARGET(MyScanner lexer.l ${CMAKE_CURRENT_SOURCE_DIR}/lexer.cpp) 7 | ADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser) 8 | 9 | 10 | include_directories(${CMAKE_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}) 11 | add_library(sqleast-frontend STATIC 12 | nodes.cpp 13 | ${BISON_MyParser_OUTPUTS} 14 | ${FLEX_MyScanner_OUTPUTS} 15 | ) 16 | target_link_libraries(sqleast-frontend sqleast-ql) 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.4) 2 | project(SQLeast) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | set(CMAKE_BUILD_TYPE DEBUG) 7 | 8 | #set(Boost_USE_STATIC_LIBS ON) 9 | #find_package(Boost 1.35 REQUIRED) 10 | 11 | #include_directories(${Boost_INCLUDE_DIRS}) 12 | #link_directories(${Boost_LIBRARY_DIRS}) 13 | 14 | message("Build: ${CMAKE_BUILD_TYPE}") 15 | message("Flags: ${CMAKE_CXX_FLAGS_RELEASE}") 16 | 17 | set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) 18 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 19 | add_subdirectory(src) 20 | add_subdirectory(test) 21 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SQLeast) 2 | 3 | include_directories(../include) 4 | 5 | add_executable(testpagefs testpagefs.cpp) 6 | target_link_libraries(testpagefs pagefs) 7 | 8 | add_executable(testrm testrm.cpp) 9 | target_link_libraries(testrm pagefs sqleast-rm) 10 | 11 | add_executable(testix testix.cpp) 12 | target_link_libraries(testix pagefs sqleast-rm sqleast-ix) 13 | 14 | # add_executable(testsm testsm.cpp) 15 | # target_link_libraries(testsm pagefs sqleast-rm sqleast-ix sqleast-sm boost_system boost_filesystem) 16 | 17 | add_executable(testfrontend testfrontend.cpp) 18 | target_link_libraries(testfrontend sqleast-frontend) 19 | -------------------------------------------------------------------------------- /include/ql/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef QL_PARSER_H 2 | #define QL_PARSER_H 3 | 4 | #include "ql/query.h" 5 | #include 6 | 7 | namespace sqleast { 8 | namespace ql { 9 | 10 | class Parser { 11 | 12 | public: 13 | Parser(){ 14 | q_ = nullptr; 15 | } 16 | StructuredQuery *parse(std::string input); // input should change \n to space and remove the end ; of input 17 | 18 | private: 19 | StructuredQuery *q_; 20 | 21 | std::string getWord(std::string& input); 22 | std::string getTuple(std::string& input); 23 | int findRealMark(std::string& input, char c); 24 | void getAttr(std::string input, int& type, int& len); 25 | }; 26 | 27 | } 28 | } 29 | 30 | #endif -------------------------------------------------------------------------------- /include/sm/catalog.h: -------------------------------------------------------------------------------- 1 | #ifndef SM_CATALOG_H 2 | #define SM_CATALOG_H 3 | 4 | #include "sqleast.h" 5 | 6 | namespace sqleast { 7 | namespace sm { 8 | 9 | struct DataAttrInfo { 10 | char relName[MAX_NAME_LENGTH + 1]; 11 | char attrName[MAX_NAME_LENGTH + 1]; 12 | int offset; 13 | AttrType attrType; 14 | int attrLength; 15 | int nullable; 16 | int nullBitOffset; 17 | int nullBitMask; 18 | int indexNo; 19 | bool isPrimary; 20 | }; 21 | 22 | struct RelInfo { 23 | char relName[MAX_NAME_LENGTH + 1]; 24 | int tupleLength; 25 | int bitmapSize; 26 | int attrCount; 27 | int indexCount; 28 | char primaryKey[MAX_NAME_LENGTH + 1]; 29 | }; 30 | 31 | } 32 | } 33 | 34 | #endif -------------------------------------------------------------------------------- /include/rm/filescan.h: -------------------------------------------------------------------------------- 1 | #ifndef RM_FILESCAN_H 2 | #define RM_FILESCAN_H 3 | 4 | #include "filehandle.h" 5 | 6 | namespace sqleast { 7 | namespace rm { 8 | 9 | class FileScan { 10 | 11 | public: 12 | FileScan(FileHandle &handle, 13 | AttrType attrType, int attrLength, int attrOffset, 14 | int nullBitOffset, int nullBitMask, 15 | CompOp compOp, void *value); 16 | ~FileScan(); 17 | 18 | Record &next(); 19 | inline Record *current() { return &c_; } 20 | 21 | private: 22 | int pageNum_, slotNum_; 23 | AttrType attrType_; 24 | int attrLength_, attrOffset_, nullBitOffset_, nullBitMask_; 25 | CompOp compOp_; 26 | void *value_; 27 | FileHandle handle_; 28 | FileInfo info_; 29 | Record c_; // order matters (for initialization) 30 | }; 31 | 32 | } 33 | } 34 | 35 | #endif -------------------------------------------------------------------------------- /include/ix/indexscan.h: -------------------------------------------------------------------------------- 1 | #ifndef IX_INDEXSCAN_H 2 | #define IX_INDEXSCAN_H 3 | 4 | #include 5 | #include "ix/index.h" 6 | 7 | namespace sqleast { 8 | namespace ix { 9 | 10 | class IndexScan { 11 | public: 12 | IndexScan(Index& i, int v, CompOp c); 13 | ~IndexScan(); 14 | RID next(); 15 | private: 16 | Index &index_; 17 | int value_; 18 | CompOp compOp_; 19 | bool canDo; 20 | 21 | RID current; 22 | std::vector position; 23 | 24 | void toStart1(); // for no, eq, ne 25 | void toStart2(); // for lt, gt, le, ge 26 | bool toRight(); 27 | bool toLeft(); 28 | RID NoSearch(); 29 | RID EqSearch(); 30 | RID LtSearch(); 31 | RID GtSearch(); 32 | RID LeSearch(); 33 | RID GeSearch(); 34 | RID NeSearch(); 35 | }; 36 | 37 | } 38 | } 39 | 40 | #endif -------------------------------------------------------------------------------- /include/rm/recordmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef RM_CORE_H 2 | #define RM_CORE_H 3 | 4 | #include "sqleast.h" 5 | #include "pagefs/pagefs.h" 6 | 7 | namespace sqleast { 8 | namespace rm { 9 | 10 | struct FileInfo { 11 | int recordSize; 12 | int slotPerPage; 13 | int slotBitmapSize; 14 | int firstEmptyPage; 15 | int totalPageNum; 16 | }; 17 | 18 | struct PageHeader { 19 | int nextPage; 20 | int emptySlot; 21 | char *slotBitmap; 22 | 23 | PageHeader (int _nextPage, int _emptySlot, char *_slotBitmap): 24 | nextPage(_nextPage), emptySlot(_emptySlot), slotBitmap(_slotBitmap) {} 25 | }; // should be created and released by core, cuz slotBitmap is variable-length 26 | 27 | class FileHandle; 28 | 29 | class RecordManager { 30 | 31 | public: 32 | static void createFile(char const *fileName, int dataSize, bool override = false); 33 | static FileHandle openFile(char const *fileName); 34 | static void destroyFile(char const *fileName); 35 | 36 | }; 37 | } 38 | } 39 | 40 | #endif -------------------------------------------------------------------------------- /test/testsm.cpp: -------------------------------------------------------------------------------- 1 | #include "sm/systemmanager.h" 2 | #include "sm/dbhandle.h" 3 | 4 | int main() { 5 | sqleast::sm::SystemManager::destroyDB("testing"); 6 | std::cout << sizeof(sqleast::sm::RelInfo) << std::endl; 7 | std::cout << sizeof(sqleast::sm::DataAttrInfo) << std::endl; 8 | sqleast::sm::SystemManager::createDB("testing"); 9 | sqleast::sm::SystemManager::useDB("testing"); 10 | 11 | sqleast::AttrInfo attrs[3]; 12 | 13 | attrs[0].attrName = new char[sqleast::MAX_NAME_LENGTH]; 14 | memcpy(attrs[0].attrName, "id", sqleast::MAX_NAME_LENGTH); 15 | attrs[0].attrLength = sizeof(int); 16 | attrs[0].attrType = sqleast::INT; 17 | attrs[0].nullable = 0; 18 | 19 | attrs[1].attrName = new char[sqleast::MAX_NAME_LENGTH]; 20 | memcpy(attrs[1].attrName, "name", sqleast::MAX_NAME_LENGTH); 21 | attrs[1].attrLength = 100; 22 | attrs[1].attrType = sqleast::STRING; 23 | attrs[1].nullable = 0; 24 | 25 | attrs[2].attrName = new char[sqleast::MAX_NAME_LENGTH]; 26 | memcpy(attrs[2].attrName, "nation", sqleast::MAX_NAME_LENGTH); 27 | attrs[2].attrLength = 3; 28 | attrs[2].attrType = sqleast::STRING; 29 | attrs[2].nullable = 1; 30 | 31 | sqleast::sm::DBManager manager("testing"); 32 | manager.createTable("publisher", 3, attrs); 33 | manager.createTable("crap", 2, attrs + 1); 34 | manager.dropTable("publisher"); 35 | 36 | sqleast::sm::SystemManager::closeDB(); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /test/testfrontend.cpp: -------------------------------------------------------------------------------- 1 | #include "frontend/parser_internal.h" 2 | #include "test.h" 3 | #include 4 | 5 | void interp(NODE *n) { 6 | 7 | using namespace std; 8 | 9 | switch(n -> kind) { 10 | 11 | case N_CREATETABLE: /* for CreateTable() */ 12 | cout << "create table" << endl; 13 | break; 14 | 15 | case N_CREATEINDEX: /* for CreateIndex() */ 16 | cout << "create index" << endl; 17 | break; 18 | 19 | case N_DROPINDEX: /* for DropIndex() */ 20 | cout << "drop index" << endl; 21 | break; 22 | 23 | case N_DROPTABLE: /* for DropTable() */ 24 | cout << "drop table" << endl; 25 | break; 26 | 27 | case N_QUERY: /* for Query() */ 28 | cout << "query" << endl; 29 | break; 30 | 31 | case N_INSERT: /* for Insert() */ 32 | cout << "insert" << endl; 33 | break; 34 | 35 | case N_DELETE: /* for Delete() */ 36 | cout << "delete" << endl; 37 | break; 38 | 39 | case N_UPDATE: /* for Update() */ 40 | cout << "update" << endl; 41 | break; 42 | 43 | default: // should never get here 44 | cout << "???" << endl; 45 | break; 46 | } 47 | } 48 | 49 | int main() { 50 | 51 | sqleast_parse(); 52 | 53 | std::cout << "bye" << std::endl; 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /include/sm/dbhandle.h: -------------------------------------------------------------------------------- 1 | #ifndef SM_DBMANAGER_H 2 | #define SM_DBMANAGER_H 3 | 4 | #include "catalog.h" 5 | #include "rm/filehandle.h" 6 | #include "rm/recordmanager.h" 7 | #include "sm/systemmanager.h" 8 | 9 | namespace sqleast { 10 | namespace sm { 11 | 12 | class DBHandle { 13 | 14 | public: 15 | 16 | DBHandle(const char *dbName); 17 | ~DBHandle(); 18 | void createTable(const char *relName, int attrNum, const char *primary, AttrInfo *attrs); 19 | void dropTable(const char *relName); 20 | void createIndex(const char *relName, const char *attrName); 21 | void dropIndex(const char *relName, const char *attrName); 22 | 23 | void showTables(); 24 | void descTable(const char *relName); 25 | 26 | int findTable(const char *relName); 27 | 28 | int loadRelAttrInfo(const char *relName, DataAttrInfo *attrInfoArr); 29 | RelInfo getRelInfo(const char *relName); 30 | 31 | void loadCol(char *data, int offset, int size, AttrType type, void *target); 32 | 33 | RID findKey(char *relName, DataAttrInfo *dai, Value *value); 34 | 35 | inline rm::FileHandle openRelFile(char *relName) { 36 | return rm::RecordManager::openFile(SystemManager::appendDBExt(relName).c_str()); 37 | } 38 | 39 | // inline rm::FileHandle openIndexFile(char *relName) { 40 | // return rm::RecordManager::openFile(SystemManager::appendDBExt(name).c_str()); 41 | // } 42 | 43 | private: 44 | rm::FileHandle relCatalog_, attrCatalog_; 45 | }; 46 | } 47 | } 48 | 49 | 50 | #endif -------------------------------------------------------------------------------- /src/rm/recordmanager.cpp: -------------------------------------------------------------------------------- 1 | #include "rm/recordmanager.h" 2 | #include "rm/filehandle.h" 3 | #include "rm/exception.h" 4 | #include "rm/bitmaputil.h" 5 | 6 | namespace sqleast { 7 | namespace rm { 8 | 9 | using namespace pagefs; 10 | 11 | void RecordManager::createFile(char const *fileName, int recordSize, bool override) { 12 | PageFS &fs = PageFS::getInstance(); 13 | fs.createFile(fileName, override); 14 | int fid = fs.openFile(fileName); 15 | rm::FileHandle handle(fid); 16 | 17 | FileInfo *infoPtr = (FileInfo*) fs.loadPage(fid, 0); 18 | infoPtr->firstEmptyPage = 0; 19 | infoPtr->recordSize = recordSize; 20 | // slot_n = 8 * (PAGE_SIZE - HEADER_SIZE) / (8 * rsize + 1) 21 | infoPtr->slotPerPage = ((PAGE_SIZE - (int)sizeof(PageHeader) + (int)sizeof(char *)) << 3) / ((recordSize << 3) + 1); 22 | infoPtr->slotBitmapSize = infoPtr->slotPerPage / 8 + (infoPtr->slotPerPage % 8 > 0); 23 | infoPtr->totalPageNum = 1; 24 | 25 | if (infoPtr->slotPerPage <= 0) 26 | throw RecordTooLargeException(); 27 | 28 | fs.markDirty(fid, 0); 29 | fs.forcePage(fid, 0); 30 | fs.unpinPage(fid, 0); 31 | } 32 | 33 | FileHandle RecordManager::openFile(char const*fileName) { 34 | PageFS &fs = PageFS::getInstance(); 35 | int fid = fs.openFile(fileName); 36 | return rm::FileHandle(fid); 37 | } 38 | 39 | void RecordManager::destroyFile(char const *fileName) { 40 | PageFS &fs = PageFS::getInstance(); 41 | fs.destroyFile(fileName); 42 | } 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /include/sm/systemmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef SM_SM_H 2 | #define SM_SM_H 3 | 4 | #include "sqleast.h" 5 | #include "sm/catalog.h" 6 | #include 7 | #include 8 | 9 | #define REL_CATALOG "rel.catalog" 10 | #define ATTR_CATALOG "attr.catalog" 11 | 12 | namespace sqleast { 13 | namespace sm { 14 | 15 | class SystemManager { 16 | 17 | public: 18 | 19 | inline static std::string appendRelCatalogExt(const char *c) { 20 | return std::string(c) + ".rel.catalog"; 21 | } 22 | 23 | inline static std::string appendAttrCatalogExt(const char *c) { 24 | return std::string(c) + ".attr.catalog"; 25 | } 26 | 27 | inline static std::string appendDBExt(const char *c) { 28 | return std::string(c) + ".db"; 29 | } 30 | 31 | inline static std::string appendIndexExt(const char *c, int no) { 32 | std::stringstream s; 33 | s << c << "_" << no << ".index"; 34 | std::string res; 35 | s >> res; 36 | return res; 37 | } 38 | 39 | static void createDB(char const *dbName); 40 | static void useDB(char const *dbName); 41 | static void closeDB(); 42 | static void destroyDB(char const *dbName); 43 | 44 | private: 45 | static int working_; 46 | static int isDirectory(char *path) { 47 | struct stat s; 48 | if( stat(path, &s) == 0 ) 49 | { 50 | if( s.st_mode & S_IFDIR ) 51 | { 52 | return 1; 53 | } 54 | } 55 | return 0; 56 | }; 57 | 58 | }; 59 | } 60 | } 61 | 62 | #endif -------------------------------------------------------------------------------- /include/rm/bitmaputil.h: -------------------------------------------------------------------------------- 1 | #ifndef RM_BITMAP_H 2 | #define RM_BITMAP_H 3 | 4 | namespace sqleast { 5 | 6 | namespace rm { 7 | 8 | class Bitmap8Util { 9 | 10 | public: 11 | 12 | static int lowest0(int x) { 13 | static char lowest0_[256]; // table of lowest 0 in a int 14 | static bool inited = false; 15 | if (!inited) { 16 | for (int i = 0; i < 255; i++) { 17 | int tmp = ((i ^ (i + 1)) >> 1) + 1; 18 | switch (tmp) { 19 | case 1: 20 | lowest0_[i] = 0; 21 | break; 22 | case 2: 23 | lowest0_[i] = 1; 24 | break; 25 | case 4: 26 | lowest0_[i] = 2; 27 | break; 28 | case 8: 29 | lowest0_[i] = 3; 30 | break; 31 | case 16: 32 | lowest0_[i] = 4; 33 | break; 34 | case 32: 35 | lowest0_[i] = 5; 36 | break; 37 | case 64: 38 | lowest0_[i] = 6; 39 | break; 40 | case 128: 41 | lowest0_[i] = 7; 42 | break; 43 | } 44 | } 45 | lowest0_[255] = -1; 46 | } 47 | return lowest0_[x]; 48 | } 49 | 50 | }; 51 | 52 | } 53 | 54 | } 55 | 56 | #endif -------------------------------------------------------------------------------- /include/pagefs/exception.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGEFS_EXCEPTION_H 2 | #define PAGEFS_EXCEPTION_H 3 | 4 | namespace pagefs { 5 | 6 | /* expected exceptions 7 | * 8 | * These exceptions may occur in runtime. They should be derived from 9 | * PageFSException. 10 | */ 11 | 12 | class PageFSException {}; 13 | 14 | class EOFException : public PageFSException {}; 15 | 16 | // page is already pinned 17 | class PagePinnedException : public PageFSException {}; 18 | 19 | // page is not in buf 20 | class PageNotInBufException : public PageFSException {}; 21 | 22 | // page is unpinned 23 | class PageUnpinnedException : public PageFSException {}; 24 | 25 | // page is already freed 26 | class PageFreedException : public PageFSException {}; 27 | 28 | // invalid page number 29 | class InvalidPageException : public PageFSException {}; 30 | 31 | // file is opened 32 | class FileOpenedException : public PageFSException {}; 33 | 34 | // file is closed 35 | class FileClosedException : public PageFSException {}; 36 | 37 | // file existed 38 | class FileExistsException : public PageFSException {}; 39 | 40 | // file not found 41 | class FileNotFoundException : public PageFSException {}; 42 | 43 | // file table full 44 | class FileTableFullException : public PageFSException {}; 45 | 46 | // exception when removing a file 47 | class FileRemoveException : public PageFSException {}; 48 | 49 | class ItemNotFound : public PageFSException {}; 50 | 51 | /* unexpected exceptions 52 | * 53 | * These exceptions are not expected to occur in runtime. 54 | */ 55 | 56 | class PageFSError {}; 57 | 58 | class NoMemError : public PageFSError {}; 59 | 60 | class NoBufError : public PageFSError {}; 61 | 62 | class TooManyFilesError : public PageFSError {}; 63 | 64 | // errors when reading files 65 | class ReadFileError : public PageFSError {}; 66 | 67 | // errors when writing files 68 | class WriteFileError : public PageFSError {}; 69 | 70 | // duplicated buffer pages 71 | class PageInBufError : public PageFSError {}; 72 | 73 | class HashTableError : public PageFSError {}; 74 | 75 | } 76 | 77 | #endif -------------------------------------------------------------------------------- /src/frontend/lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | /* 3 | * scan.l: lex spec for RQL 4 | * 5 | * Authors: Dallan Quass 6 | * Jan Jannink 7 | * 8 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 9 | */ 10 | 11 | #include 12 | #include "sqleast.h" /* parse.h needs the definition of real */ 13 | #include "parser_internal.h" /* y.tab.h needs the definition of NODE */ 14 | #include "parser.hpp" 15 | 16 | using namespace sqleast; 17 | 18 | static int get_id(char *s); /* defined in scanhelp.c */ 19 | static char *get_qstring(char *qstring, int len); 20 | 21 | %} 22 | letter [A-Za-z] 23 | digit [0-9] 24 | num {digit}+ 25 | s_num [+\-]?{num} 26 | %x comment 27 | %x shell_cmd 28 | %% 29 | "/*" {BEGIN(comment);} 30 | [^*] {/* ignore the text of the comment */} 31 | "*/" {BEGIN(INITIAL);} 32 | \* {/* ignore *'s that aren't part of */} 33 | [ \n\t\r] {/* ignore spaces, tabs, and newlines (zxk: add /r)*/} 34 | {s_num} {sscanf(yytext, "%d", &yylval.ival); 35 | return T_INT;} 36 | {s_num}\.{num} {sscanf(yytext, "%f", &yylval.rval); 37 | return T_REAL;} 38 | {s_num}\.{num}[Ee]{s_num} {sscanf(yytext, "%f", &yylval.rval); 39 | return T_REAL;} 40 | '([^'\n]|(''))*\' {yylval.sval = get_qstring(yytext, yyleng); 41 | return T_QSTRING;} 42 | '([^'\n]|(''))*\n {printf("newline in string constant\n");} 43 | {letter}({letter}|{digit}|_)* {return get_id(yylval.sval = yytext);} 44 | "<" {return T_LT;} 45 | "<=" {return T_LE;} 46 | ">" {return T_GT;} 47 | ">=" {return T_GE;} 48 | "=" {return T_EQ;} 49 | "!=" {return T_NE;} 50 | "<>" {return T_NE;} 51 | ! {BEGIN(shell_cmd);} 52 | [*/+\-=<>':;,.|&()] {return yytext[0];} 53 | [^\n]* {yylval.sval = yytext; return T_SHELL_CMD; /* reorder this line to import multiline input*/ } 54 | \n {BEGIN(INITIAL);} 55 | <> {return T_EOF;} 56 | . {printf("illegal character [%c]\n", yytext[0]);} 57 | %% 58 | 59 | #include "lexerhelp.cpp" 60 | -------------------------------------------------------------------------------- /include/ix/index.h: -------------------------------------------------------------------------------- 1 | #ifndef IX_INDEX_H 2 | #define IX_INDEX_H 3 | 4 | #include "sqleast.h" 5 | #include "rm/filehandle.h" 6 | 7 | namespace sqleast { 8 | namespace ix { 9 | 10 | const int B_PLUS_TREE_BRANCH = 4; // k's max value when not leaf node 11 | 12 | struct Node { 13 | bool isLeaf; 14 | int size; // if leaf then size of n and k; if not then size of k; 15 | RID parent; 16 | RID n[B_PLUS_TREE_BRANCH + 2]; 17 | int k[B_PLUS_TREE_BRANCH + 2]; 18 | 19 | int getPosition(int value); 20 | bool insertN(RID value, int position); 21 | bool insertK(int value, int position); 22 | bool removeN(int position); 23 | bool removeK(int position); 24 | }; 25 | 26 | struct IndexInfo { 27 | int indexSize; 28 | int rootPageNum; 29 | int rootSlotNum; 30 | }; 31 | 32 | class IndexScan; 33 | class Index { 34 | 35 | public: 36 | friend class IndexScan; 37 | 38 | Index(const char *indexName); 39 | ~Index(); 40 | 41 | RID insertEntry(int key, RID value); 42 | RID searchEntry(int key); 43 | bool removeEntry(int key); 44 | 45 | void printIndex(); 46 | 47 | static void createIndex(const char *indexName); 48 | 49 | private: 50 | rm::FileHandle handle_; 51 | RID hot_; 52 | IndexInfo indexInfo_; 53 | 54 | RID getRootRID(); 55 | void setRoot(RID rid); 56 | void getNode(RID rid, Node &node); // client firstly create a node instance, then pass it to the "get node" method 57 | void commitNode(RID rid, const Node &node); // after a part of "write" operation, call this function to write to disk 58 | RID allocateNode(); 59 | RID releaseNode(RID rid); 60 | void forcePages(); 61 | int getIndexSize(); 62 | void incIndexSize(); 63 | void decIndexSize(); 64 | void commitIndexInfo(); 65 | void getRoot(Node &node); 66 | void solveOverFlow(RID rid); 67 | void solveUnderFlow(RID rid); 68 | 69 | void printNode(RID rid); 70 | }; 71 | } 72 | } 73 | 74 | 75 | #endif -------------------------------------------------------------------------------- /include/ql/query.h: -------------------------------------------------------------------------------- 1 | #ifndef QL_QUERY_H 2 | #define QL_QUERY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "sm/dbmanager.h" 9 | 10 | namespace sqleast { 11 | 12 | // extern sm::DBManager dbManager; 13 | 14 | namespace ql { 15 | enum SupportedQueryType { 16 | Q_CREATE_DB, 17 | Q_DROP_DB, 18 | Q_USE_DB, 19 | Q_SHOW_TABLES, 20 | Q_CREATE_TABLE, 21 | Q_DROP_TABLE, 22 | Q_DESC_TABLE, 23 | 24 | Q_INSERT, 25 | Q_DELETE, 26 | Q_UPDATE, 27 | Q_SEARCH 28 | }; 29 | 30 | struct StructuredQuery { 31 | SupportedQueryType type; 32 | virtual void execute() {std::cerr << "NOT IMPL" << std::endl;} 33 | }; 34 | 35 | struct SingleStringQuery: public StructuredQuery { 36 | char name[MAX_NAME_LENGTH + 1]; 37 | void execute(); 38 | }; 39 | 40 | struct CreateTableQuery: public StructuredQuery { 41 | char name[MAX_NAME_LENGTH + 1]; 42 | AttrInfo attrs[MAX_ATTR_NUM]; 43 | int attrNum; 44 | 45 | void execute(); 46 | }; 47 | 48 | /* insert */ 49 | struct InsertAttrItem { 50 | int iValue; 51 | std::string sValue; 52 | AttrType type; 53 | }; 54 | 55 | typedef std::vector< InsertAttrItem > InsertItem; 56 | 57 | struct InsertQuery: public StructuredQuery { 58 | char relName[MAX_NAME_LENGTH + 1]; 59 | std::vector v; 60 | 61 | void execute(); 62 | }; 63 | 64 | /* where item */ 65 | struct WhereItem { 66 | CompOp op; 67 | int iValue; 68 | std::string sValue; 69 | AttrType type; 70 | std::string op1; 71 | std::string op2; 72 | int useOp2; // 1 means op1 CompOp op2 73 | }; 74 | 75 | enum LogicOp {AND_OP, OR_OP}; 76 | 77 | struct WhereClause { 78 | std::vector item; 79 | std::vector op; 80 | // op.size == item.size - 1; 81 | }; 82 | 83 | /* delete */ 84 | struct DeleteQuery : public StructuredQuery { 85 | std::string relName; 86 | WhereClause where; 87 | }; 88 | 89 | } 90 | } 91 | 92 | #endif -------------------------------------------------------------------------------- /src/sm/systemmanager.cpp: -------------------------------------------------------------------------------- 1 | #include "rm/recordmanager.h" 2 | #include "sm/systemmanager.h" 3 | #include "sm/exception.h" 4 | #include "unistd.h" 5 | 6 | #include "boost/filesystem.hpp" 7 | 8 | namespace sqleast { 9 | namespace sm { 10 | 11 | int SystemManager::working_ = 0; 12 | 13 | void SystemManager::createDB(char const *dbName) { 14 | char name[MAX_PATH_LENGTH + 1]; 15 | memset(name, 0, sizeof(name)); 16 | getcwd(name, MAX_PATH_LENGTH); // system dependent 17 | strcat(name, PATH_SEP); 18 | strcat(name, dbName); 19 | std::cout << "[TARGET-DB]" << name << std::endl; 20 | if (isDirectory(name)) { 21 | throw DBExistsException(); 22 | return; 23 | } 24 | mkdir(name, 0777); 25 | chdir(dbName); 26 | // rm::RecordManager::createFile(appendRelCatalogExt(dbName).c_str(), sizeof(RelInfo) + FLAG_SIZE); 27 | // rm::RecordManager::createFile(appendAttrCatalogExt(dbName).c_str(), sizeof(DataAttrInfo) + FLAG_SIZE); 28 | rm::RecordManager::createFile(REL_CATALOG, sizeof(RelInfo) + FLAG_SIZE); 29 | rm::RecordManager::createFile(ATTR_CATALOG, sizeof(DataAttrInfo) + FLAG_SIZE); 30 | chdir(".."); 31 | } 32 | 33 | void SystemManager::useDB(char const *dbName) { 34 | if (working_) 35 | closeDB(); 36 | char name[MAX_PATH_LENGTH + 1]; 37 | memset(name, 0, sizeof(name)); 38 | getcwd(name, MAX_PATH_LENGTH); // system dependent 39 | strcat(name, PATH_SEP); 40 | strcat(name, dbName); 41 | std::cout << "[TARGET-DB]" << name << std::endl; 42 | if (!isDirectory(name)) { 43 | throw DBNotExistsException(); 44 | return; 45 | } 46 | chdir(dbName); 47 | working_ = 1; 48 | } 49 | 50 | void SystemManager::closeDB() { 51 | if (!working_) return; 52 | chdir(".."); 53 | working_ = 0; 54 | } 55 | 56 | void SystemManager::destroyDB(char const *dbName) { 57 | if (working_) 58 | closeDB(); 59 | char name[MAX_PATH_LENGTH + 1]; 60 | memset(name, 0, sizeof(name)); 61 | getcwd(name, MAX_PATH_LENGTH); // system dependent 62 | strcat(name, PATH_SEP); 63 | strcat(name, dbName); 64 | std::cout << "[TARGET-DB]" << name << std::endl; 65 | if (!isDirectory(name)) { 66 | throw DBNotExistsException(); 67 | return; 68 | } 69 | boost::filesystem::remove_all(boost::filesystem::path(dbName)); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /include/rm/filehandle.h: -------------------------------------------------------------------------------- 1 | #ifndef RM_FILEHANDLE_H 2 | #define RM_FILEHANDLE_H 3 | 4 | #include "recordmanager.h" 5 | 6 | namespace sqleast { 7 | namespace rm { 8 | 9 | class FileHandle { 10 | 11 | public: 12 | FileHandle(FileId fid); 13 | FileHandle() : fs_(pagefs::PageFS::getInstance()){ 14 | } 15 | FileHandle &operator= (const FileHandle &f) { 16 | fid_ = f.fid_; 17 | FileInfo *infoPtr = (FileInfo*)fs_.loadPage(fid_, 0); 18 | info_ = *(infoPtr); 19 | return *this; 20 | } 21 | ~FileHandle(); 22 | 23 | void getRec(RID rid, Record &r); 24 | void insertRec(Record &r); 25 | RID declareRec(); 26 | void updateRec(Record &r); 27 | void deleteRec(const Record &r); 28 | void deleteRec(RID rid); 29 | void forcePages(); 30 | inline FileInfo getInfo() { return info_; } 31 | inline char *getFileInfo() { return fs_.loadPage(fid_, 0); } 32 | 33 | /* for performance concern, 3 dangerous quick callable */ 34 | char *getRecDataPtr(RID rid); 35 | inline void releaseRecDataPtr(RID rid) { unpinPage(rid.pageNum); } 36 | inline void commitPage(int pageNum) { fs_.markDirty(fid_, pageNum); } 37 | 38 | private: 39 | FileId fid_; 40 | FileInfo info_; 41 | pagefs::PageFS &fs_; 42 | 43 | inline void commitInfo() { 44 | char *pData = fs_.loadPage(fid_, 0); 45 | memcpy(pData, &info_, sizeof(info_)); 46 | fs_.markDirty(fid_, 0); 47 | } 48 | 49 | inline void unpinPage(int pageNum) { 50 | fs_.unpinPage(fid_, pageNum); 51 | } 52 | 53 | inline PageHeader getPageHeader(char *pData) { 54 | return PageHeader( 55 | *(int*)(pData), // next page 56 | *(int*)(pData + sizeof(int)), // empty slot num 57 | pData + sizeof(PageHeader) - sizeof(char *) // bitmap 58 | ); 59 | } 60 | 61 | inline void writePageHeader(char *pData, PageHeader &pHeader) { 62 | *(int*)pData = pHeader.nextPage; 63 | pData += sizeof(int); 64 | *(int*)pData = pHeader.emptySlot; 65 | pData += sizeof(int); 66 | } 67 | 68 | inline char *moveToRec(char *pData) { 69 | PageHeader header = getPageHeader(pData); 70 | return header.slotBitmap + info_.slotBitmapSize; 71 | } 72 | 73 | int newPage(); 74 | RID allocateRec(); 75 | }; 76 | } 77 | } 78 | 79 | #endif -------------------------------------------------------------------------------- /test/testrm.cpp: -------------------------------------------------------------------------------- 1 | #include "rm/recordmanager.h" 2 | #include "rm/filehandle.h" 3 | #include "rm/filescan.h" 4 | 5 | using namespace sqleast; 6 | using namespace sqleast::rm; 7 | using namespace std; 8 | 9 | void testCreateFile() { 10 | int n; 11 | cout << "Record size = "; 12 | cin >> n; 13 | RecordManager::createFile("rmtest.db", n, true); 14 | FileHandle f = RecordManager::openFile("rmtest.db"); 15 | cout << "File created with record size " << n << endl; 16 | FileInfo info = f.getInfo(); 17 | cout << "Slot per page = " << info.slotPerPage << endl; 18 | cout << "Slot bitmap size = " << info.slotBitmapSize << endl; 19 | cout << "Total page num = " << info.totalPageNum << endl; 20 | } 21 | 22 | void testDestroyFile() { 23 | RecordManager::destroyFile("rmtest.db"); 24 | } 25 | 26 | void testStoreRecords() { 27 | int flagLen = sizeof(int); 28 | int dataLen = 32; 29 | int bitmapLen = 0; 30 | int rLen = flagLen + dataLen + bitmapLen; 31 | RecordManager::createFile("rmtest.db", rLen, true); 32 | FileHandle f = RecordManager::openFile("rmtest.db"); 33 | Record r(rLen); 34 | for (int i = 0; i < 20; i++) { 35 | memset(r.rData, 0, r.size); 36 | sprintf(r.rData + sizeof(int), "fuck you baby no %d", i); 37 | f.insertRec(r); 38 | } 39 | } 40 | 41 | void interativeTest() { 42 | 43 | struct myData { 44 | int a, b; 45 | char c[10]; 46 | }; 47 | string cmd; 48 | size_t rLen = FLAG_SIZE + sizeof(myData) + 1; 49 | cout << "BOUND" << endl; 50 | RecordManager::createFile("rmtest2.db", rLen, true); 51 | FileHandle f = RecordManager::openFile("rmtest2.db"); 52 | cout << "[INTERACTIVE TEST]" << endl; 53 | Record r(rLen); 54 | int flag; 55 | string c; 56 | while (true) { 57 | cin >> cmd; 58 | flag = 0; 59 | switch (cmd[0]) { 60 | case 'Q': flag = 1; break; 61 | case 'I': 62 | int a, b; 63 | cin >> a >> b >> c; 64 | myData d; 65 | d.a = a; 66 | d.b = b; 67 | strcpy(d.c, c.c_str()); 68 | memcpy(r.getData(), &d, sizeof(d)); 69 | f.insertRec(r); 70 | cout << r.rid.pageNum << " " << r.rid.slotNum << endl; 71 | break; 72 | case 'D': 73 | int pageNum, slotNum; 74 | cin >> pageNum >> slotNum; 75 | f.deleteRec(RID(pageNum, slotNum)); 76 | default: 77 | cout << "UNKNOWN COMMAND" << endl; 78 | } 79 | if (flag) break; 80 | } 81 | } 82 | 83 | int main() { 84 | // cout << "Page size = " << pagefs::PAGE_SIZE << endl; 85 | // testCreateFile(); 86 | // testDestroyFile(); 87 | // testStoreRecords(); 88 | interativeTest(); 89 | return 0; 90 | } -------------------------------------------------------------------------------- /test/testpagefs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pagefs/pagefs.h" 5 | 6 | using namespace std; 7 | 8 | void testSingleIO1() { 9 | cout << "single io 1 test begin!" << endl; 10 | pagefs::PageFS &fs = pagefs::PageFS::getInstance(); 11 | fs.createFile("test.db", true); 12 | cout << "created!" << endl; 13 | pagefs::FileId f = fs.openFile("test.db"); 14 | cout << "opened!" << endl; 15 | char *c = fs.loadPage(f, 0); 16 | cout << "loaded!" << endl; 17 | assert(c != nullptr); 18 | strcat(c, "Hello world, how are you?"); 19 | cout << "written!" << endl; 20 | fs.markDirty(f, 0); 21 | fs.forcePage(f, 0); 22 | cout << "forced!" << endl; 23 | } 24 | 25 | void testSingleIO2() { 26 | cout << "single io 2 test begin!" << endl; 27 | pagefs::PageFS &fs = pagefs::PageFS::getInstance(); 28 | pagefs::FileId f = fs.openFile("test.db"); 29 | cout << "opened!" << endl; 30 | char *c = fs.loadPage(f, 0); 31 | cout << "loaded!" << endl; 32 | assert(c != nullptr); 33 | strcat(c, "This is a test."); 34 | cout << "written!" << endl; 35 | fs.markDirty(f, 0); 36 | fs.forcePage(f, 0); 37 | cout << "forced!" << endl; 38 | } 39 | 40 | void testSingleIO3() { 41 | char msg[200]; 42 | cout << "single io 3 test begin!" << endl; 43 | pagefs::PageFS &fs = pagefs::PageFS::getInstance(); 44 | // fs.createFile("test3.db", true); 45 | pagefs::FileId f = fs.openFile("test3.db"); 46 | cout << "opened!" << endl; 47 | for (int i = 0; i < 20; i++) { 48 | cout << i << endl; 49 | char *c = fs.loadPage(f, i); 50 | sprintf(msg, "Page No.%d", i); 51 | cout << "loaded!" << endl; 52 | assert(c != nullptr); 53 | memset(c, 0, pagefs::PAGE_SIZE); 54 | strcpy(c, msg); 55 | cout << "written!" << endl; 56 | fs.markDirty(f, i); 57 | fs.unpinPage(f, i); 58 | fs.forcePage(f, i); 59 | cout << i << " done!" << endl; 60 | } 61 | } 62 | 63 | void testBuffer() { 64 | pagefs::PageFS &fs = pagefs::PageFS::getInstance(); 65 | cout << "buffer test begin!" << endl; 66 | pagefs::FileId f = fs.openFile("test.db"); 67 | char *a1 = fs.loadPage(f, 0); 68 | char *a2 = fs.loadPage(f, 1); 69 | char *a3 = fs.loadPage(f, 2); 70 | char *a4 = fs.loadPage(f, 3); 71 | } 72 | 73 | void interactiveTest() { 74 | using namespace pagefs; 75 | PageFS &fs = PageFS::getInstance(); 76 | char op; 77 | char *c; 78 | int page, flag = 0; 79 | FileId f = fs.openFile("test3.db"); 80 | string s; 81 | while (true) { 82 | cin >> op >> page; 83 | cout << "[CMD]$ "; 84 | switch (op) { 85 | case 'P': 86 | c = fs.loadPage(f, page); 87 | cout << "[P]" << c << endl; 88 | fs.unpinPage(f, page); 89 | break; 90 | case 'W': 91 | c = fs.loadPage(f, page); 92 | strcpy(c, "hahaha"); 93 | fs.markDirty(f, page); 94 | fs.unpinPage(f, page); 95 | break; 96 | case 'S': 97 | fs.printState(cout); 98 | break; 99 | case 'Q': 100 | flag = 1; 101 | break; 102 | default: 103 | break; 104 | } 105 | if (flag) break; 106 | } 107 | } 108 | 109 | int main() { 110 | testBuffer(); 111 | // testSingleIO2(); 112 | // testSingleIO3(); 113 | // interactiveTest(); 114 | return 0; 115 | } -------------------------------------------------------------------------------- /src/rm/filescan.cpp: -------------------------------------------------------------------------------- 1 | #include "rm/filescan.h" 2 | #include "rm/filehandle.h" 3 | #include "rm/exception.h" 4 | 5 | namespace sqleast { 6 | namespace rm { 7 | 8 | FileScan::FileScan(FileHandle &handle, 9 | AttrType attrType, int attrLength, int attrOffset, 10 | int nullBitOffset, int nullBitMask, 11 | CompOp compOp, void *value) : 12 | handle_(handle), attrType_(attrType), attrLength_(attrLength), attrOffset_(attrOffset), 13 | nullBitOffset_(nullBitOffset), nullBitMask_(nullBitMask), value_(value), compOp_(compOp), 14 | pageNum_(1), slotNum_(0), info_(handle.getInfo()), c_(info_.recordSize) 15 | { 16 | } 17 | 18 | FileScan::~FileScan() { 19 | } 20 | 21 | Record &FileScan::next() { 22 | do { 23 | if (pageNum_ == info_.totalPageNum) { 24 | c_.rid.pageNum = -1; 25 | c_.rid.slotNum = -1; 26 | return c_; 27 | } 28 | handle_.getRec(RID(pageNum_, slotNum_), c_); 29 | if (c_.getFlag() & REC_ALIVE) { 30 | if (compOp_ == NO_OP) { 31 | break; 32 | } else if (compOp_ == IS_NULL_OP) { 33 | // std::cout << (int)*(unsigned char*)(c_.getData() + nullBitOffset_); 34 | if ((*(c_.getData() + nullBitOffset_)) & nullBitMask_) 35 | break; 36 | } else if (compOp_ == NOT_NULL_OP) { 37 | if (((*(c_.getData() + nullBitOffset_)) & nullBitMask_) == 0) 38 | break; 39 | } else if (attrType_ == INT) { 40 | int attr = *(int*)(c_.getData() + attrOffset_); 41 | int flag = 0; 42 | // NO_OP, EQ_OP, LT_OP, GT_OP, LE_OP, GE_OP, NE_OP, IS_NULL_OP, NOT_NULL_OP 43 | switch (compOp_) { 44 | case EQ_OP: flag = (*(int*)value_ == attr); break; 45 | case LT_OP: flag = (*(int*)value_ > attr); break; 46 | case GT_OP: flag = (*(int*)value_ < attr); break; 47 | case LE_OP: flag = (*(int*)value_ >= attr); break; 48 | case GE_OP: flag = (*(int*)value_ <= attr); break; 49 | case NE_OP: flag = (*(int*)value_ != attr); break; 50 | default: flag = 0; 51 | } 52 | if (flag) break; 53 | } else if (attrType_ == STRING) { 54 | char *attr = c_.getData() + attrOffset_; 55 | char *value = (char *) value_; 56 | int i = strncmp(attr, value, (size_t)attrLength_); 57 | if (i == 0) { 58 | if (compOp_ == EQ_OP || compOp_ == LE_OP || compOp_ == GE_OP) 59 | break; 60 | } else if (i < 0) { 61 | if (compOp_ == LT_OP || compOp_ == LE_OP || compOp_ == NE_OP) 62 | break; 63 | } else { 64 | if (compOp_ == GT_OP || compOp_ == GE_OP || compOp_ == NE_OP) 65 | break; 66 | } 67 | } 68 | } 69 | slotNum_ += 1; 70 | if (slotNum_ == info_.slotPerPage) { 71 | slotNum_ = 0; 72 | pageNum_ += 1; 73 | } 74 | } while (true); 75 | slotNum_ += 1; 76 | if (slotNum_ == info_.slotPerPage) { 77 | slotNum_ = 0; 78 | pageNum_ += 1; 79 | } 80 | return c_; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /include/pagefs/pagefs.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGEFS_H 2 | #define PAGEFS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "exception.h" 9 | 10 | #include "../debug.h" 11 | 12 | namespace pagefs { 13 | 14 | // pagefs 15 | const int MAX_FILE_NUM(32767); 16 | const int PAGE_SIZE(8192); 17 | const int MAX_BUFFER_SIZE(64); // has to be 2^n for the performance concern 18 | const int MAX_BUFFER_SIZE_M1 = MAX_BUFFER_SIZE - 1; 19 | 20 | const int ALL_PAGES(-1); 21 | 22 | // file system 23 | const int FILE_NAME_MAX_LEN(40); 24 | 25 | typedef int FileId; 26 | 27 | struct FileEntry { 28 | char fileName[FILE_NAME_MAX_LEN]; 29 | int fileId; 30 | int counter; 31 | }; 32 | 33 | struct BufferPage { 34 | char *data; 35 | int dirty; 36 | int pinned; 37 | int fileId; 38 | int pageNum; 39 | BufferPage &operator=(const BufferPage &p) { 40 | data = p.data; 41 | dirty = p.dirty; 42 | pinned = p.pinned; 43 | fileId = p.fileId; 44 | pageNum = p.pageNum; 45 | return *this; 46 | } 47 | }; 48 | 49 | struct LRUListNode; 50 | struct LRUHashItem; 51 | 52 | struct LRUListNode { 53 | LRUListNode *next, *prev; 54 | LRUHashItem *item; 55 | LRUListNode(): next(nullptr), prev(nullptr), item(nullptr) {} 56 | }; 57 | 58 | struct LRUHashItem { 59 | BufferPage data; 60 | LRUListNode *node; // nullptr means unused 61 | int hashValue; 62 | LRUHashItem(): node(nullptr) {} 63 | LRUHashItem &operator=(const LRUHashItem &h) { 64 | data = h.data; 65 | node = h.node; 66 | hashValue = h.hashValue; 67 | return *this; 68 | } 69 | }; 70 | 71 | struct LRUHash { 72 | 73 | LRUHash(); 74 | ~LRUHash(); 75 | LRUHashItem table[MAX_BUFFER_SIZE]; 76 | int total; 77 | 78 | inline int hash(int fileId, int pageNum) { 79 | return ((fileId << 5) + pageNum) & MAX_BUFFER_SIZE_M1; 80 | } 81 | 82 | LRUHashItem *get(int fileId, int pageNum); 83 | LRUHashItem pop(int fileId, int pageNum); 84 | LRUHashItem popByKey(int key); 85 | LRUHashItem *add(BufferPage &p, LRUListNode *node); 86 | 87 | }; 88 | 89 | struct LRUList { 90 | 91 | LRUList(); 92 | ~LRUList(); 93 | 94 | LRUListNode *push_head(LRUHashItem *p); 95 | LRUListNode *push_back(LRUHashItem *p); 96 | void move_back(LRUListNode *p); 97 | inline LRUHashItem *pop_head() { return remove(head); } 98 | inline LRUHashItem *pop_back() { return remove(tail); } 99 | LRUHashItem *remove(LRUListNode *p); 100 | 101 | LRUListNode *head, *tail; 102 | }; 103 | 104 | class PageFS { 105 | 106 | public: 107 | 108 | static PageFS &getInstance() { 109 | static PageFS instance_; 110 | return instance_; 111 | } 112 | 113 | ~PageFS(); 114 | 115 | void createFile(const char *fileName, bool override = false); 116 | void destroyFile(const char *fileName); 117 | FileId openFile(const char *fileName); 118 | void closeFile(FileId f); 119 | 120 | char *loadPage(int fileId, int pageNum); 121 | void forcePage(int fileId, int pageNum); 122 | void unpinPage(int fileId, int pageNum); 123 | void markDirty(int fileId, int pageNum); 124 | void commitAll(int fileId); // fileId == -1: commit all whatever the fileid is 125 | 126 | void printState(std::ostream &os); 127 | 128 | 129 | private: 130 | 131 | PageFS(); 132 | 133 | FileEntry entries_[MAX_FILE_NUM]; 134 | int entryCnt_; 135 | FILE* filePtr_[MAX_FILE_NUM]; 136 | LRUHash lruTable_; 137 | LRUList lruList_; 138 | 139 | /* buffer */ 140 | 141 | void pinPage(int fileId, int pageNum); 142 | bool commitOnePage(); 143 | bool writeBack(BufferPage p); 144 | 145 | }; 146 | 147 | }; 148 | 149 | #endif -------------------------------------------------------------------------------- /include/sqleast.h: -------------------------------------------------------------------------------- 1 | #ifndef SQLEAST_DEF_H 2 | #define SQLEAST_DEF_H 3 | 4 | #define PATH_SEP "/" 5 | 6 | #include "pagefs/pagefs.h" 7 | 8 | namespace sqleast { 9 | 10 | /* Definitions 11 | 12 | */ 13 | 14 | const int MAX_COND_NUM = 6; 15 | const int MAX_NAME_LENGTH = 20; 16 | const int MAX_PATH_LENGTH = 200; 17 | 18 | enum AttrType {INT, STRING, FLOAT, NULLV}; 19 | 20 | enum CompOp {NO_OP, EQ_OP, LT_OP, GT_OP, LE_OP, GE_OP, NE_OP, IS_NULL_OP, NOT_NULL_OP}; 21 | 22 | /* File 23 | 24 | */ 25 | 26 | typedef pagefs::FileId FileId; 27 | 28 | /* Record 29 | 30 | * A record is a combination of validation info, real data and null bitmap. 31 | * rec = INFO + DATA + NULLBITMAP 32 | * where INFO is a flag unsigned int. 33 | */ 34 | 35 | const size_t FLAG_SIZE = sizeof(int); 36 | 37 | struct RID { 38 | int pageNum; 39 | int slotNum; 40 | 41 | RID(): pageNum(-1), slotNum(-1) {} 42 | RID(const int _pageNum, const int _slotNum): pageNum(_pageNum), slotNum(_slotNum) {} 43 | bool operator==(const RID &b) { 44 | return pageNum == b.pageNum && slotNum == b.slotNum; 45 | } 46 | }; 47 | 48 | struct Record { 49 | RID rid; 50 | char *rData; 51 | const size_t size; 52 | 53 | Record(int _size): size((size_t)_size) { 54 | rData = new char[size]; 55 | } 56 | 57 | ~Record() { 58 | delete[] rData; 59 | } 60 | 61 | Record &operator=(const Record &r) { 62 | rid = r.rid; 63 | rData = r.rData; 64 | delete[] rData; 65 | memcpy(rData, r.rData, size); 66 | return *this; 67 | } 68 | 69 | inline int getFlag() { 70 | return *(int*)rData; 71 | } 72 | inline char *getData() { 73 | return rData + FLAG_SIZE; 74 | } 75 | 76 | inline void clear() { 77 | memset(rData, 0, size); 78 | } 79 | }; 80 | 81 | enum RecordFlags { 82 | REC_ALIVE = 1 83 | }; 84 | 85 | const int MAX_ATTR_NUM = 100; 86 | 87 | enum AttrProp { 88 | PRIMARY_KEY 89 | }; 90 | 91 | struct AttrInfo { 92 | char *attrName; 93 | AttrType attrType; 94 | int attrLength; 95 | int nullable; 96 | bool isPrimary; 97 | }; 98 | 99 | struct RelAttr{ 100 | char *relName; // Relation name (may be NULL) 101 | char *attrName; // Attribute name 102 | 103 | // Print function 104 | // friend std::ostream &operator<<(std::ostream &s, const RelAttr &ra); 105 | }; 106 | 107 | struct Value{ 108 | AttrType type; /* type of value */ 109 | void *data; /* value */ 110 | /* print function */ 111 | // friend std::ostream &operator<<(std::ostream &s, const Value &v); 112 | }; 113 | 114 | struct Condition{ 115 | RelAttr lhsAttr; /* left-hand side attribute */ 116 | CompOp op; /* comparison operator */ 117 | int bRhsIsAttr; /* TRUE if the rhs is an attribute, */ 118 | /* in which case rhsAttr below is valid;*/ 119 | /* otherwise, rhsValue below is valid. */ 120 | RelAttr rhsAttr; /* right-hand side attribute */ 121 | Value rhsValue; /* right-hand side value */ 122 | /* print function */ 123 | // friend std::ostream &operator<<(std::ostream &s, const Condition &c); 124 | 125 | }; 126 | 127 | //std::ostream &operator<<(std::ostream &s, const CompOp &op); 128 | //std::ostream &operator<<(std::ostream &s, const AttrType &at); 129 | 130 | // 131 | // Parse function 132 | // 133 | 134 | void sqleast_parse(); 135 | 136 | // 137 | // Error printing function; calls component-specific functions 138 | // 139 | 140 | // bQueryPlans is allocated by parse.y. When bQueryPlans is 1 then the 141 | // query plan chosen for the SFW query will be displayed. When 142 | // bQueryPlans is 0 then no query plan is shown. 143 | extern int bQueryPlans; 144 | 145 | /* utils */ 146 | inline int lower_equal(const char *a, const char *b) { 147 | char t; 148 | while (*a && *b) { 149 | t = *a; 150 | if (*a < 'a') t += 'z' - 'Z'; 151 | if (t != *b) return 0; 152 | a++; 153 | b++; 154 | } 155 | return (!*a && !*b); 156 | } 157 | 158 | } 159 | 160 | 161 | #endif -------------------------------------------------------------------------------- /src/frontend/parser.hpp: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 2.3. */ 2 | 3 | /* Skeleton interface for Bison's Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 6 | Free Software Foundation, Inc. 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2, or (at your option) 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | Boston, MA 02110-1301, USA. */ 22 | 23 | /* As a special exception, you may create a larger work that contains 24 | part or all of the Bison parser skeleton and distribute that work 25 | under terms of your choice, so long as that work isn't itself a 26 | parser generator using the skeleton or a modified version thereof 27 | as a parser skeleton. Alternatively, if you modify or redistribute 28 | the parser skeleton itself, you may (at your option) remove this 29 | special exception, which will cause the skeleton and the resulting 30 | Bison output files to be licensed under the GNU General Public 31 | License without this special exception. 32 | 33 | This special exception was added by the Free Software Foundation in 34 | version 2.2 of Bison. */ 35 | 36 | /* Tokens. */ 37 | #ifndef YYTOKENTYPE 38 | # define YYTOKENTYPE 39 | /* Put the tokens into the symbol table, so that GDB and other debuggers 40 | know about them. */ 41 | enum yytokentype { 42 | RW_CREATE = 258, 43 | RW_DROP = 259, 44 | RW_SHOW = 260, 45 | RW_DESC = 261, 46 | RW_USE = 262, 47 | RW_TABLES = 263, 48 | RW_TABLE = 264, 49 | RW_INDEX = 265, 50 | RW_DATABASE = 266, 51 | RW_LOAD = 267, 52 | RW_SET = 268, 53 | RW_HELP = 269, 54 | RW_PRINT = 270, 55 | RW_EXIT = 271, 56 | RW_SELECT = 272, 57 | RW_FROM = 273, 58 | RW_WHERE = 274, 59 | RW_INSERT = 275, 60 | RW_DELETE = 276, 61 | RW_UPDATE = 277, 62 | RW_AND = 278, 63 | RW_IS = 279, 64 | RW_NOT = 280, 65 | RW_NULL = 281, 66 | RW_INTO = 282, 67 | RW_VALUES = 283, 68 | RW_PRIMARY = 284, 69 | RW_KEY = 285, 70 | RW_INT = 286, 71 | RW_VCHAR = 287, 72 | T_EQ = 288, 73 | T_LT = 289, 74 | T_LE = 290, 75 | T_GT = 291, 76 | T_GE = 292, 77 | T_NE = 293, 78 | T_EOF = 294, 79 | NOTOKEN = 295, 80 | RW_RESET = 296, 81 | RW_IO = 297, 82 | RW_BUFFER = 298, 83 | RW_RESIZE = 299, 84 | RW_QUERY_PLAN = 300, 85 | RW_ON = 301, 86 | RW_OFF = 302, 87 | T_INT = 303, 88 | T_REAL = 304, 89 | T_STRING = 305, 90 | T_QSTRING = 306, 91 | T_SHELL_CMD = 307 92 | }; 93 | #endif 94 | /* Tokens. */ 95 | #define RW_CREATE 258 96 | #define RW_DROP 259 97 | #define RW_SHOW 260 98 | #define RW_DESC 261 99 | #define RW_USE 262 100 | #define RW_TABLES 263 101 | #define RW_TABLE 264 102 | #define RW_INDEX 265 103 | #define RW_DATABASE 266 104 | #define RW_LOAD 267 105 | #define RW_SET 268 106 | #define RW_HELP 269 107 | #define RW_PRINT 270 108 | #define RW_EXIT 271 109 | #define RW_SELECT 272 110 | #define RW_FROM 273 111 | #define RW_WHERE 274 112 | #define RW_INSERT 275 113 | #define RW_DELETE 276 114 | #define RW_UPDATE 277 115 | #define RW_AND 278 116 | #define RW_IS 279 117 | #define RW_NOT 280 118 | #define RW_NULL 281 119 | #define RW_INTO 282 120 | #define RW_VALUES 283 121 | #define RW_PRIMARY 284 122 | #define RW_KEY 285 123 | #define RW_INT 286 124 | #define RW_VCHAR 287 125 | #define T_EQ 288 126 | #define T_LT 289 127 | #define T_LE 290 128 | #define T_GT 291 129 | #define T_GE 292 130 | #define T_NE 293 131 | #define T_EOF 294 132 | #define NOTOKEN 295 133 | #define RW_RESET 296 134 | #define RW_IO 297 135 | #define RW_BUFFER 298 136 | #define RW_RESIZE 299 137 | #define RW_QUERY_PLAN 300 138 | #define RW_ON 301 139 | #define RW_OFF 302 140 | #define T_INT 303 141 | #define T_REAL 304 142 | #define T_STRING 305 143 | #define T_QSTRING 306 144 | #define T_SHELL_CMD 307 145 | 146 | 147 | 148 | 149 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 150 | typedef union YYSTYPE 151 | #line 49 "parser.y" 152 | { 153 | int ival; 154 | CompOp cval; 155 | float rval; 156 | char *sval; 157 | NODE *n; 158 | } 159 | /* Line 1529 of yacc.c. */ 160 | #line 161 "/Users/badpoet/Workspace/Repositories/SQLeast/src/frontend/parser.hpp" 161 | YYSTYPE; 162 | # define yystype YYSTYPE /* obsolescent; will be withdrawn */ 163 | # define YYSTYPE_IS_DECLARED 1 164 | # define YYSTYPE_IS_TRIVIAL 1 165 | #endif 166 | 167 | extern YYSTYPE yylval; 168 | 169 | -------------------------------------------------------------------------------- /src/ql/query.cpp.bak: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ql/query.h" 3 | #include "sm/systemmanager.h" 4 | #include "sm/exception.h" 5 | #include "sm/dbhandle.h" 6 | 7 | #define ZXK 8 | 9 | #ifdef ZXK 10 | 11 | namespace sqleast { 12 | 13 | namespace ql { 14 | 15 | sm::DBHandle *dbManager = nullptr; 16 | 17 | void SingleStringQuery::execute() { 18 | if (type == Q_CREATE_DB) { 19 | try { 20 | sm::SystemManager::createDB(name); 21 | } catch (sm::SMException e) { 22 | std::cerr << "[ERROR] DB already exists" << std::endl; 23 | } 24 | } else if (type == Q_DROP_DB) { 25 | try { 26 | sm::SystemManager::destroyDB(name); 27 | } catch (sm::SMException e) { 28 | std::cerr << "[ERROR] no such DB" << std::endl; 29 | } 30 | } else if (type == Q_USE_DB) { 31 | try { 32 | sm::SystemManager::useDB(name); 33 | } catch (sm::SMException e) { 34 | std::cerr << "[ERROR] no such DB" << std::endl; 35 | } 36 | if (dbManager != nullptr) delete dbManager; 37 | dbManager = new sm::DBHandle(name); 38 | } else if (type == Q_SHOW_TABLES) { 39 | if (dbManager == nullptr) { 40 | std::cerr << "[ERROR] DB not found" << std::endl; 41 | return; 42 | } 43 | dbManager->showTables(); 44 | } else if (type == Q_DESC_TABLE) { 45 | if (dbManager == nullptr) { 46 | std::cerr << "[ERROR] DB not found" << std::endl; 47 | return; 48 | } 49 | dbManager->descTable(name); 50 | } else if (type == Q_DROP_TABLE) { 51 | if (dbManager == nullptr) { 52 | std::cerr << "[ERROR] DB not found" << std::endl; 53 | return; 54 | } 55 | dbManager->dropTable(name); 56 | } 57 | } 58 | 59 | void CreateTableQuery::execute() { 60 | if (dbManager == nullptr) { 61 | std::cerr << "[ERROR] DB not found" << std::endl; 62 | return; 63 | } 64 | if (dbManager->findTable(name)) { 65 | std::cerr << "[ERROR] Relation exists" << std::endl; 66 | } 67 | dbManager->createTable(name, attrNum, attrs); 68 | } 69 | 70 | void InsertQuery::execute() { 71 | if (dbManager == nullptr) { 72 | std::cerr << "[ERROR] DB not found" << std::endl; 73 | return; 74 | } 75 | if (!dbManager->findTable(relName)) { 76 | std::cerr << "[ERROR] Realtion not found" << std::endl; 77 | return; 78 | } 79 | sm::RelAttrInfo relAttrInfo = dbManager->getRelAttrInfo(relName); 80 | rm::FileHandle db = rm::RecordManager::openFile(sm::SystemManager::appendDBExt(relName).c_str()); 81 | sm::RelInfo relInfo = dbManager->getRelInfo(relName); 82 | for (int i = 0; i < v.size(); i++) { 83 | if (v[i].size() > relAttrInfo.size()) { 84 | std::cerr << "[ERROR] Too many attributes" << std::endl; 85 | return; 86 | } 87 | for (int j = 0; j < relAttrInfo.size(); j++) { 88 | if (j >= v[i].size()) { 89 | if (!relAttrInfo[j].nullable) { 90 | std::cerr << "[ERROR] attribute " << j + 1 << " is not nullable" << std::endl; 91 | return; 92 | } 93 | } else { 94 | if (v[i][j].type != relAttrInfo[j].attrType) { 95 | std::cerr << "[ERROR] attribute " << j + 1 << "'s type dismatches" << std::endl; 96 | return; 97 | } 98 | if (relAttrInfo[j].isPrimary) { 99 | /* find key */ 100 | rm::FileScan scan(db, relAttrInfo[j].attrType, relAttrInfo[j].attrLength, 101 | relAttrInfo[j].offset, 0, 0, 102 | EQ_OP, v[i][j].type == INT ? (void *)&v[i][j].iValue : (void *)v[i][j].sValue.c_str()); 103 | Record &r = scan.next(); 104 | if (r.rid.pageNum >= 0) { 105 | std::cerr << "[ERROR] primary key conflict" << std::endl; 106 | return; 107 | } 108 | } 109 | } 110 | } 111 | } 112 | Record r((size_t)(relInfo.tupleLength + relInfo.bitmapSize + FLAG_SIZE)); 113 | for (int i = 0; i < v.size(); i++) { 114 | r.clear(); 115 | /* make record */ 116 | for (int j = 0; j < relAttrInfo.size(); j++) { 117 | if (j < v[i].size()) { 118 | char *p = r.getData() + relAttrInfo[j].offset; 119 | if (v[i][j].type == INT) 120 | memcpy(p, &(v[i][j].iValue), relAttrInfo[j].attrLength); 121 | else 122 | memcpy(p, v[i][j].sValue.c_str(), relAttrInfo[j].attrLength); 123 | } else { 124 | char *p = r.getData() + relAttrInfo[j].nullBitOffset; 125 | *p |= relAttrInfo[j].nullBitMask; 126 | } 127 | } 128 | db.insertRec(r); 129 | } 130 | } 131 | } 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /src/frontend/lexerhelp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * scanhelp.c: help functions for lexer 3 | * 4 | * Authors: Jan Jannink 5 | * Jason McHugh 6 | * 7 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 8 | * 9 | * 1997 Changes: "print", "buffer", "reset" and "io" added. 10 | * 1998 Changes: "resize", "queryplans", "on" and "off" added. 11 | * 12 | * 13 | * This file is not compiled separately; it is #included into lex.yy.c . 14 | */ 15 | 16 | /* 17 | * size of buffer of strings 18 | */ 19 | #define MAXCHAR 5000000 20 | #define MAXSTRINGLEN 500 21 | 22 | #include 23 | #include 24 | #include 25 | #include "parser_internal.h" 26 | #include "sqleast.h" 27 | #include "parser.hpp" 28 | 29 | using namespace sqleast; 30 | 31 | /* 32 | * buffer for string allocation 33 | */ 34 | 35 | static char charpool[MAXCHAR]; 36 | static int charptr = 0; 37 | 38 | static int lower(char *dst, char *src, int max); 39 | static char *mk_string(char *s, int len); 40 | 41 | /* 42 | * string_alloc: returns a pointer to a string of length len if possible 43 | */ 44 | static char *string_alloc(int len) 45 | { 46 | char *s; 47 | 48 | if(charptr + len > MAXCHAR){ 49 | fprintf(stderr, "out of memory (string pool)\n"); 50 | exit(1); 51 | } 52 | 53 | s = charpool + charptr; 54 | charptr += len; 55 | 56 | return s; 57 | } 58 | 59 | /* 60 | * reset_charptr: releases all memory allocated in preparation for the 61 | * next query. 62 | * 63 | * No return value. 64 | */ 65 | void reset_charptr(void) 66 | { 67 | charptr = 0; 68 | } 69 | 70 | /* 71 | * reset_scanner: resets the scanner after a syntax error 72 | * 73 | * No return value. 74 | */ 75 | void reset_scanner(void) 76 | { 77 | charptr = 0; 78 | yyrestart(yyin); 79 | } 80 | 81 | /* 82 | * get_id: determines whether s is a reserved word, and returns the 83 | * appropriate token value if it is. Otherwise, it returns the token 84 | * value corresponding to a string. If s is longer than the maximum token 85 | * length (MAXSTRINGLEN) then it returns NOTOKEN, so that the parser will 86 | * flag an error (this is a stupid kludge). 87 | */ 88 | static int get_id(char *s) 89 | { 90 | static char string[MAXSTRINGLEN]; 91 | int len; 92 | 93 | if((len = lower(string, s, MAXSTRINGLEN)) == MAXSTRINGLEN) 94 | return NOTOKEN; 95 | 96 | /* SM layer lexemes */ 97 | 98 | if(!strcmp(string, "create")) 99 | return yylval.ival = RW_CREATE; 100 | if(!strcmp(string, "drop")) 101 | return yylval.ival = RW_DROP; 102 | if(!strcmp(string, "show")) 103 | return yylval.ival = RW_SHOW; 104 | if(!strcmp(string, "use")) 105 | return yylval.ival = RW_USE; 106 | if(!strcmp(string, "desc")) 107 | return yylval.ival = RW_DESC; 108 | if(!strcmp(string, "table")) 109 | return yylval.ival = RW_TABLE; 110 | if(!strcmp(string, "database")) 111 | return yylval.ival = RW_DATABASE; 112 | if(!strcmp(string, "tables")) 113 | return yylval.ival = RW_TABLES; 114 | if(!strcmp(string, "index")) 115 | return yylval.ival = RW_INDEX; 116 | if(!strcmp(string, "load")) 117 | return yylval.ival = RW_LOAD; 118 | if(!strcmp(string, "help")) 119 | return yylval.ival = RW_HELP; 120 | if(!strcmp(string, "exit")) 121 | return yylval.ival = RW_EXIT; 122 | if(!strcmp(string, "print")) 123 | return yylval.ival = RW_PRINT; 124 | if(!strcmp(string, "set")) 125 | return yylval.ival = RW_SET; 126 | 127 | if(!strcmp(string, "and")) 128 | return yylval.ival = RW_AND; 129 | if(!strcmp(string, "is")) 130 | return yylval.ival = RW_IS; 131 | if(!strcmp(string, "not")) 132 | return yylval.ival = RW_NOT; 133 | if(!strcmp(string, "null")) 134 | return yylval.ival = RW_NULL; 135 | 136 | if(!strcmp(string, "primary")) 137 | return yylval.ival = RW_PRIMARY; 138 | if(!strcmp(string, "key")) 139 | return yylval.ival = RW_KEY; 140 | 141 | if(!strcmp(string, "into")) 142 | return yylval.ival = RW_INTO; 143 | if(!strcmp(string, "values")) 144 | return yylval.ival = RW_VALUES; 145 | 146 | 147 | /* QL layer lexemes */ 148 | if(!strcmp(string, "select")) 149 | return yylval.ival = RW_SELECT; 150 | if(!strcmp(string, "from")) 151 | return yylval.ival = RW_FROM; 152 | if(!strcmp(string, "where")) 153 | return yylval.ival = RW_WHERE; 154 | if(!strcmp(string, "insert")) 155 | return yylval.ival = RW_INSERT; 156 | if(!strcmp(string, "delete")) 157 | return yylval.ival = RW_DELETE; 158 | if(!strcmp(string, "update")) 159 | return yylval.ival = RW_UPDATE; 160 | 161 | /* IO Statistics lexemes */ 162 | if(!strcmp(string, "reset")) 163 | return yylval.ival = RW_RESET; 164 | if(!strcmp(string, "io")) 165 | return yylval.ival = RW_IO; 166 | 167 | if(!strcmp(string, "resize")) 168 | return yylval.ival = RW_RESIZE; 169 | if(!strcmp(string, "buffer")) 170 | return yylval.ival = RW_BUFFER; 171 | 172 | if(!strcmp(string, "queryplans")) 173 | return yylval.ival = RW_QUERY_PLAN; 174 | if(!strcmp(string, "on")) 175 | return yylval.ival = RW_ON; 176 | if(!strcmp(string, "off")) 177 | return yylval.ival = RW_OFF; 178 | 179 | /* unresolved lexemes are strings */ 180 | 181 | yylval.sval = mk_string(s, len); 182 | return T_STRING; 183 | } 184 | 185 | /* 186 | * lower: copies src to dst, converting it to lowercase, stopping at the 187 | * end of src or after max characters. 188 | * 189 | * Returns: 190 | * the length of dst (which may be less than the length of src, if 191 | * src is too long). 192 | */ 193 | static int lower(char *dst, char *src, int max) 194 | { 195 | int len; 196 | 197 | for(len = 0; len < max && src[len] != '\0'; ++len){ 198 | dst[len] = src[len]; 199 | if(src[len] >= 'A' && src[len] <= 'Z') 200 | dst[len] += 'a' - 'A'; 201 | } 202 | dst[len] = '\0'; 203 | 204 | return len; 205 | } 206 | 207 | /* 208 | * get_qstring: removes the quotes from a quoted string, allocates 209 | * space for the resulting string. 210 | * 211 | * Returns: 212 | * a pointer to the new string 213 | */ 214 | static char *get_qstring(char *qstring, int len) 215 | { 216 | /* replace ending quote with \0 */ 217 | qstring[len - 1] = '\0'; 218 | 219 | /* copy everything following beginning quote */ 220 | return mk_string(qstring + 1, len - 2); 221 | } 222 | 223 | /* 224 | * mk_string: allocates space for a string of length len and copies s into 225 | * it. 226 | * 227 | * Returns: 228 | * a pointer to the new string 229 | */ 230 | static char *mk_string(char *s, int len) 231 | { 232 | char *copy; 233 | 234 | /* allocate space for new string */ 235 | if((copy = string_alloc(len + 1)) == NULL){ 236 | printf("out of string space\n"); 237 | exit(1); 238 | } 239 | 240 | /* copy the string */ 241 | strncpy(copy, s, len + 1); 242 | return copy; 243 | } 244 | -------------------------------------------------------------------------------- /src/rm/filehandle.cpp: -------------------------------------------------------------------------------- 1 | #include "rm/bitmaputil.h" 2 | #include "rm/filehandle.h" 3 | #include "rm/exception.h" 4 | 5 | namespace sqleast { 6 | namespace rm { 7 | 8 | using namespace pagefs; 9 | 10 | FileHandle::FileHandle(FileId fid) : fid_(fid), fs_(PageFS::getInstance()) { 11 | FileInfo *infoPtr = (FileInfo*)fs_.loadPage(fid, 0); 12 | info_ = *(infoPtr); 13 | // std::cout << "[CONSTRUCTION]FileHandle " << fid << std::endl; 14 | } 15 | 16 | FileHandle::~FileHandle() { 17 | // std::cout << "[DESTRUCTION]FileHandle " << fid_ << std::endl; 18 | forcePages(); 19 | fs_.closeFile(fid_); 20 | } 21 | 22 | char *FileHandle::getRecDataPtr(RID rid) { 23 | char *pData = fs_.loadPage(fid_, rid.pageNum); 24 | pData = moveToRec(pData); 25 | pData += rid.slotNum * info_.recordSize; 26 | return pData; 27 | } 28 | 29 | void FileHandle::getRec(RID rid, Record &r) { 30 | if (r.size != info_.recordSize) 31 | throw RecordSizeError(); 32 | char *pData = fs_.loadPage(fid_, rid.pageNum); 33 | pData = moveToRec(pData); 34 | pData += rid.slotNum * info_.recordSize; 35 | memcpy(r.rData, pData, (size_t)r.size); 36 | r.rid = rid; 37 | unpinPage(rid.pageNum); 38 | } 39 | 40 | void FileHandle::updateRec(Record &r) { 41 | if (r.rid.pageNum <= 0 || r.rid.slotNum < 0) 42 | throw InvalidRecordException(); 43 | if (r.size != info_.recordSize) 44 | throw RecordSizeError(); 45 | int pNum = r.rid.pageNum; 46 | char *pData = fs_.loadPage(fid_, pNum); 47 | pData = moveToRec(pData); 48 | pData += r.rid.slotNum * info_.recordSize; 49 | *(int*)r.rData |= REC_ALIVE; 50 | memcpy(pData, r.rData, (size_t)(info_.recordSize)); 51 | commitPage(pNum); 52 | unpinPage(pNum); 53 | } 54 | 55 | RID FileHandle::allocateRec() { 56 | int pageNum = info_.firstEmptyPage; 57 | int slotNum = 0; 58 | while (true) { 59 | if (pageNum == 0) pageNum = newPage(); 60 | while (pageNum != 0) { 61 | char *pData = fs_.loadPage(fid_, pageNum); 62 | PageHeader pHeader = getPageHeader(pData); 63 | char *bitmap = pHeader.slotBitmap; 64 | char *records = pHeader.slotBitmap + info_.slotBitmapSize; 65 | bool found = false; 66 | 67 | while (bitmap < records) { 68 | if ((unsigned char) (*bitmap) != 255) { // find avaliable slot 69 | slotNum = Bitmap8Util::lowest0((unsigned char) *bitmap); 70 | *bitmap |= 1 << slotNum; 71 | pHeader.emptySlot -= 1; 72 | if (pHeader.emptySlot == 0) { // remove the page from empty array 73 | if (pHeader.nextPage != 0) { 74 | char *next = fs_.loadPage(fid_, pHeader.nextPage); 75 | PageHeader nextPH = getPageHeader(next); 76 | writePageHeader(next, nextPH); 77 | unpinPage(pHeader.nextPage); 78 | } 79 | if (pageNum == info_.firstEmptyPage) { 80 | info_.firstEmptyPage = pHeader.nextPage; 81 | commitInfo(); 82 | } 83 | } 84 | writePageHeader(pData, pHeader); 85 | found = true; 86 | slotNum += (int) ((bitmap - pHeader.slotBitmap) << 3); 87 | break; 88 | } 89 | bitmap += 1; 90 | } 91 | if (!found) { 92 | pageNum = pHeader.nextPage; 93 | } else { 94 | break; 95 | } 96 | } 97 | if (pageNum > 0) break; 98 | unpinPage(pageNum); 99 | } 100 | return RID(pageNum, slotNum); 101 | } 102 | 103 | void FileHandle::insertRec(Record &r) { 104 | r.rid = allocateRec(); 105 | updateRec(r); 106 | } 107 | 108 | RID FileHandle::declareRec() { 109 | RID rid = allocateRec(); 110 | int pNum = rid.pageNum; 111 | char *pData = fs_.loadPage(fid_, pNum); 112 | pData = moveToRec(pData); 113 | pData += rid.slotNum * info_.recordSize; 114 | memset(pData, 0, info_.recordSize); 115 | *(int*)pData |= REC_ALIVE; 116 | commitPage(pNum); 117 | unpinPage(pNum); 118 | return rid; 119 | } 120 | 121 | void FileHandle::deleteRec(const Record &r) { 122 | deleteRec(r.rid); 123 | } 124 | 125 | void FileHandle::deleteRec(RID rid) { 126 | char *pData = fs_.loadPage(fid_, rid.pageNum); 127 | PageHeader pHeader = getPageHeader(pData); 128 | if (pHeader.emptySlot == 0) { 129 | pHeader.nextPage = info_.firstEmptyPage; 130 | info_.firstEmptyPage = rid.pageNum; 131 | commitInfo(); 132 | } 133 | pHeader.emptySlot += 1; 134 | *(pHeader.slotBitmap + (rid.slotNum >> 3)) &= ~(1 << (rid.slotNum & 7)); 135 | writePageHeader(pData, pHeader); 136 | pData = moveToRec(pData); 137 | pData += rid.slotNum * info_.recordSize; 138 | *(unsigned int *)pData &= ~REC_ALIVE; 139 | commitPage(rid.pageNum); 140 | } 141 | 142 | int FileHandle::newPage() { 143 | char *pData = fs_.loadPage(fid_, info_.totalPageNum); 144 | 145 | PageHeader pHeader = getPageHeader(pData); 146 | pHeader.emptySlot = info_.slotPerPage; 147 | pHeader.nextPage = info_.firstEmptyPage; 148 | info_.firstEmptyPage = info_.totalPageNum; 149 | writePageHeader(pData, pHeader); 150 | 151 | char *bitmap = pHeader.slotBitmap; 152 | memset(bitmap, 0, (size_t)info_.slotBitmapSize); 153 | if (info_.slotPerPage % 8) { 154 | bitmap += info_.slotBitmapSize - 1; 155 | *bitmap = (unsigned char)(255) << (info_.slotPerPage % 8); 156 | } 157 | commitPage(info_.totalPageNum); 158 | info_.totalPageNum += 1; 159 | commitInfo(); 160 | return info_.totalPageNum - 1; 161 | } 162 | 163 | void FileHandle::forcePages() { 164 | fs_.forcePage(fid_, pagefs::ALL_PAGES); 165 | } 166 | 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /include/frontend/parser_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * parser_internal.h: internal declarations for the REDBASE parser 3 | * 4 | * Authors: Dallan Quass 5 | * Jan Jannink 6 | * 7 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 8 | */ 9 | 10 | #ifndef FRONTEND_PARSER_INTERNAL_H 11 | #define FRONTEND_PARSER_INTERNAL_H 12 | 13 | #include "sqleast.h" 14 | 15 | #define PROMPT "\n $ " 16 | #define YYINITDEPTH 100000 17 | 18 | using namespace sqleast; 19 | 20 | void yyerror(const char *err); 21 | 22 | /* 23 | * use double for real 24 | */ 25 | typedef double real; 26 | 27 | 28 | /* 29 | * REL_ATTR: describes a qualified attribute (relName.attrName) 30 | */ 31 | typedef struct{ 32 | char *relName; /* relation name */ 33 | char *attrName; /* attribute name */ 34 | } REL_ATTR; 35 | 36 | /* 37 | * ATTR_VAL: pair 38 | */ 39 | typedef struct{ 40 | char *attrName; /* attribute name */ 41 | AttrType valType; /* type of value */ 42 | int valLength; /* length if type = STRING */ 43 | void *value; /* value for attribute */ 44 | } ATTR_VAL; 45 | 46 | /* 47 | * all the available kinds of nodes 48 | */ 49 | typedef enum{ 50 | N_CREATEDATABASE, 51 | N_DROPDATABASE, 52 | N_USEDATABASE, 53 | N_SHOWTABLES, 54 | N_DESCTABLE, 55 | N_CREATETABLE, 56 | N_CREATEINDEX, 57 | N_DROPTABLE, 58 | N_DROPINDEX, 59 | N_LOAD, 60 | N_SET, 61 | N_HELP, 62 | N_PRINT, 63 | N_QUERY, 64 | N_INSERT, 65 | N_DELETE, 66 | N_UPDATE, 67 | N_RELATTR, 68 | N_CONDITION, 69 | N_RELATTR_OR_VALUE, 70 | N_ATTRTYPE, 71 | N_ATTRPROP, 72 | N_VALUE, 73 | N_RELATION, 74 | N_STATISTICS, 75 | N_LIST 76 | } NODEKIND; 77 | 78 | /* 79 | * structure of parse tree nodes 80 | */ 81 | typedef struct node{ 82 | NODEKIND kind; 83 | 84 | union{ 85 | /* SM component nodes */ 86 | /* create table node */ 87 | 88 | struct{ 89 | char *dbname; 90 | } CREATEDATABASE; 91 | 92 | struct{ 93 | char *dbname; 94 | } DROPDATABASE; 95 | 96 | struct{ 97 | char *dbname; 98 | } USEDATABASE; 99 | 100 | struct{ 101 | } SHOWTABLES; 102 | 103 | struct{ 104 | char *relname; 105 | } DESCTABLE; 106 | 107 | struct{ 108 | char *relname; 109 | struct node *attrlist; 110 | char *primary; 111 | } CREATETABLE; 112 | 113 | /* create index node */ 114 | struct{ 115 | char *relname; 116 | char *attrname; 117 | } CREATEINDEX; 118 | 119 | /* drop index node */ 120 | struct{ 121 | char *relname; 122 | char *attrname; 123 | } DROPINDEX; 124 | 125 | /* drop table node */ 126 | struct{ 127 | char *relname; 128 | } DROPTABLE; 129 | 130 | /* load node */ 131 | struct{ 132 | char *relname; 133 | char *filename; 134 | } LOAD; 135 | 136 | /* set node */ 137 | struct{ 138 | char *paramName; 139 | char *string; 140 | } SET; 141 | 142 | /* help node */ 143 | struct{ 144 | char *relname; 145 | } HELP; 146 | 147 | /* print node */ 148 | struct{ 149 | char *relname; 150 | } PRINT; 151 | 152 | /* QL component nodes */ 153 | /* query node */ 154 | struct{ 155 | struct node *relattrlist; 156 | struct node *rellist; 157 | struct node *conditionlist; 158 | } QUERY; 159 | 160 | /* insert node */ 161 | struct{ 162 | char *relname; 163 | struct node *tuplelist; 164 | } INSERT; 165 | 166 | /* delete node */ 167 | struct{ 168 | char *relname; 169 | struct node *conditionlist; 170 | } DELETE; 171 | 172 | /* update node */ 173 | struct{ 174 | char *relname; 175 | struct node *relattr; 176 | struct node *relorvalue; 177 | struct node *conditionlist; 178 | } UPDATE; 179 | 180 | /* command support nodes */ 181 | /* relation attribute node */ 182 | struct{ 183 | char *relname; 184 | char *attrname; 185 | } RELATTR; 186 | 187 | /* condition node */ 188 | struct{ 189 | struct node *lhsRelattr; 190 | CompOp op; 191 | struct node *rhsRelattr; 192 | struct node *rhsValue; 193 | } CONDITION; 194 | 195 | /* relation-attribute or value */ 196 | struct{ 197 | struct node *relattr; 198 | struct node *value; 199 | } RELATTR_OR_VALUE; 200 | 201 | /* pair */ 202 | struct{ 203 | char *attrname; 204 | char *type; 205 | int length; 206 | int nullable; 207 | } ATTRTYPE; // upgrade by zxk 208 | 209 | /* zxk: attr prop */ 210 | struct{ 211 | AttrProp attrProp; 212 | char *attrname; 213 | } ATTRPROP; 214 | 215 | /* pair */ 216 | struct{ 217 | AttrType type; 218 | int ival; 219 | real rval; 220 | char *sval; 221 | } VALUE; 222 | 223 | /* relation node */ 224 | struct{ 225 | char *relname; 226 | } RELATION; 227 | 228 | /* list node */ 229 | struct{ 230 | struct node *curr; 231 | struct node *next; 232 | } LIST; 233 | } u; 234 | } NODE; 235 | 236 | 237 | /* 238 | * function prototypes 239 | */ 240 | NODE *newnode(NODEKIND kind); 241 | NODE *create_database_node(char *dbname); 242 | NODE *drop_database_node(char *dbname); 243 | NODE *use_database_node(char *dbname); 244 | NODE *show_tables_node(); 245 | NODE *desc_table_node(char *relname); 246 | NODE *create_table_node(char *relname, NODE *attrlist); 247 | NODE *create_index_node(char *relname, char *attrname); 248 | NODE *drop_index_node(char *relname, char *attrname); 249 | NODE *drop_table_node(char *relname); 250 | NODE *load_node(char *relname, char *filename); 251 | NODE *set_node(char *paramName, char *string); 252 | NODE *help_node(char *relname); 253 | NODE *print_node(char *relname); 254 | NODE *query_node(NODE *relattrlist, NODE *rellist, NODE *conditionlist); 255 | NODE *insert_node(char *relname, NODE *valuelist); 256 | NODE *delete_node(char *relname, NODE *conditionlist); 257 | NODE *update_node(char *relname, NODE *relattr, NODE *value, 258 | NODE *conditionlist); 259 | NODE *relattr_node(char *relname, char *attrname); 260 | NODE *condition_node(NODE *lhsRelattr, CompOp op, NODE *rhsRelattrOrValue); 261 | NODE *value_node(AttrType type, void *value); 262 | NODE *relattr_or_value_node(NODE *relattr, NODE *value); 263 | NODE *attrtype_node(char *attrname, char *type, int length, int nullable); 264 | NODE *attrprop_node(AttrProp prop, char *attrname); 265 | NODE *relation_node(char *relname); 266 | NODE *list_node(NODE *n); 267 | NODE *prepend(NODE *n, NODE *list); 268 | 269 | void reset_scanner(void); 270 | void reset_charptr(void); 271 | void new_query(void); 272 | namespace sqleast { 273 | namespace ql { 274 | void interp(NODE *n); 275 | } 276 | } 277 | int yylex(void); 278 | int yyparse(void); 279 | 280 | extern "C" int yywrap(void); 281 | 282 | #endif 283 | 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /src/ix/indexscan.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ix/indexscan.h" 3 | 4 | namespace sqleast{ 5 | namespace ix{ 6 | IndexScan::IndexScan(Index& i, int v, CompOp c) :index_(i), value_(v), compOp_(c) 7 | { 8 | canDo = true; 9 | switch(compOp_){ 10 | case NO_OP: 11 | toStart1(); 12 | break; 13 | case EQ_OP: 14 | toStart2(); 15 | break; 16 | case LT_OP: 17 | toStart2(); 18 | break; 19 | case GT_OP: 20 | toStart2(); 21 | break; 22 | case LE_OP: 23 | toStart2(); 24 | break; 25 | case GE_OP: 26 | toStart2(); 27 | break; 28 | case NE_OP: 29 | toStart1(); 30 | break; 31 | default: 32 | break; 33 | } 34 | } 35 | 36 | IndexScan::~IndexScan() { 37 | } 38 | 39 | RID IndexScan::next() { 40 | RID res(-1,-1); 41 | if(!canDo) 42 | return res; 43 | switch(compOp_){ 44 | case NO_OP: 45 | res = NoSearch(); 46 | break; 47 | case EQ_OP: 48 | res = EqSearch(); 49 | break; 50 | case LT_OP: 51 | res = LtSearch(); 52 | break; 53 | case GT_OP: 54 | res = GtSearch(); 55 | break; 56 | case LE_OP: 57 | res = LeSearch(); 58 | break; 59 | case GE_OP: 60 | res = GeSearch(); 61 | break; 62 | case NE_OP: 63 | res = NeSearch(); 64 | break; 65 | default: 66 | break; 67 | } 68 | return res; 69 | } 70 | 71 | void IndexScan::toStart1() { 72 | current = index_.getRootRID(); 73 | Node root; 74 | index_.getNode(current, root); 75 | if(root.size == 0) return; 76 | while(!root.isLeaf){ 77 | current = root.n[0]; 78 | index_.getNode(current, root); 79 | position.push_back(0); 80 | } 81 | position.push_back(0); 82 | } 83 | 84 | void IndexScan::toStart2(){ 85 | current = index_.getRootRID(); 86 | Node root; 87 | index_.getNode(current, root); 88 | if(root.size == 0) return; 89 | while(!root.isLeaf){ 90 | int rank = root.getPosition(value_); 91 | current = root.n[rank+1]; 92 | index_.getNode(current, root); 93 | position.push_back(rank+1); 94 | } 95 | int r = root.getPosition(value_); 96 | position.push_back(r); 97 | } 98 | 99 | bool IndexScan::toRight(){ 100 | int num = position.back(); 101 | Node n; 102 | index_.getNode(current, n); 103 | if(num < n.size - 1){ 104 | num ++; 105 | position[position.size()-1] = num; 106 | return true; 107 | } 108 | 109 | if(current == index_.getRootRID()) return false; 110 | current = n.parent; 111 | index_.getNode(current, n); 112 | position.pop_back(); 113 | num = position.back(); 114 | while(num == n.size){ 115 | if(current == index_.getRootRID()) return false; 116 | current = n.parent; 117 | index_.getNode(current, n); 118 | position.pop_back(); 119 | num = position.back(); 120 | } 121 | if(num < n.size){ 122 | num ++; 123 | position[position.size()-1] = num; 124 | } 125 | while(!n.isLeaf){ 126 | num = position.back(); 127 | current = n.n[num]; 128 | index_.getNode(current, n); 129 | position.push_back(0); 130 | } 131 | return true; 132 | } 133 | 134 | bool IndexScan::toLeft(){ 135 | int num = position.back(); 136 | Node n; 137 | index_.getNode(current, n); 138 | if(num > 0){ 139 | num --; 140 | position[position.size()-1] = num; 141 | return true; 142 | } 143 | 144 | if(current == index_.getRootRID()) return false; 145 | current = n.parent; 146 | index_.getNode(current, n); 147 | position.pop_back(); 148 | num = position.back(); 149 | while(num == 0){ 150 | if(current == index_.getRootRID()) return false; 151 | current = n.parent; 152 | index_.getNode(current, n); 153 | position.pop_back(); 154 | num = position.back(); 155 | } 156 | if(num > 0){ 157 | num --; 158 | position[position.size()-1] = num; 159 | } 160 | while(!n.isLeaf){ 161 | num = position.back(); 162 | current = n.n[num]; 163 | index_.getNode(current, n); 164 | if(!n.isLeaf) 165 | position.push_back(n.size); 166 | else 167 | position.push_back(n.size - 1); 168 | } 169 | return true; 170 | } 171 | 172 | RID IndexScan::NoSearch(){ 173 | if(position.size() == 0) return RID(-1,-1); 174 | int num = position.back(); 175 | Node n; 176 | index_.getNode(current, n); 177 | RID res = n.n[num]; 178 | canDo = toRight(); 179 | return res; 180 | } 181 | 182 | RID IndexScan::EqSearch() { 183 | if(position.size() == 0) return RID(-1,-1); 184 | int num = position.back(); 185 | Node n; 186 | index_.getNode(current, n); 187 | RID res = n.n[num]; 188 | if(n.k[num]) 189 | return res; 190 | else 191 | return RID(-1,-1); 192 | } 193 | 194 | RID IndexScan::LtSearch(){ 195 | if(position.size() == 0) return RID(-1,-1); 196 | int num = position.back(); 197 | Node n; 198 | index_.getNode(current, n); 199 | RID res = n.n[num]; 200 | while(n.k[num] >= value_){ 201 | canDo = toLeft(); 202 | if(canDo) { 203 | num = position.back(); 204 | index_.getNode(current, n); 205 | res = n.n[num]; 206 | } 207 | else 208 | return RID(-1,-1); 209 | } 210 | canDo = toLeft(); 211 | return res; 212 | } 213 | 214 | RID IndexScan::GtSearch() { 215 | if(position.size() == 0) return RID(-1,-1); 216 | int num = position.back(); 217 | Node n; 218 | index_.getNode(current, n); 219 | RID res = n.n[num]; 220 | while(n.k[num] <= value_){ 221 | canDo = toRight(); 222 | if(canDo) { 223 | num = position.back(); 224 | index_.getNode(current, n); 225 | res = n.n[num]; 226 | } 227 | else 228 | return RID(-1,-1); 229 | } 230 | canDo = toRight(); 231 | return res; 232 | } 233 | 234 | RID IndexScan::LeSearch(){ 235 | if(position.size() == 0) return RID(-1,-1); 236 | int num = position.back(); 237 | Node n; 238 | index_.getNode(current, n); 239 | RID res = n.n[num]; 240 | while(n.k[num] > value_){ 241 | canDo = toLeft(); 242 | if(canDo) { 243 | num = position.back(); 244 | index_.getNode(current, n); 245 | res = n.n[num]; 246 | } 247 | else 248 | return RID(-1,-1); 249 | } 250 | canDo = toLeft(); 251 | return res; 252 | } 253 | 254 | RID IndexScan::GeSearch() { 255 | if(position.size() == 0) return RID(-1,-1); 256 | int num = position.back(); 257 | Node n; 258 | index_.getNode(current, n); 259 | RID res = n.n[num]; 260 | while(n.k[num] < value_){ 261 | canDo = toRight(); 262 | if(canDo) { 263 | num = position.back(); 264 | index_.getNode(current, n); 265 | res = n.n[num]; 266 | } 267 | else 268 | return RID(-1,-1); 269 | } 270 | canDo = toRight(); 271 | return res; 272 | } 273 | 274 | RID IndexScan::NeSearch(){ 275 | if(position.size() == 0) return RID(-1,-1); 276 | int num = position.back(); 277 | Node n; 278 | index_.getNode(current, n); 279 | RID res = n.n[num]; 280 | while(n.k[num] == value_){ 281 | canDo = toRight(); 282 | if(canDo) { 283 | num = position.back(); 284 | index_.getNode(current, n); 285 | res = n.n[num]; 286 | } 287 | else 288 | return RID(-1,-1); 289 | } 290 | canDo = toRight(); 291 | return res; 292 | } 293 | } 294 | } -------------------------------------------------------------------------------- /src/sm/dbhandle.cpp: -------------------------------------------------------------------------------- 1 | #include "rm/filehandle.h" 2 | #include "rm/filescan.h" 3 | #include "rm/exception.h" 4 | #include "sm/dbhandle.h" 5 | #include "sm/systemmanager.h" 6 | #include "ql/printer.h" 7 | 8 | extern Printer printer; 9 | extern Printer err; 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | namespace sqleast { 18 | namespace sm { 19 | 20 | DBHandle::DBHandle(const char *dbName): 21 | // relCatalog_(rm::RecordManager::openFile(SystemManager::appendRelCatalogExt(dbName).c_str())), 22 | // attrCatalog_(rm::RecordManager::openFile(SystemManager::appendAttrCatalogExt(dbName).c_str())) 23 | relCatalog_(rm::RecordManager::openFile(REL_CATALOG)), 24 | attrCatalog_(rm::RecordManager::openFile(ATTR_CATALOG)) 25 | { 26 | } 27 | 28 | DBHandle::~DBHandle() { 29 | } 30 | 31 | void DBHandle::createTable(const char *relName, int attrNum, const char *primary, AttrInfo *attrs) { 32 | DataAttrInfo dataAttrInfo; 33 | memset(&dataAttrInfo, 0, sizeof(dataAttrInfo)); 34 | int offset = 0; 35 | Record r2(FLAG_SIZE + sizeof(DataAttrInfo)); 36 | AttrInfo *a = attrs; 37 | int tupleLength = 0; 38 | int primaryFound = 0; 39 | for (int i = 0; i < attrNum; i++, a++) { 40 | if ((*a).attrType == STRING) 41 | tupleLength += (*a).attrLength; 42 | else if ((*a).attrType == INT) 43 | tupleLength += sizeof(int); 44 | else 45 | tupleLength += sizeof(double); 46 | if (primary != nullptr && !strcmp(primary, (*a).attrName) && (*a).attrType == INT) { 47 | primaryFound = 1; 48 | } 49 | } 50 | if (primary != nullptr && !primaryFound) { 51 | err << "[ERROR] primary key invaild" << endl; 52 | } 53 | 54 | for (int i = 0; i < attrNum; i++, attrs++) { 55 | strncpy(dataAttrInfo.relName, relName, MAX_NAME_LENGTH); 56 | strncpy(dataAttrInfo.attrName, (*attrs).attrName, MAX_NAME_LENGTH); 57 | if ((*attrs).attrType == STRING) 58 | dataAttrInfo.attrLength = (*attrs).attrLength; 59 | else if ((*attrs).attrType == INT) 60 | dataAttrInfo.attrLength = sizeof(int); 61 | else 62 | dataAttrInfo.attrLength = sizeof(double); 63 | dataAttrInfo.attrType = (*attrs).attrType; 64 | dataAttrInfo.nullable = (*attrs).nullable; 65 | dataAttrInfo.nullBitOffset = (i / 8) + tupleLength; 66 | dataAttrInfo.nullBitMask = (1 << (i & 7)); 67 | if (primary != nullptr && strcmp((*attrs).attrName, primary) == 0) { 68 | dataAttrInfo.isPrimary = 1; 69 | dataAttrInfo.indexNo = 0; 70 | ix::Index::createIndex(SystemManager::appendIndexExt(relName, 0).c_str()); 71 | } else { 72 | dataAttrInfo.isPrimary = false; 73 | dataAttrInfo.indexNo = -1; 74 | } 75 | dataAttrInfo.offset = offset; 76 | 77 | memcpy(r2.getData(), &dataAttrInfo, sizeof(DataAttrInfo)); 78 | attrCatalog_.insertRec(r2); 79 | 80 | offset += dataAttrInfo.attrLength; 81 | } 82 | RelInfo relInfo; 83 | assert(offset == tupleLength); 84 | memset(&relInfo, 0, sizeof(relInfo)); 85 | strncpy(relInfo.relName, relName, MAX_NAME_LENGTH); 86 | relInfo.tupleLength = offset; 87 | relInfo.bitmapSize = attrNum / 8 + (attrNum % 8 > 0); 88 | relInfo.attrCount = attrNum; 89 | relInfo.indexCount = primaryFound; 90 | if (primary != nullptr) 91 | strncpy(relInfo.primaryKey, primary, MAX_NAME_LENGTH); 92 | else 93 | strncpy(relInfo.primaryKey, "", MAX_NAME_LENGTH); 94 | Record r(FLAG_SIZE + sizeof(RelInfo)); 95 | memcpy(r.getData(), &relInfo, sizeof(RelInfo)); 96 | relCatalog_.insertRec(r); 97 | 98 | rm::RecordManager::createFile( 99 | SystemManager::appendDBExt(relInfo.relName).c_str(), 100 | (int)FLAG_SIZE + relInfo.tupleLength + relInfo.bitmapSize, 101 | true); 102 | } 103 | 104 | void DBHandle::dropTable(const char *relName) { 105 | /* FileHandle &handle, 106 | AttrType attrType, int attrLength, int attrOffset, 107 | int nullBitOffset, int nullBitMask, 108 | CompOp compOp, void *value) */ 109 | std::vector< RID > rids; 110 | char name[MAX_NAME_LENGTH]; 111 | strcpy(name, relName); 112 | rm::FileScan attrScan(attrCatalog_, STRING, MAX_NAME_LENGTH, 0, 0, 0, EQ_OP, name); 113 | while (true) { 114 | Record &r = attrScan.next(); 115 | if (r.rid.pageNum <= 0) 116 | break; 117 | rids.push_back(r.rid); 118 | } 119 | for (int i = 0; i < rids.size(); i++) { 120 | attrCatalog_.deleteRec(rids[i]); 121 | } 122 | rids.clear(); 123 | rm::FileScan relScan(relCatalog_, STRING, MAX_NAME_LENGTH, 0, 0, 0, EQ_OP, name); 124 | while (true) { 125 | Record &r = relScan.next(); 126 | if (r.rid.pageNum <= 0) 127 | break; 128 | rids.push_back(r.rid); 129 | } 130 | for (int i = 0; i < rids.size(); i++) { 131 | relCatalog_.deleteRec(rids[i]); 132 | } 133 | rids.clear(); 134 | } 135 | 136 | void DBHandle::showTables() { 137 | char name[MAX_NAME_LENGTH]; 138 | rm::FileScan relScan(relCatalog_, STRING, MAX_NAME_LENGTH, 0, 0, 0, NO_OP, name); 139 | while (true) { 140 | Record &r = relScan.next(); 141 | if (r.rid.pageNum <= 0) break; 142 | loadCol(r.getData(), 0, MAX_NAME_LENGTH, STRING, name); 143 | printer << name << endl; 144 | } 145 | } 146 | 147 | int DBHandle::findTable(const char *relName) { 148 | char name[MAX_NAME_LENGTH]; 149 | strcpy(name, relName); 150 | 151 | rm::FileScan relScan(relCatalog_, STRING, MAX_NAME_LENGTH, 0, 0, 0, EQ_OP, name); 152 | while (true) { 153 | Record &r = relScan.next(); 154 | if (r.rid.pageNum <= 0) break; 155 | return 1; 156 | } 157 | return 0; 158 | } 159 | 160 | void DBHandle::descTable(const char *relName) { 161 | char name[MAX_NAME_LENGTH]; 162 | strcpy(name, relName); 163 | DataAttrInfo infoArr[MAX_ATTR_NUM]; 164 | memset(infoArr, 0, sizeof(infoArr)); 165 | int n = loadRelAttrInfo(relName, infoArr); 166 | printer << "Total columns: " << n << endl; 167 | for (int i = 0; i < n; i++) { 168 | DataAttrInfo *dai = &infoArr[i]; 169 | printer << dai->attrName << " "; 170 | if (dai->attrType == INT) { 171 | printer << "int "; 172 | } else if (dai->attrType == STRING) { 173 | printer << "vchar(" << dai->attrLength - 1 << ") "; 174 | } else { 175 | printer << "float" << dai->attrLength; 176 | } 177 | if (dai->nullable == 0) { 178 | printer << "NOT NULL "; 179 | } 180 | if (dai->isPrimary) { 181 | printer << "primary key "; 182 | } 183 | printer << endl; 184 | } 185 | } 186 | 187 | void DBHandle::loadCol(char *data, int offset, int size, AttrType type, void *target) { 188 | data += offset; 189 | if (type == INT) { 190 | *(int*)target = *(int*)data; 191 | } else if (type == FLOAT) { 192 | *(double*)target = *(double*)data; 193 | } else { 194 | strncpy((char*)target, data, (size_t) size); 195 | } 196 | } 197 | 198 | int DBHandle::loadRelAttrInfo(const char *relName, DataAttrInfo *attrInfoArr) { 199 | char name[MAX_NAME_LENGTH]; 200 | strcpy(name, relName); 201 | rm::FileScan attrScan(attrCatalog_, STRING, MAX_NAME_LENGTH, 0, 0, 0, EQ_OP, name); 202 | int n = 0; 203 | while (true) { 204 | Record &r = attrScan.next(); 205 | if (r.rid.pageNum <= 0) break; 206 | attrInfoArr[n] = *(DataAttrInfo*) r.getData(); 207 | n++; 208 | } 209 | return n; 210 | } 211 | 212 | RelInfo DBHandle::getRelInfo(const char *relName) { 213 | char name[MAX_NAME_LENGTH]; 214 | strcpy(name, relName); 215 | 216 | rm::FileScan relScan(relCatalog_, STRING, MAX_NAME_LENGTH, 0, 0, 0, EQ_OP, name); 217 | Record &r = relScan.next(); 218 | return *(RelInfo*)(r.getData()); 219 | } 220 | 221 | RID DBHandle::findKey(char *relName, DataAttrInfo *dai, Value *value) { 222 | if (dai->indexNo < 0) { 223 | rm::FileHandle db = openRelFile(relName); 224 | rm::FileScan dbScan( 225 | db, 226 | value->type, 227 | dai->attrLength, 228 | dai->offset, 229 | 0, 230 | 0, 231 | EQ_OP, 232 | value->data 233 | ); 234 | Record &r = dbScan.next(); 235 | return r.rid; 236 | } else { 237 | // TODO check index 238 | } 239 | return RID(-1, -1); 240 | } 241 | } 242 | } -------------------------------------------------------------------------------- /src/frontend/nodes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * nodes.c: functions to allocate an initialize parse-tree nodes 3 | * 4 | * Authors; Dallan Quass 5 | * Jan Jannink 6 | * 7 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "parser_internal.h" 17 | #include "parser.hpp" 18 | 19 | /* 20 | * total number of nodes available for a given parse-tree 21 | */ 22 | // #define MAXNODE 100 // ZXK: this is too small!!! 23 | #define MAXNODE 32767 // ZXK: this is too small!!! 24 | 25 | static NODE nodepool[MAXNODE]; 26 | static int nodeptr = 0; 27 | 28 | /* 29 | * reset_parser: resets the scanner and parser when a syntax error occurs 30 | * 31 | * No return value 32 | */ 33 | void reset_parser(void) 34 | { 35 | reset_scanner(); 36 | nodeptr = 0; 37 | } 38 | 39 | static void (*cleanup_func)() = NULL; 40 | 41 | /* 42 | * new_query: prepares for a new query by releasing all resources used 43 | * by previous queries. 44 | * 45 | * No return value. 46 | */ 47 | void new_query(void) 48 | { 49 | nodeptr = 0; 50 | reset_charptr(); 51 | if(cleanup_func != NULL) 52 | (*cleanup_func)(); 53 | } 54 | 55 | void register_cleanup_function(void (*func)()) 56 | { 57 | cleanup_func = func; 58 | } 59 | 60 | /* 61 | * newnode: allocates a new node of the specified kind and returns a pointer 62 | * to it on success. Returns NULL on error. 63 | */ 64 | NODE *newnode(NODEKIND kind) 65 | { 66 | NODE *n; 67 | 68 | /* if we've used up all of the nodes then error */ 69 | if(nodeptr == MAXNODE){ 70 | fprintf(stderr, "out of memory\n"); 71 | exit(1); 72 | } 73 | 74 | /* get the next node */ 75 | n = nodepool + nodeptr; 76 | ++nodeptr; 77 | 78 | /* initialize the `kind' field */ 79 | n -> kind = kind; 80 | return n; 81 | } 82 | 83 | /* zxk add 5 ddl commands here */ 84 | 85 | NODE *create_database_node(char *dbname) { 86 | NODE *n = newnode(N_CREATEDATABASE); 87 | n->u.CREATEDATABASE.dbname = dbname; 88 | return n; 89 | } 90 | 91 | NODE *drop_database_node(char *dbname) { 92 | NODE *n = newnode(N_DROPDATABASE); 93 | n->u.DROPDATABASE.dbname = dbname; 94 | return n; 95 | } 96 | 97 | NODE *use_database_node(char *dbname) { 98 | NODE *n = newnode(N_USEDATABASE); 99 | n->u.USEDATABASE.dbname = dbname; 100 | return n; 101 | } 102 | 103 | NODE *show_tables_node() { 104 | NODE *n = newnode(N_SHOWTABLES); 105 | return n; 106 | } 107 | 108 | NODE *desc_table_node(char *relname) { 109 | NODE *n = newnode(N_DESCTABLE); 110 | n->u.DESCTABLE.relname = relname; 111 | return n; 112 | } 113 | 114 | 115 | /* 116 | * create_table_node: allocates, initializes, and returns a pointer to a new 117 | * create table node having the indicated values. 118 | */ 119 | NODE *create_table_node(char *relname, NODE *attrlist) 120 | { 121 | NODE *n = newnode(N_CREATETABLE); 122 | 123 | n -> u.CREATETABLE.relname = relname; 124 | n -> u.CREATETABLE.attrlist = attrlist; 125 | 126 | char *primaryKeyAttr = nullptr; 127 | NODE *curr, *iter; 128 | for (iter = attrlist; iter != nullptr; iter = iter->u.LIST.next) { 129 | curr = iter->u.LIST.curr; 130 | if (curr->kind == N_ATTRPROP) { 131 | if (curr->u.ATTRPROP.attrProp == PRIMARY_KEY) { 132 | primaryKeyAttr = curr->u.ATTRPROP.attrname; 133 | } 134 | } 135 | } 136 | 137 | n -> u.CREATETABLE.primary = primaryKeyAttr; 138 | return n; 139 | } 140 | 141 | /* 142 | * create_index_node: allocates, initializes, and returns a pointer to a new 143 | * create index node having the indicated values. 144 | */ 145 | NODE *create_index_node(char *relname, char *attrname) 146 | { 147 | NODE *n = newnode(N_CREATEINDEX); 148 | 149 | n -> u.CREATEINDEX.relname = relname; 150 | n -> u.CREATEINDEX.attrname = attrname; 151 | return n; 152 | } 153 | 154 | /* 155 | * drop_index_node: allocates, initializes, and returns a pointer to a new 156 | * drop index node having the indicated values. 157 | */ 158 | NODE *drop_index_node(char *relname, char *attrname) 159 | { 160 | NODE *n = newnode(N_DROPINDEX); 161 | 162 | n -> u.DROPINDEX.relname = relname; 163 | n -> u.DROPINDEX.attrname = attrname; 164 | return n; 165 | } 166 | 167 | /* 168 | * drop_table_node: allocates, initializes, and returns a pointer to a new 169 | * drop table node having the indicated values. 170 | */ 171 | NODE *drop_table_node(char *relname) 172 | { 173 | NODE *n = newnode(N_DROPTABLE); 174 | 175 | n -> u.DROPTABLE.relname = relname; 176 | return n; 177 | } 178 | 179 | /* 180 | * load_node: allocates, initializes, and returns a pointer to a new 181 | * load node having the indicated values. 182 | */ 183 | NODE *load_node(char *relname, char *filename) 184 | { 185 | NODE *n = newnode(N_LOAD); 186 | 187 | n -> u.LOAD.relname = relname; 188 | n -> u.LOAD.filename = filename; 189 | return n; 190 | } 191 | 192 | /* 193 | * set_node: allocates, initializes, and returns a pointer to a new 194 | * set node having the indicated values. 195 | */ 196 | NODE *set_node(char *paramName, char *string) 197 | { 198 | NODE *n = newnode(N_SET); 199 | 200 | n -> u.SET.paramName = paramName; 201 | n -> u.SET.string = string; 202 | return n; 203 | } 204 | 205 | /* 206 | * help_node: allocates, initializes, and returns a pointer to a new 207 | * help node having the indicated values. 208 | */ 209 | NODE *help_node(char *relname) 210 | { 211 | NODE *n = newnode(N_HELP); 212 | 213 | n -> u.HELP.relname = relname; 214 | return n; 215 | } 216 | 217 | /* 218 | * print_node: allocates, initializes, and returns a pointer to a new 219 | * print node having the indicated values. 220 | */ 221 | NODE *print_node(char *relname) 222 | { 223 | NODE *n = newnode(N_PRINT); 224 | 225 | n -> u.PRINT.relname = relname; 226 | return n; 227 | } 228 | 229 | /* 230 | * query_node: allocates, initializes, and returns a pointer to a new 231 | * query node having the indicated values. 232 | */ 233 | NODE *query_node(NODE *relattrlist, NODE *rellist, NODE *conditionlist) 234 | { 235 | NODE *n = newnode(N_QUERY); 236 | 237 | n->u.QUERY.relattrlist = relattrlist; 238 | n->u.QUERY.rellist = rellist; 239 | n->u.QUERY.conditionlist = conditionlist; 240 | return n; 241 | } 242 | 243 | /* 244 | * insert_node: allocates, initializes, and returns a pointer to a new 245 | * insert node having the indicated values. 246 | */ 247 | NODE *insert_node(char *relname, NODE *tuplelist) 248 | { 249 | NODE *n = newnode(N_INSERT); 250 | 251 | n->u.INSERT.relname = relname; 252 | n->u.INSERT.tuplelist = tuplelist; 253 | return n; 254 | } 255 | 256 | /* 257 | * delete_node: allocates, initializes, and returns a pointer to a new 258 | * delete node having the indicated values. 259 | */ 260 | NODE *delete_node(char *relname, NODE *conditionlist) 261 | { 262 | NODE *n = newnode(N_DELETE); 263 | 264 | n->u.DELETE.relname = relname; 265 | n->u.DELETE.conditionlist = conditionlist; 266 | return n; 267 | } 268 | 269 | /* 270 | * update_node: allocates, initializes, and returns a pointer to a new 271 | * update node having the indicated values. 272 | */ 273 | NODE *update_node(char *relname, NODE *relattr, NODE *relorvalue, 274 | NODE *conditionlist) 275 | { 276 | NODE *n = newnode(N_UPDATE); 277 | 278 | n->u.UPDATE.relname = relname; 279 | n->u.UPDATE.relattr = relattr; 280 | n->u.UPDATE.relorvalue = relorvalue; 281 | n->u.UPDATE.conditionlist = conditionlist; 282 | return n; 283 | } 284 | 285 | 286 | /* 287 | * relattr_node: allocates, initializes, and returns a pointer to a new 288 | * relattr node having the indicated values. 289 | */ 290 | NODE *relattr_node(char *relname, char *attrname) 291 | { 292 | NODE *n = newnode(N_RELATTR); 293 | 294 | n -> u.RELATTR.relname = relname; 295 | n -> u.RELATTR.attrname = attrname; 296 | return n; 297 | } 298 | 299 | /* 300 | * condition_node: allocates, initializes, and returns a pointer to a new 301 | * condition node having the indicated values. 302 | */ 303 | 304 | /* zxk: add null / not null ops */ 305 | 306 | NODE *condition_node(NODE *lhsRelattr, CompOp op, NODE *rhsRelattrOrValue) 307 | { 308 | NODE *n = newnode(N_CONDITION); 309 | 310 | n->u.CONDITION.lhsRelattr = lhsRelattr; 311 | n->u.CONDITION.op = op; 312 | if (rhsRelattrOrValue != nullptr) { 313 | n->u.CONDITION.rhsRelattr = 314 | rhsRelattrOrValue->u.RELATTR_OR_VALUE.relattr; 315 | n->u.CONDITION.rhsValue = 316 | rhsRelattrOrValue->u.RELATTR_OR_VALUE.value; 317 | } else { 318 | n->u.CONDITION.rhsRelattr = nullptr; 319 | n->u.CONDITION.rhsValue = nullptr; 320 | } 321 | return n; 322 | } 323 | 324 | /* 325 | * value_node: allocates, initializes, and returns a pointer to a new 326 | * value node having the indicated values. 327 | */ 328 | NODE *value_node(AttrType type, void *value) 329 | { 330 | NODE *n = newnode(N_VALUE); 331 | 332 | n->u.VALUE.type = type; 333 | switch (type) { 334 | case INT: 335 | n->u.VALUE.ival = *(int *)value; 336 | break; 337 | case FLOAT: 338 | n->u.VALUE.rval = *(float *)value; 339 | break; 340 | case STRING: 341 | n->u.VALUE.sval = (char *)value; 342 | break; 343 | case NULLV: 344 | break; 345 | } 346 | return n; 347 | } 348 | 349 | /* 350 | * relattr_or_valuenode: allocates, initializes, and returns a pointer to 351 | * a new relattr_or_value node having the indicated values. 352 | */ 353 | NODE *relattr_or_value_node(NODE *relattr, NODE *value) 354 | { 355 | NODE *n = newnode(N_RELATTR_OR_VALUE); 356 | 357 | n->u.RELATTR_OR_VALUE.relattr = relattr; 358 | n->u.RELATTR_OR_VALUE.value = value; 359 | return n; 360 | } 361 | 362 | /* 363 | * attrtype_node: allocates, initializes, and returns a pointer to a new 364 | * attrtype node having the indicated values. 365 | */ 366 | NODE *attrtype_node(char *attrname, char *type, int length, int nullable) 367 | { 368 | NODE *n = newnode(N_ATTRTYPE); 369 | 370 | n -> u.ATTRTYPE.attrname = attrname; 371 | n -> u.ATTRTYPE.type = type; 372 | n -> u.ATTRTYPE.length = length + 1; 373 | n -> u.ATTRTYPE.nullable = nullable; 374 | return n; 375 | } 376 | 377 | /* zxk: add support of rel propertise */ 378 | NODE *attrprop_node(AttrProp prop, char *attrname) 379 | { 380 | NODE *n = newnode(N_ATTRPROP); 381 | 382 | n->u.ATTRPROP.attrProp = prop; 383 | n->u.ATTRPROP.attrname = attrname; 384 | return n; 385 | } 386 | 387 | /* 388 | * relation_node: allocates, initializes, and returns a pointer to a new 389 | * relation node having the indicated values. 390 | */ 391 | NODE *relation_node(char *relname) 392 | { 393 | NODE *n = newnode(N_RELATION); 394 | 395 | n->u.RELATION.relname = relname; 396 | return n; 397 | } 398 | 399 | /* 400 | * list_node: allocates, initializes, and returns a pointer to a new 401 | * list node having the indicated values. 402 | */ 403 | NODE *list_node(NODE *n) 404 | { 405 | NODE *list = newnode(N_LIST); 406 | 407 | list -> u.LIST.curr = n; 408 | list -> u.LIST.next = NULL; 409 | return list; 410 | } 411 | 412 | /* 413 | * prepends node n onto the front of list. 414 | * 415 | * Returns the resulting list. 416 | */ 417 | NODE *prepend(NODE *n, NODE *list) 418 | { 419 | NODE *newlist = newnode(N_LIST); 420 | 421 | newlist -> u.LIST.curr = n; 422 | newlist -> u.LIST.next = list; 423 | return newlist; 424 | } 425 | -------------------------------------------------------------------------------- /src/frontend/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | /* 3 | * parser.y: yacc specification for RQL 4 | * 5 | * Authors: Dallan Quass 6 | * Jan Jannink 7 | * Jason McHugh 8 | * 9 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 10 | * 11 | * 1997: Added "print buffer", "print io", "reset io" and the "*" in 12 | * SFW Query. 13 | * 1998: Added "reset buffer", "resize buffer [int]", "queryplans on", 14 | * and "queryplans off". 15 | * 2000: Added "const" to yyerror-header 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "sqleast.h" 25 | #include "parser_internal.h" 26 | 27 | using namespace std; 28 | using namespace sqleast; 29 | 30 | // Added by Wendy Tobagus 31 | void yyrestart(FILE*); 32 | 33 | /* 34 | * string representation of tokens; provided by scanner 35 | */ 36 | extern char *yytext; 37 | 38 | /* 39 | * points to root of parse tree 40 | */ 41 | static NODE *parse_tree; 42 | 43 | int bExit; // when to return from RBparse 44 | 45 | int bQueryPlans; // When to print the query plans 46 | 47 | %} 48 | 49 | %union{ 50 | int ival; 51 | CompOp cval; 52 | float rval; 53 | char *sval; 54 | NODE *n; 55 | } 56 | 57 | %token 58 | RW_CREATE 59 | RW_DROP 60 | RW_SHOW 61 | RW_DESC 62 | RW_USE 63 | RW_TABLES 64 | RW_TABLE 65 | RW_INDEX 66 | RW_DATABASE 67 | RW_LOAD 68 | RW_SET 69 | RW_HELP 70 | RW_PRINT 71 | RW_EXIT 72 | RW_SELECT 73 | RW_FROM 74 | RW_WHERE 75 | RW_INSERT 76 | RW_DELETE 77 | RW_UPDATE 78 | RW_AND 79 | RW_IS 80 | RW_NOT 81 | RW_NULL 82 | RW_INTO 83 | RW_VALUES 84 | RW_PRIMARY 85 | RW_KEY 86 | RW_INT 87 | RW_VCHAR 88 | T_EQ 89 | T_LT 90 | T_LE 91 | T_GT 92 | T_GE 93 | T_NE 94 | T_EOF 95 | NOTOKEN 96 | RW_RESET 97 | RW_IO 98 | RW_BUFFER 99 | RW_RESIZE 100 | RW_QUERY_PLAN 101 | RW_ON 102 | RW_OFF 103 | 104 | %token T_INT 105 | 106 | %token T_REAL 107 | 108 | %token T_STRING 109 | T_QSTRING 110 | T_SHELL_CMD 111 | 112 | %type op 113 | 114 | %type opt_relname 115 | 116 | %type command 117 | ddl 118 | dml 119 | utility 120 | createdatabase 121 | dropdatabase 122 | usedatabase 123 | showtables 124 | desctable 125 | createtable 126 | createindex 127 | droptable 128 | dropindex 129 | load 130 | set 131 | help 132 | print 133 | exit 134 | query 135 | insert 136 | delete 137 | update 138 | insert_value_list 139 | non_mt_attrtype_list 140 | attrtype 141 | non_mt_relattr_list 142 | non_mt_select_clause 143 | relattr 144 | non_mt_relation_list 145 | relation 146 | opt_where_clause 147 | non_mt_cond_list 148 | condition 149 | relattr_or_value 150 | non_mt_value_list 151 | value 152 | null_value 153 | %% 154 | 155 | start 156 | : command ';' 157 | { 158 | parse_tree = $1; 159 | YYACCEPT; 160 | } 161 | | T_SHELL_CMD 162 | { 163 | if (!isatty(0)) { 164 | cout << ($1) << "\n"; 165 | cout.flush(); 166 | } 167 | system($1); 168 | parse_tree = nullptr; 169 | YYACCEPT; 170 | } 171 | | error 172 | { 173 | reset_scanner(); 174 | parse_tree = nullptr; 175 | YYACCEPT; 176 | } 177 | | T_EOF 178 | { 179 | parse_tree = nullptr; 180 | bExit = 1; 181 | YYACCEPT; 182 | } 183 | ; 184 | 185 | command 186 | : ddl 187 | | dml 188 | | utility 189 | | nothing 190 | { 191 | $$ = nullptr; 192 | } 193 | ; 194 | 195 | ddl 196 | : createtable 197 | | createindex 198 | | droptable 199 | | dropindex 200 | | createdatabase 201 | | dropdatabase 202 | | usedatabase 203 | | showtables 204 | | desctable 205 | ; 206 | 207 | dml 208 | : query 209 | | insert 210 | | delete 211 | | update 212 | ; 213 | 214 | utility 215 | : load 216 | | exit 217 | | set 218 | | help 219 | | print 220 | ; 221 | 222 | createdatabase 223 | : RW_CREATE RW_DATABASE T_STRING 224 | { 225 | $$ = create_database_node($3); 226 | } 227 | ; 228 | 229 | dropdatabase 230 | : RW_DROP RW_DATABASE T_STRING 231 | { 232 | $$ = drop_database_node($3); 233 | } 234 | ; 235 | 236 | usedatabase 237 | : RW_USE T_STRING 238 | { 239 | $$ = use_database_node($2); 240 | } 241 | ; 242 | 243 | showtables 244 | : RW_SHOW RW_TABLES 245 | { 246 | $$ = show_tables_node(); 247 | } 248 | ; 249 | 250 | desctable 251 | : RW_DESC T_STRING 252 | { 253 | $$ = desc_table_node($2); 254 | } 255 | ; 256 | 257 | createtable 258 | : RW_CREATE RW_TABLE T_STRING '(' non_mt_attrtype_list ')' 259 | { 260 | $$ = create_table_node($3, $5); 261 | } 262 | ; 263 | 264 | createindex 265 | : RW_CREATE RW_INDEX T_STRING '(' T_STRING ')' 266 | { 267 | $$ = create_index_node($3, $5); 268 | } 269 | ; 270 | 271 | droptable 272 | : RW_DROP RW_TABLE T_STRING 273 | { 274 | $$ = drop_table_node($3); 275 | } 276 | ; 277 | 278 | dropindex 279 | : RW_DROP RW_INDEX T_STRING '(' T_STRING ')' 280 | { 281 | $$ = drop_index_node($3, $5); 282 | } 283 | ; 284 | 285 | load 286 | : RW_LOAD T_STRING '(' T_QSTRING ')' 287 | { 288 | $$ = load_node($2, $4); 289 | } 290 | ; 291 | 292 | 293 | set 294 | : RW_SET T_STRING T_EQ T_QSTRING 295 | { 296 | $$ = set_node($2, $4); 297 | } 298 | ; 299 | 300 | help 301 | : RW_HELP opt_relname 302 | { 303 | $$ = help_node($2); 304 | } 305 | ; 306 | 307 | print 308 | : RW_PRINT T_STRING 309 | { 310 | $$ = print_node($2); 311 | } 312 | ; 313 | 314 | exit 315 | : RW_EXIT 316 | { 317 | $$ = nullptr; 318 | bExit = 1; 319 | } 320 | ; 321 | 322 | query 323 | : RW_SELECT non_mt_select_clause RW_FROM non_mt_relation_list opt_where_clause 324 | { 325 | $$ = query_node($2, $4, $5); 326 | } 327 | ; 328 | 329 | insert 330 | : RW_INSERT RW_INTO T_STRING RW_VALUES insert_value_list 331 | { 332 | $$ = insert_node($3, $5); 333 | } 334 | ; 335 | 336 | insert_value_list 337 | : '(' non_mt_value_list ')' ',' insert_value_list 338 | { 339 | $$ = prepend($2, $5); 340 | } 341 | | '(' non_mt_value_list ')' 342 | { 343 | $$ = list_node($2); 344 | } 345 | ; 346 | 347 | delete 348 | : RW_DELETE RW_FROM T_STRING opt_where_clause 349 | { 350 | $$ = delete_node($3, $4); 351 | } 352 | ; 353 | 354 | update 355 | : RW_UPDATE T_STRING RW_SET relattr T_EQ relattr_or_value opt_where_clause 356 | { 357 | $$ = update_node($2, $4, $6, $7); 358 | } 359 | ; 360 | 361 | non_mt_attrtype_list 362 | : attrtype ',' non_mt_attrtype_list 363 | { 364 | $$ = prepend($1, $3); 365 | } 366 | | attrtype 367 | { 368 | $$ = list_node($1); 369 | } 370 | ; 371 | 372 | attrtype 373 | : T_STRING T_STRING '(' T_INT ')' 374 | { 375 | $$ = attrtype_node($1, $2, $4, 1); 376 | } 377 | | T_STRING T_STRING '(' T_INT ')' RW_NOT RW_NULL 378 | { 379 | $$ = attrtype_node($1, $2, $4, 0); 380 | } 381 | | RW_PRIMARY RW_KEY '(' T_STRING ')' 382 | { 383 | $$ = attrprop_node(PRIMARY_KEY, $4); 384 | } 385 | ; 386 | 387 | non_mt_select_clause 388 | : non_mt_relattr_list 389 | | '*' 390 | { 391 | $$ = list_node(relattr_node(nullptr, (char*)"*")); 392 | } 393 | 394 | 395 | non_mt_relattr_list 396 | : relattr ',' non_mt_relattr_list 397 | { 398 | $$ = prepend($1, $3); 399 | } 400 | | relattr 401 | { 402 | $$ = list_node($1); 403 | } 404 | ; 405 | 406 | relattr 407 | : T_STRING '.' T_STRING 408 | { 409 | $$ = relattr_node($1, $3); 410 | } 411 | | T_STRING 412 | { 413 | $$ = relattr_node(nullptr, $1); 414 | } 415 | ; 416 | 417 | non_mt_relation_list 418 | : relation ',' non_mt_relation_list 419 | { 420 | $$ = prepend($1, $3); 421 | } 422 | | relation 423 | { 424 | $$ = list_node($1); 425 | } 426 | ; 427 | 428 | relation 429 | : T_STRING 430 | { 431 | $$ = relation_node($1); 432 | } 433 | ; 434 | 435 | opt_where_clause 436 | : RW_WHERE non_mt_cond_list 437 | { 438 | $$ = $2; 439 | } 440 | | nothing 441 | { 442 | $$ = nullptr; 443 | } 444 | ; 445 | 446 | non_mt_cond_list 447 | : condition RW_AND non_mt_cond_list 448 | { 449 | $$ = prepend($1, $3); 450 | } 451 | | condition 452 | { 453 | $$ = list_node($1); 454 | } 455 | ; 456 | 457 | condition 458 | : relattr op relattr_or_value 459 | { 460 | $$ = condition_node($1, $2, $3); 461 | } 462 | | relattr RW_IS RW_NULL 463 | { 464 | $$ = condition_node($1, IS_NULL_OP, nullptr); 465 | } 466 | | relattr RW_IS RW_NOT RW_NULL 467 | { 468 | $$ = condition_node($1, NOT_NULL_OP, nullptr); 469 | } 470 | ; 471 | 472 | relattr_or_value 473 | : relattr 474 | { 475 | $$ = relattr_or_value_node($1, nullptr); 476 | } 477 | | value 478 | { 479 | $$ = relattr_or_value_node(nullptr, $1); 480 | } 481 | ; 482 | 483 | non_mt_value_list 484 | : value ',' non_mt_value_list 485 | { 486 | $$ = prepend($1, $3); 487 | } 488 | | value 489 | { 490 | $$ = list_node($1); 491 | } 492 | | null_value ',' non_mt_value_list 493 | { 494 | $$ = prepend($1, $3); 495 | } 496 | | null_value 497 | { 498 | $$ = list_node($1); 499 | } 500 | ; 501 | 502 | value 503 | : T_QSTRING 504 | { 505 | $$ = value_node(STRING, (void *) $1); 506 | } 507 | | T_INT 508 | { 509 | $$ = value_node(INT, (void *)& $1); 510 | } 511 | | T_REAL 512 | { 513 | $$ = value_node(FLOAT, (void *)& $1); 514 | } 515 | ; 516 | 517 | null_value 518 | : RW_NULL 519 | { 520 | $$ = value_node(NULLV, nullptr); 521 | } 522 | ; 523 | 524 | opt_relname 525 | : T_STRING 526 | { 527 | $$ = $1; 528 | } 529 | | nothing 530 | { 531 | $$ = nullptr; 532 | } 533 | ; 534 | 535 | op 536 | : T_LT 537 | { 538 | $$ = LT_OP; 539 | } 540 | | T_LE 541 | { 542 | $$ = LE_OP; 543 | } 544 | | T_GT 545 | { 546 | $$ = GT_OP; 547 | } 548 | | T_GE 549 | { 550 | $$ = GE_OP; 551 | } 552 | | T_EQ 553 | { 554 | $$ = EQ_OP; 555 | } 556 | | T_NE 557 | { 558 | $$ = NE_OP; 559 | } 560 | ; 561 | 562 | nothing 563 | : /* epsilon */ 564 | ; 565 | 566 | %% 567 | 568 | void sqleast::sqleast_parse() 569 | { 570 | 571 | // Set up global variables to their defaults 572 | bExit = 0; 573 | 574 | /* Do forever */ 575 | while (!bExit) { 576 | 577 | /* Reset parser and scanner for a new query */ 578 | new_query(); 579 | 580 | /* Print a prompt */ 581 | cout << PROMPT; 582 | 583 | /* Get the prompt to actually show up on the screen */ 584 | cout.flush(); 585 | 586 | /* If a query was successfully read, interpret it */ 587 | if(yyparse() == 0 && parse_tree != nullptr) 588 | sqleast::ql::interp(parse_tree); 589 | } 590 | } 591 | 592 | /* 593 | * Required by yacc 594 | */ 595 | void yyerror(char const *s) // New in 2000 596 | { 597 | puts(s); 598 | } 599 | 600 | /* 601 | * Sometimes required 602 | */ 603 | int yywrap(void) 604 | { 605 | return 1; 606 | } 607 | -------------------------------------------------------------------------------- /src/pagefs/pagefs.cpp: -------------------------------------------------------------------------------- 1 | #include "pagefs/pagefs.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | //#define PAGEFS_VERBOSE 8 | 9 | namespace { 10 | #ifdef PAGEFS_VERBOSE 11 | std::ostream &debug_ = std::cout; 12 | #else 13 | std::ostream debug_(0); 14 | #endif 15 | } 16 | 17 | namespace pagefs { 18 | 19 | /* pagefs */ 20 | 21 | PageFS::PageFS(): entryCnt_(0) { 22 | lruList_ = LRUList(); 23 | lruTable_ = LRUHash(); 24 | 25 | for (int i = 0; i < MAX_BUFFER_SIZE; i++) { 26 | lruTable_.table[i].node = nullptr; 27 | } 28 | } 29 | 30 | PageFS::~PageFS() { 31 | debug_ << "destructing pagefs" << std::endl; 32 | commitAll(-1); 33 | } 34 | 35 | void PageFS::printState(std::ostream &os) { 36 | os << "[PageFS state]" << std::endl; 37 | os << "[buffer] " << lruTable_.total << " / " << MAX_BUFFER_SIZE << std::endl; 38 | } 39 | // TODO check file names 40 | 41 | void PageFS::createFile(const char *fileName, bool override) { 42 | FILE *f = fopen(fileName, "r"); 43 | if (f != nullptr) { 44 | fclose(f); 45 | if (!override) { 46 | throw FileExistsException(); 47 | } 48 | } 49 | f = fopen(fileName, "w"); 50 | if (f == nullptr) { 51 | throw FileNotFoundException(); 52 | } 53 | fclose(f); 54 | } 55 | 56 | void PageFS::destroyFile(const char *fileName) { 57 | if (remove(fileName) != 0) { 58 | throw FileRemoveException(); 59 | } 60 | } 61 | 62 | FileId PageFS::openFile(const char *fileName) { 63 | // TODO read-only file handles 64 | // TODO use TRIE, hash or any other high level tech to control the files. 65 | 66 | for (int i = 0; i < entryCnt_; i++) 67 | if (entries_[i].fileId >= 0 && strcmp(fileName, entries_[i].fileName) == 0) { 68 | // Hint: a file can only has a file handle alive 69 | // throw FileOpenedException(); 70 | entries_[i].counter += 1; 71 | return i; 72 | } 73 | 74 | int k; 75 | for (k = 0; k < entryCnt_ && entries_[k].fileId >= 0; k++); // find empty position 76 | if (k == entryCnt_) { 77 | if (entryCnt_ == MAX_FILE_NUM) { 78 | throw TooManyFilesError(); 79 | } 80 | entryCnt_++; 81 | } 82 | 83 | FILE *f = fopen(fileName, "rb+"); 84 | strcpy(entries_[k].fileName, fileName); 85 | entries_[k].fileId = entryCnt_; 86 | entries_[k].counter = 1; 87 | filePtr_[k] = f; 88 | 89 | return k; 90 | } 91 | 92 | void PageFS::closeFile(FileId f) { 93 | // TODO use TRIE, hash or any other high level tech to control the files. 94 | // if (entries_[f].counter == 0) { 95 | // throw FileClosedException(); 96 | // } 97 | commitAll(f); 98 | // entries_[f].counter -= 1; 99 | // if (entries_[f].counter == 0) { 100 | // entries_[f].fileId = -1; // release 101 | // fclose(filePtr_[f]); 102 | // } 103 | } 104 | 105 | char *PageFS::loadPage(int fileId, int pageNum) { 106 | LRUHashItem *t; 107 | try { 108 | t = lruTable_.get(fileId, pageNum); 109 | } catch (const ItemNotFound &e) { 110 | t = nullptr; 111 | } 112 | if (t == nullptr) { 113 | if (lruTable_.total == MAX_BUFFER_SIZE) { 114 | if (!commitOnePage()) 115 | throw NoBufError(); 116 | } 117 | debug_ << "[NOTINBUF]" << fileId << " " << pageNum << std::endl; 118 | BufferPage p; 119 | p.data = new char[PAGE_SIZE]; 120 | memset(p.data, 0, PAGE_SIZE); 121 | p.fileId = fileId; 122 | p.pageNum = pageNum; 123 | p.dirty = 0; 124 | p.pinned = 1; // automatically pinned 125 | 126 | FILE *f = filePtr_[fileId]; 127 | fseek(f, 0, SEEK_END); 128 | off_t offset = PAGE_SIZE * pageNum; 129 | if (offset <= ftell(f)) { 130 | fseek(f, pageNum * PAGE_SIZE, 0); 131 | fread(p.data, 1, PAGE_SIZE, f); 132 | } 133 | 134 | t = lruTable_.add(p, nullptr); 135 | t->node = lruList_.push_back(t); 136 | 137 | return p.data; 138 | } else { 139 | debug_ << "[INBUF]" << fileId << " " << pageNum << std::endl; 140 | lruList_.move_back(t->node); 141 | return t->data.data; 142 | } 143 | } 144 | 145 | void PageFS::forcePage(int fileId, int pageNum) { 146 | if (pageNum != ALL_PAGES) { 147 | LRUHashItem *t = lruTable_.get(fileId, pageNum); 148 | writeBack(t->data); 149 | t->data.dirty = 0; 150 | lruList_.move_back(t->node); 151 | } else { 152 | for (int i = 0; i < MAX_BUFFER_SIZE; i++) { 153 | LRUHashItem &t = lruTable_.table[i]; 154 | if (t.node != nullptr && t.data.fileId == fileId) { 155 | writeBack(t.data); 156 | t.data.dirty = 0; 157 | lruList_.move_back(t.node); 158 | } 159 | } 160 | } 161 | } 162 | 163 | void PageFS::pinPage(int fileId, int pageNum) { 164 | LRUHashItem *p = lruTable_.get(fileId, pageNum); 165 | p->data.pinned = 1; 166 | } 167 | 168 | void PageFS::unpinPage(int fileId, int pageNum) { 169 | LRUHashItem *p = lruTable_.get(fileId, pageNum); 170 | p->data.pinned = 0; 171 | } 172 | 173 | void PageFS::markDirty(int fileId, int pageNum) { 174 | LRUHashItem *p = lruTable_.get(fileId, pageNum); 175 | p->data.dirty = 1; 176 | } 177 | 178 | bool PageFS::commitOnePage() { 179 | LRUListNode *p = lruList_.head; 180 | while (p != nullptr && p->item->data.pinned) 181 | p = p->next; 182 | // Debug::info("found unpined"); 183 | if (p == nullptr) 184 | return false; 185 | char *data = p->item->data.data; 186 | LRUHashItem *t = lruList_.remove(p); 187 | int key = (int)(t - lruTable_.table); 188 | debug_ << "[RELEASE]" << t->data.fileId << " " << t->data.pageNum << std::endl; 189 | bool res = writeBack(lruTable_.popByKey(key).data); 190 | delete[] data; 191 | return res; 192 | } 193 | 194 | void PageFS::commitAll(int fid) { 195 | LRUListNode *p = lruList_.head, *q; 196 | while (p != nullptr) { 197 | q = p->next; 198 | if (fid < 0 || p->item->data.fileId == fid) { 199 | char *data = p->item->data.data; 200 | LRUHashItem *t = lruList_.remove(p); 201 | int key = (int)(t - lruTable_.table); 202 | debug_ << "[RELEASE(ALL)]" << t->data.fileId << " " << t->data.pageNum << std::endl; 203 | writeBack(lruTable_.popByKey(key).data); 204 | delete[] data; 205 | } 206 | p = q; 207 | } 208 | } 209 | 210 | bool PageFS::writeBack(BufferPage p) { 211 | if (p.dirty) { 212 | FILE *f = filePtr_[p.fileId]; 213 | fseek(f, p.pageNum * PAGE_SIZE, 0); 214 | fwrite(p.data, 1, PAGE_SIZE, f); 215 | } 216 | return true; 217 | } 218 | 219 | 220 | /* LRU hash */ 221 | 222 | LRUHash::LRUHash() : total(0) { 223 | memset(table, 0, sizeof(table)); 224 | for (int i = 0; i < MAX_BUFFER_SIZE; i++) { 225 | table[i].node = nullptr; 226 | } 227 | } 228 | 229 | LRUHash::~LRUHash() { 230 | 231 | } 232 | 233 | LRUHashItem *LRUHash::add(BufferPage &p, LRUListNode *node) { 234 | if (total == MAX_BUFFER_SIZE) { 235 | throw HashTableError(); // PageFS should be responsible for clearing data 236 | } 237 | total += 1; 238 | int key = hash(p.fileId, p.pageNum); 239 | int hashValue = key; 240 | while (table[key].node != nullptr) { 241 | key = (key + 1) & MAX_BUFFER_SIZE_M1; 242 | } 243 | table[key].data = p; 244 | table[key].hashValue = hashValue; 245 | table[key].node = node; 246 | return &(table[key]); 247 | } 248 | 249 | LRUHashItem *LRUHash::get(int fileId, int pageNum) { 250 | int key = hash(fileId, pageNum); 251 | int hashValue = key; 252 | if (table[key].node == nullptr) 253 | return nullptr; 254 | while (table[key].data.fileId != fileId || table[key].data.pageNum != pageNum) { 255 | key = (key + 1) & MAX_BUFFER_SIZE_M1; 256 | if (table[key].node == nullptr) 257 | return nullptr; 258 | if (key == hashValue) { 259 | throw ItemNotFound(); 260 | } 261 | } 262 | return &(table[key]); 263 | } 264 | 265 | LRUHashItem LRUHash::pop(int fileId, int pageNum) { 266 | int key = hash(fileId, pageNum); 267 | int hashValue = key; 268 | if (table[key].node == nullptr) 269 | throw ItemNotFound(); 270 | while (table[key].data.fileId != fileId || table[key].data.pageNum != pageNum) { 271 | key = (key + 1) & MAX_BUFFER_SIZE_M1; 272 | if (table[key].node == nullptr || key == hashValue) { 273 | throw ItemNotFound(); 274 | } 275 | } 276 | return popByKey(key); 277 | } 278 | 279 | LRUHashItem LRUHash::popByKey(int j) { 280 | /* see http://en.wikipedia.org/wiki/Open_addressing */ 281 | int i = j, k; 282 | total -= 1; 283 | LRUHashItem res = table[i]; 284 | // Debug::info("start popping"); 285 | table[i].node = nullptr; 286 | do { 287 | j = (j + 1) & MAX_BUFFER_SIZE_M1; 288 | if (table[j].node == nullptr) 289 | break; 290 | k = table[j].hashValue; 291 | if ((i <= j) ? ((i < k) && (k <= j)) : (i < k) || (k <= j)) 292 | continue; 293 | table[i] = table[j]; 294 | table[j].node = nullptr; 295 | table[i].node->item = table + i; 296 | i = j; 297 | } while (true); 298 | // Debug::info("popped"); 299 | return res; 300 | } 301 | 302 | /* LRU list */ 303 | LRUList::LRUList() { 304 | head = nullptr; 305 | tail = nullptr; 306 | } 307 | 308 | LRUList::~LRUList() { 309 | while (head != nullptr) { 310 | tail = head->next; 311 | delete head; 312 | head = tail; 313 | } 314 | } 315 | 316 | LRUListNode *LRUList::push_head(LRUHashItem *p) { 317 | LRUListNode *n = new LRUListNode; 318 | n->item = p; 319 | n->prev = nullptr; 320 | n->next = head; 321 | if (head != nullptr) head->prev = n; 322 | head = n; 323 | if (tail == nullptr) tail = head; 324 | return head; 325 | } 326 | 327 | LRUListNode *LRUList::push_back(LRUHashItem *p) { 328 | // debug_ << "[PUSHBACK]" << p->data.fileId << " " << p->data.pageNum << std::endl; 329 | LRUListNode *n = new LRUListNode; 330 | n->item = p; 331 | n->prev = tail; 332 | n->next = nullptr; 333 | if (tail != nullptr) tail->next = n; 334 | tail = n; 335 | if (head == nullptr) head = tail; 336 | return tail; 337 | } 338 | 339 | void LRUList::move_back(LRUListNode *p) { 340 | if (p == nullptr) return; 341 | if (p == tail) return; 342 | if (p == head) head = p->next; 343 | LRUListNode *q = p->prev, *r = p->next; 344 | if (q != nullptr) q->next = r; 345 | if (r != nullptr) r->prev = q; 346 | if (tail != nullptr) { 347 | tail->next = p; 348 | } 349 | p->prev = tail; 350 | tail = p; 351 | p->next = nullptr; 352 | } 353 | 354 | LRUHashItem *LRUList::remove(LRUListNode *p) { 355 | // debug_ << "[REMOVE] try" << std::endl; 356 | if (p == nullptr) return nullptr; 357 | // debug_ << "[REMOVE]" << p->item->data.fileId << " " << p->item->data.pageNum << std::endl; 358 | if (p == head) head = p->next; 359 | if (p == tail) tail = p->prev; 360 | LRUHashItem *res = p->item; 361 | LRUListNode *q = p->prev, *r = p->next; 362 | if (q != nullptr) q->next = r; 363 | if (r != nullptr) r->prev = q; 364 | delete p; 365 | return res; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/ix/index.cpp: -------------------------------------------------------------------------------- 1 | #include "ix/index.h" 2 | #include "rm/filehandle.h" 3 | 4 | namespace sqleast { 5 | namespace ix { 6 | 7 | int Node::getPosition(int value) { 8 | //return the exact value small than or equal to value 9 | int lo = 0; 10 | int hi = size; 11 | int mi; 12 | while(lo < hi){ 13 | mi = (lo + hi) / 2; 14 | if(k[mi] == value) 15 | return mi; 16 | if(k[mi] < value) 17 | lo = mi + 1; 18 | else 19 | hi = mi; 20 | } 21 | return lo - 1; 22 | } 23 | 24 | bool Node::insertK(int value, int position) { 25 | if(isLeaf && size == B_PLUS_TREE_BRANCH + 1) 26 | return false; 27 | if(!isLeaf && size == B_PLUS_TREE_BRANCH + 1) 28 | return false; 29 | size ++; 30 | for(int i = B_PLUS_TREE_BRANCH + 1 ; i > position ; i --) 31 | k[i] = k[i-1]; 32 | k[position] = value; 33 | return true; 34 | } 35 | 36 | bool Node::insertN(RID value, int position) { 37 | if(size == B_PLUS_TREE_BRANCH + 2) 38 | return false; 39 | for(int i = B_PLUS_TREE_BRANCH + 1 ; i > position ; i --) 40 | n[i] = n[i-1]; 41 | n[position] = value; 42 | return true; 43 | } 44 | 45 | bool Node::removeK(int position) { 46 | if(size == 0) 47 | return false; 48 | size --; 49 | for(int i = position ; i < B_PLUS_TREE_BRANCH + 1 ; i ++) 50 | k[i] = k[i+1]; 51 | return true; 52 | } 53 | 54 | bool Node::removeN(int position) { 55 | if(size == 0) 56 | return false; 57 | for(int i = position ; i < B_PLUS_TREE_BRANCH + 1 ; i ++) 58 | n[i] = n[i+1]; 59 | return true; 60 | } 61 | 62 | /* index */ 63 | 64 | void Index::createIndex(const char *indexName) { 65 | // std::cout << "very before open index" << std::endl; 66 | rm::RecordManager::createFile(indexName, sizeof(Node), true); 67 | // std::cout << "before open index" << std::endl; 68 | Index idx(indexName); 69 | RID rid = idx.allocateNode(); 70 | idx.indexInfo_.indexSize = 0; 71 | idx.setRoot(rid); 72 | Node n; 73 | idx.getNode(rid, n); 74 | n.isLeaf = true; 75 | n.size = 0; 76 | n.parent = RID(-1, -1); 77 | memset(n.n, 0, sizeof(n.n)); 78 | memset(n.k, 0, sizeof(n.n)); 79 | idx.commitNode(rid, n); 80 | // std::cout << "index created" << std::endl; 81 | } 82 | 83 | Index::Index(const char *indexName): 84 | handle_(rm::RecordManager::openFile(indexName)) 85 | { 86 | hot_ = RID(-1, -1); 87 | char *p = handle_.getFileInfo(); 88 | p += sizeof(rm::FileInfo); 89 | indexInfo_ = *(IndexInfo *)p; 90 | } 91 | 92 | Index::~Index() { 93 | } 94 | 95 | void Index::commitIndexInfo() { 96 | char *p = handle_.getFileInfo(); 97 | p += sizeof(rm::FileInfo); 98 | *(IndexInfo *)p = indexInfo_; 99 | } 100 | 101 | RID Index::getRootRID() { 102 | return RID(indexInfo_.rootPageNum, indexInfo_.rootSlotNum); 103 | } 104 | 105 | void Index::getRoot(Node &node) { 106 | getNode(getRootRID(), node); 107 | } 108 | 109 | void Index::setRoot(RID rid) { 110 | indexInfo_.rootPageNum = rid.pageNum; 111 | indexInfo_.rootSlotNum = rid.slotNum; 112 | commitIndexInfo(); 113 | } 114 | 115 | void Index::getNode(RID rid, Node &node) { 116 | char *pData = handle_.getRecDataPtr(rid) + FLAG_SIZE; // move flag 117 | memcpy(&node, pData, sizeof(node)); 118 | handle_.releaseRecDataPtr(rid); 119 | } 120 | 121 | void Index::commitNode(RID rid, const Node &node) { 122 | char *pData = handle_.getRecDataPtr(rid) + FLAG_SIZE; 123 | memcpy(pData, &node, sizeof(node)); 124 | handle_.commitPage(rid.pageNum); 125 | handle_.releaseRecDataPtr(rid); 126 | } 127 | 128 | RID Index::allocateNode() { 129 | RID r = handle_.declareRec(); 130 | return r; 131 | } 132 | 133 | RID Index::releaseNode(RID rid) { 134 | handle_.deleteRec(rid); 135 | return rid; 136 | } 137 | 138 | void Index::forcePages() { 139 | handle_.forcePages(); 140 | } 141 | 142 | int Index::getIndexSize() { 143 | return indexInfo_.indexSize; 144 | } 145 | 146 | void Index::incIndexSize() { 147 | indexInfo_.indexSize++; 148 | } 149 | 150 | void Index::decIndexSize() { 151 | indexInfo_.indexSize--; 152 | } 153 | 154 | RID Index::searchEntry(int key) { 155 | hot_ = getRootRID(); 156 | Node v; 157 | getNode(hot_, v); 158 | while(true){ 159 | int r = v.getPosition(key); 160 | if(!v.isLeaf){ 161 | hot_ = v.n[r+1]; 162 | getNode(hot_, v); 163 | } 164 | else{ 165 | if(r >= 0 && v.k[r] == key){ 166 | return v.n[r]; 167 | } 168 | else{ 169 | return RID(-1, r+1); // refer to not find and the second value means the expected position 170 | } 171 | } 172 | } 173 | } 174 | 175 | RID Index::insertEntry(int key, RID value) { 176 | RID v = searchEntry(key); 177 | if(v.pageNum >= 0) 178 | return v; 179 | int posi = v.slotNum; 180 | // std::cout << posi << std::endl; 181 | Node leaf; 182 | getNode(hot_, leaf); 183 | leaf.insertN(value, posi); 184 | leaf.insertK(key, posi); 185 | commitNode(hot_, leaf); 186 | incIndexSize(); 187 | solveOverFlow(hot_); 188 | forcePages(); 189 | return value; 190 | } 191 | 192 | bool Index::removeEntry(int key) { 193 | std::cout << "a" << key << std::endl; 194 | RID v = searchEntry(key); 195 | std::cout << "b" << key << std::endl; 196 | if(v.pageNum < 0) 197 | return false; 198 | Node leaf; 199 | getNode(hot_, leaf); 200 | std::cout << "c" << key << std::endl; 201 | int rank = leaf.getPosition(key); 202 | std::cout << "d" << key << std::endl; 203 | leaf.removeK(rank); 204 | std::cout << "e" << key << std::endl; 205 | leaf.removeN(rank); 206 | std::cout << "f" << key << std::endl; 207 | commitNode(hot_, leaf); 208 | std::cout << "g" << key << std::endl; 209 | decIndexSize(); 210 | std::cout << "h" << key << std::endl; 211 | solveUnderFlow(hot_); 212 | std::cout << "i" << key << std::endl; 213 | forcePages(); 214 | std::cout << "j" << key << std::endl; 215 | return true; 216 | } 217 | 218 | void Index::solveOverFlow(RID rid) { 219 | Node v; 220 | getNode(rid, v); 221 | if(v.isLeaf) { 222 | if (B_PLUS_TREE_BRANCH >= v.size) return; 223 | int half = (B_PLUS_TREE_BRANCH + 1) / 2; 224 | RID nid = allocateNode(); 225 | Node n; 226 | getNode(nid, n); 227 | n.isLeaf = true; 228 | for (int j = 0; j < B_PLUS_TREE_BRANCH - half; j++) { 229 | n.insertN(v.n[half + 1], j); 230 | v.removeN(half + 1); 231 | n.insertK(v.k[half + 1], j); 232 | v.removeK(half + 1); 233 | } 234 | if(getRootRID() == rid){ 235 | RID rt = allocateNode(); 236 | setRoot(rt); 237 | Node root; 238 | getNode(rt, root); 239 | root.isLeaf = false; 240 | root.insertN(rid, 0); 241 | v.parent = rt; 242 | commitNode(rt, root); 243 | } 244 | RID pid = v.parent; 245 | Node p; 246 | getNode(pid, p); 247 | int rank = p.getPosition(v.k[0]) + 1; 248 | p.insertK(n.k[0], rank); 249 | p.insertN(nid, rank + 1); 250 | n.parent = pid; 251 | commitNode(rid, v); 252 | commitNode(nid, n); 253 | commitNode(pid, p); 254 | solveOverFlow(pid); 255 | } 256 | else 257 | { 258 | if (B_PLUS_TREE_BRANCH >= v.size) return; 259 | int half = (B_PLUS_TREE_BRANCH + 1) / 2; 260 | RID nid = allocateNode(); 261 | Node n; 262 | getNode(nid, n); 263 | n.isLeaf = false; 264 | for (int j = 0; j < B_PLUS_TREE_BRANCH - half ; j++) { 265 | n.insertN(v.n[half + 1], j); 266 | v.removeN(half + 1); 267 | n.insertK(v.k[half + 1], j); 268 | v.removeK(half + 1); 269 | Node tmp; 270 | getNode(n.n[j], tmp); 271 | tmp.parent = nid; 272 | commitNode(n.n[j], tmp); 273 | } 274 | n.insertN(v.n[half + 1], B_PLUS_TREE_BRANCH - half); 275 | v.removeN(half + 1); 276 | Node tmp; 277 | getNode(n.n[B_PLUS_TREE_BRANCH - half], tmp); 278 | tmp.parent = nid; 279 | commitNode(n.n[B_PLUS_TREE_BRANCH - half], tmp); 280 | if(getRootRID() == rid){ 281 | RID rt = allocateNode(); 282 | setRoot(rt); 283 | Node root; 284 | getNode(rt, root); 285 | root.isLeaf = false; 286 | root.insertN(rid, 0); 287 | v.parent = rt; 288 | commitNode(rt, root); 289 | } 290 | RID pid = v.parent; 291 | Node p; 292 | getNode(pid, p); 293 | int rank = p.getPosition(v.k[0]) + 1; 294 | p.insertK(v.k[half], rank); 295 | p.insertN(nid, rank + 1); 296 | v.removeK(half); 297 | n.parent = pid; 298 | commitNode(rid, v); 299 | commitNode(nid, n); 300 | commitNode(pid, p); 301 | solveOverFlow(pid); 302 | } 303 | } 304 | 305 | void Index::solveUnderFlow(RID rid) { 306 | Node v; 307 | getNode(rid, v); 308 | if(v.isLeaf){ 309 | if(B_PLUS_TREE_BRANCH / 2 <= v.size) return; 310 | if(getRootRID() == rid) { 311 | return; 312 | } 313 | RID pid = v.parent; 314 | Node p; 315 | getNode(pid, p); 316 | int r = 0; 317 | while(!(p.n[r] == rid)) r++; 318 | if(r > 0){ 319 | Node ls; 320 | getNode(p.n[r-1], ls); 321 | if(B_PLUS_TREE_BRANCH / 2 < ls.size){ 322 | p.k[r-1] = ls.k[ls.size - 1]; 323 | v.insertN(ls.n[ls.size - 1], 0); 324 | ls.removeN(ls.size - 1); 325 | v.insertK(ls.k[ls.size - 1], 0); 326 | ls.removeK(ls.size - 1); 327 | commitNode(p.n[r-1], ls); 328 | commitNode(rid, v); 329 | commitNode(pid, p); 330 | return; 331 | } 332 | } 333 | if(r < p.size){ 334 | Node rs; 335 | getNode(p.n[r+1], rs); 336 | if(B_PLUS_TREE_BRANCH / 2 < rs.size){ 337 | v.insertN(rs.n[0], v.size); 338 | rs.removeN(0); 339 | v.insertK(rs.k[0], v.size); 340 | rs.removeK(0); 341 | p.k[r] = rs.k[0]; 342 | commitNode(p.n[r+1], rs); 343 | commitNode(rid, v); 344 | commitNode(pid, p); 345 | return; 346 | } 347 | } 348 | if(r > 0){ 349 | Node ls; 350 | getNode(p.n[r-1], ls); 351 | int len = ls.size; 352 | for(int j = len - 1 ; j >= 0 ; j --) 353 | { 354 | v.insertN(ls.n[j], 0); 355 | ls.removeN(j); 356 | v.insertK(ls.k[j], 0); 357 | ls.removeK(j); 358 | } 359 | releaseNode(p.n[r-1]); 360 | p.removeN(r-1); 361 | p.removeK(r-1); 362 | commitNode(rid, v); 363 | commitNode(pid, p); 364 | } 365 | else 366 | { 367 | Node rs; 368 | getNode(p.n[r+1], rs); 369 | int len = rs.size; 370 | int nlen = v.size; 371 | for(int j = len - 1 ; j >= 0 ; j --) 372 | { 373 | v.insertN(rs.n[j], nlen); 374 | rs.removeN(j); 375 | v.insertK(rs.k[j], nlen); 376 | rs.removeK(j); 377 | } 378 | p.removeN(r+1); 379 | p.removeK(r); 380 | //TODO: release will produce error 381 | releaseNode(p.n[r+1]); 382 | commitNode(rid, v); 383 | commitNode(pid, p); 384 | } 385 | solveUnderFlow(pid); 386 | return; 387 | } 388 | else{ 389 | if((B_PLUS_TREE_BRANCH) / 2 <= v.size) return; 390 | if(getRootRID() == rid) { 391 | if(v.size == 0){ 392 | RID child = v.n[0]; 393 | setRoot(child); 394 | releaseNode(rid); 395 | } 396 | return; 397 | } 398 | RID pid = v.parent; 399 | Node p; 400 | getNode(pid, p); 401 | int r = 0; 402 | while(!(p.n[r] == rid)) r++; 403 | if(r > 0){ 404 | Node ls; 405 | getNode(p.n[r-1], ls); 406 | if(B_PLUS_TREE_BRANCH / 2 < ls.size){ 407 | v.insertN(ls.n[ls.size], 0); 408 | ls.removeN(ls.size); 409 | v.insertK(p.k[r-1], 0); 410 | p.k[r-1] = ls.k[ls.size - 1]; 411 | ls.removeK(ls.size - 1); 412 | commitNode(p.n[r-1], ls); 413 | commitNode(rid, v); 414 | commitNode(pid, p); 415 | return; 416 | } 417 | } 418 | if(r < p.size){ 419 | Node rs; 420 | getNode(p.n[r+1], rs); 421 | if(B_PLUS_TREE_BRANCH / 2 < rs.size){ 422 | v.insertN(rs.n[0], v.size + 1); 423 | rs.removeN(0); 424 | v.insertK(p.k[r], v.size); 425 | p.k[r] = rs.k[0]; 426 | rs.removeK(0); 427 | commitNode(p.n[r+1], rs); 428 | commitNode(rid, v); 429 | commitNode(pid, p); 430 | return; 431 | } 432 | } 433 | if(r > 0){ 434 | Node ls; 435 | getNode(p.n[r-1], ls); 436 | int len = ls.size; 437 | v.insertK(p.k[r-1], 0); 438 | v.insertN(ls.n[len], 0); 439 | v.removeN(len); 440 | for(int j = len - 1 ; j >= 0 ; j --) 441 | { 442 | v.insertN(ls.n[j], 0); 443 | ls.removeN(j); 444 | v.insertK(ls.k[j], 0); 445 | ls.removeK(j); 446 | } 447 | releaseNode(p.n[r-1]); 448 | p.removeN(r-1); 449 | p.removeK(r-1); 450 | commitNode(rid, v); 451 | commitNode(pid, p); 452 | } 453 | else 454 | { 455 | Node rs; 456 | getNode(p.n[r+1], rs); 457 | int len = rs.size; 458 | int nlen = v.size; 459 | v.insertK(p.k[r], nlen); 460 | for(int j = 0 ; j < len ; j ++) 461 | { 462 | v.insertN(rs.n[0], nlen + 1 + j); 463 | rs.removeN(0); 464 | v.insertK(rs.k[0], nlen + 1 + j); 465 | rs.removeK(0); 466 | } 467 | v.insertN(rs.n[0], nlen + 1 + len); 468 | rs.removeN(0); 469 | //TODO: release will produce error 470 | //releaseNode(p.n[r+1]); 471 | p.removeN(r+1); 472 | p.removeK(r); 473 | commitNode(rid, v); 474 | commitNode(pid, p); 475 | } 476 | solveUnderFlow(pid); 477 | return; 478 | } 479 | } 480 | 481 | void Index::printIndex() { 482 | printNode(getRootRID()); 483 | } 484 | 485 | void Index::printNode(RID rid){ 486 | Node n; 487 | getNode(rid, n); 488 | /*std::cout << "this:" << rid.pageNum << "," << rid.slotNum << std::endl; 489 | std::cout << "parent:" << n.parent.pageNum << "," << n.parent.slotNum << std::endl;*/ 490 | if(n.isLeaf){ 491 | for(int i = 0 ; i < n.size ; i ++) 492 | { 493 | std::cout << "(" << n.n[i].pageNum << "," << n.n[i].slotNum << ")" << " " << n.k[i] << " | "; 494 | } 495 | std::cout << std::endl; 496 | } 497 | else{ 498 | for(int i = 0 ; i < n.size ; i ++) 499 | { 500 | std::cout << "(" << n.n[i].pageNum << "," << n.n[i].slotNum << ")" << " " << n.k[i] << " | "; 501 | } 502 | std::cout << "(" << n.n[n.size].pageNum << "," << n.n[n.size].slotNum << ")" << std::endl; 503 | for(int i = 0 ; i <= n.size ; i ++) 504 | printNode(n.n[i]); 505 | } 506 | } 507 | } 508 | } -------------------------------------------------------------------------------- /src/frontend/interp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * interp.c: interpreter for RQL 3 | * 4 | * Authors: Dallan Quass (quass@cs.stanford.edu) 5 | * Jan Jannink 6 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "parser_internal.h" 15 | #include "parser.hpp" 16 | 17 | #include "sm.h" 18 | #include "ql.h" 19 | 20 | extern SM_Manager *pSmm; 21 | extern QL_Manager *pQlm; 22 | 23 | #define E_OK 0 24 | #define E_INCOMPATIBLE -1 25 | #define E_TOOMANY -2 26 | #define E_NOLENGTH -3 27 | #define E_INVINTSIZE -4 28 | #define E_INVREALSIZE -5 29 | #define E_INVFORMATSTRING -6 30 | #define E_INVSTRLEN -7 31 | #define E_DUPLICATEATTR -8 32 | #define E_TOOLONG -9 33 | #define E_STRINGTOOLONG -10 34 | 35 | /* 36 | * file pointer to which error messages are printed 37 | */ 38 | #define ERRFP stderr 39 | 40 | /* 41 | * local functions 42 | */ 43 | static int mk_attr_infos(NODE *list, int max, AttrInfo attrInfos[]); 44 | static int parse_format_string(char *format_string, AttrType *type, int *len); 45 | static int mk_rel_attrs(NODE *list, int max, RelAttr relAttrs[]); 46 | static void mk_rel_attr(NODE *node, RelAttr &relAttr); 47 | static int mk_relations(NODE *list, int max, char *relations[]); 48 | static int mk_conditions(NODE *list, int max, Condition conditions[]); 49 | static int mk_values(NODE *list, int max, Value values[]); 50 | static void mk_value(NODE *node, Value &value); 51 | static void print_error(char *errmsg, RC errval); 52 | static void echo_query(NODE *n); 53 | static void print_attrtypes(NODE *n); 54 | static void print_op(CompOp op); 55 | static void print_relattr(NODE *n); 56 | static void print_value(NODE *n); 57 | static void print_condition(NODE *n); 58 | static void print_relattrs(NODE *n); 59 | static void print_relations(NODE *n); 60 | static void print_conditions(NODE *n); 61 | static void print_values(NODE *n); 62 | 63 | /* 64 | * interp: interprets parse trees 65 | * 66 | */ 67 | RC interp(NODE *n) 68 | { 69 | RC errval = 0; /* returned error value */ 70 | 71 | /* if input not coming from a terminal, then echo the query */ 72 | if(!isatty(0)) 73 | echo_query(n); 74 | 75 | switch(n -> kind){ 76 | 77 | case N_CREATETABLE: /* for CreateTable() */ 78 | { 79 | int nattrs; 80 | AttrInfo attrInfos[MAXATTRS]; 81 | 82 | /* Make sure relation name isn't too long */ 83 | if(strlen(n -> u.CREATETABLE.relname) > MAXNAME){ 84 | print_error((char*)"create", E_TOOLONG); 85 | break; 86 | } 87 | 88 | /* Make a list of AttrInfos suitable for sending to Create */ 89 | nattrs = mk_attr_infos(n -> u.CREATETABLE.attrlist, MAXATTRS, 90 | attrInfos); 91 | if(nattrs < 0){ 92 | print_error((char*)"create", nattrs); 93 | break; 94 | } 95 | 96 | /* Make the call to create */ 97 | errval = pSmm->CreateTable(n->u.CREATETABLE.relname, nattrs, 98 | attrInfos); 99 | break; 100 | } 101 | 102 | case N_CREATEINDEX: /* for CreateIndex() */ 103 | 104 | errval = pSmm->CreateIndex(n->u.CREATEINDEX.relname, 105 | n->u.CREATEINDEX.attrname); 106 | break; 107 | 108 | case N_DROPINDEX: /* for DropIndex() */ 109 | 110 | errval = pSmm->DropIndex(n->u.DROPINDEX.relname, 111 | n->u.DROPINDEX.attrname); 112 | break; 113 | 114 | case N_DROPTABLE: /* for DropTable() */ 115 | 116 | errval = pSmm->DropTable(n->u.DROPTABLE.relname); 117 | break; 118 | 119 | case N_LOAD: /* for Load() */ 120 | 121 | errval = pSmm->Load(n->u.LOAD.relname, 122 | n->u.LOAD.filename); 123 | break; 124 | 125 | case N_SET: /* for Set() */ 126 | 127 | errval = pSmm->Set(n->u.SET.paramName, 128 | n->u.SET.string); 129 | break; 130 | 131 | case N_HELP: /* for Help() */ 132 | 133 | if (n->u.HELP.relname) 134 | errval = pSmm->Help(n->u.HELP.relname); 135 | else 136 | errval = pSmm->Help(); 137 | break; 138 | 139 | case N_PRINT: /* for Print() */ 140 | 141 | errval = pSmm->Print(n->u.PRINT.relname); 142 | break; 143 | 144 | case N_QUERY: /* for Query() */ 145 | { 146 | int nSelAttrs = 0; 147 | RelAttr relAttrs[MAXATTRS]; 148 | int nRelations = 0; 149 | char *relations[MAXATTRS]; 150 | int nConditions = 0; 151 | Condition conditions[MAXATTRS]; 152 | 153 | /* Make a list of RelAttrs suitable for sending to Query */ 154 | nSelAttrs = mk_rel_attrs(n->u.QUERY.relattrlist, MAXATTRS, 155 | relAttrs); 156 | if(nSelAttrs < 0){ 157 | print_error((char*)"select", nSelAttrs); 158 | break; 159 | } 160 | 161 | /* Make a list of relation names suitable for sending to Query */ 162 | nRelations = mk_relations(n->u.QUERY.rellist, MAXATTRS, relations); 163 | if(nRelations < 0){ 164 | print_error((char*)"select", nRelations); 165 | break; 166 | } 167 | 168 | /* Make a list of Conditions suitable for sending to Query */ 169 | nConditions = mk_conditions(n->u.QUERY.conditionlist, MAXATTRS, 170 | conditions); 171 | if(nConditions < 0){ 172 | print_error((char*)"select", nConditions); 173 | break; 174 | } 175 | 176 | /* Make the call to Select */ 177 | errval = pQlm->Select(nSelAttrs, relAttrs, 178 | nRelations, relations, 179 | nConditions, conditions); 180 | break; 181 | } 182 | 183 | case N_INSERT: /* for Insert() */ 184 | { 185 | int nValues = 0; 186 | Value values[MAXATTRS]; 187 | 188 | /* Make a list of Values suitable for sending to Insert */ 189 | nValues = mk_values(n->u.INSERT.valuelist, MAXATTRS, values); 190 | if(nValues < 0){ 191 | print_error((char*)"insert", nValues); 192 | break; 193 | } 194 | 195 | /* Make the call to insert */ 196 | errval = pQlm->Insert(n->u.INSERT.relname, 197 | nValues, values); 198 | break; 199 | } 200 | 201 | case N_DELETE: /* for Delete() */ 202 | { 203 | int nConditions = 0; 204 | Condition conditions[MAXATTRS]; 205 | 206 | /* Make a list of Conditions suitable for sending to delete */ 207 | nConditions = mk_conditions(n->u.DELETE.conditionlist, MAXATTRS, 208 | conditions); 209 | if(nConditions < 0){ 210 | print_error((char*)"delete", nConditions); 211 | break; 212 | } 213 | 214 | /* Make the call to delete */ 215 | errval = pQlm->Delete(n->u.DELETE.relname, 216 | nConditions, conditions); 217 | break; 218 | } 219 | 220 | case N_UPDATE: /* for Update() */ 221 | { 222 | RelAttr relAttr; 223 | 224 | // The RHS can be either a value or an attribute 225 | Value rhsValue; 226 | RelAttr rhsRelAttr; 227 | int bIsValue; 228 | 229 | int nConditions = 0; 230 | Condition conditions[MAXATTRS]; 231 | 232 | /* Make a RelAttr suitable for sending to Update */ 233 | mk_rel_attr(n->u.UPDATE.relattr, relAttr); 234 | 235 | struct node *rhs = n->u.UPDATE.relorvalue; 236 | if (rhs->u.RELATTR_OR_VALUE.relattr) { 237 | mk_rel_attr(rhs->u.RELATTR_OR_VALUE.relattr, rhsRelAttr); 238 | bIsValue = 0; 239 | } else { 240 | /* Make a value suitable for sending to update */ 241 | mk_value(rhs->u.RELATTR_OR_VALUE.value, rhsValue); 242 | bIsValue = 1; 243 | } 244 | 245 | /* Make a list of Conditions suitable for sending to Update */ 246 | nConditions = mk_conditions(n->u.UPDATE.conditionlist, MAXATTRS, 247 | conditions); 248 | if(nConditions < 0){ 249 | print_error((char*)"update", nConditions); 250 | break; 251 | } 252 | 253 | /* Make the call to update */ 254 | errval = pQlm->Update(n->u.UPDATE.relname, relAttr, bIsValue, 255 | rhsRelAttr, rhsValue, nConditions, conditions); 256 | break; 257 | } 258 | 259 | default: // should never get here 260 | break; 261 | } 262 | 263 | return (errval); 264 | } 265 | 266 | /* 267 | * mk_attr_infos: converts a list of attribute descriptors (attribute names, 268 | * types, and lengths) to an array of AttrInfo's so it can be sent to 269 | * Create. 270 | * 271 | * Returns: 272 | * length of the list on success ( >= 0 ) 273 | * error code otherwise 274 | */ 275 | static int mk_attr_infos(NODE *list, int max, AttrInfo attrInfos[]) 276 | { 277 | int i; 278 | int len; 279 | AttrType type; 280 | NODE *attr; 281 | RC errval; 282 | 283 | /* for each element of the list... */ 284 | for(i = 0; list != NULL; ++i, list = list -> u.LIST.next) { 285 | 286 | /* if the list is too long, then error */ 287 | if(i == max) 288 | return E_TOOMANY; 289 | 290 | attr = list -> u.LIST.curr; 291 | 292 | /* Make sure the attribute name isn't too long */ 293 | if(strlen(attr -> u.ATTRTYPE.attrname) > MAXNAME) 294 | return E_TOOLONG; 295 | 296 | /* interpret the format string */ 297 | errval = parse_format_string(attr -> u.ATTRTYPE.type, &type, &len); 298 | if(errval != E_OK) 299 | return errval; 300 | 301 | /* add it to the list */ 302 | attrInfos[i].attrName = attr -> u.ATTRTYPE.attrname; 303 | attrInfos[i].attrType = type; 304 | attrInfos[i].attrLength = len; 305 | } 306 | 307 | return i; 308 | } 309 | 310 | /* 311 | * mk_rel_attrs: converts a list of relation-attributes ( pairs) into an array of RelAttrs 313 | * 314 | * Returns: 315 | * the lengh of the list on success ( >= 0 ) 316 | * error code otherwise 317 | */ 318 | static int mk_rel_attrs(NODE *list, int max, RelAttr relAttrs[]) 319 | { 320 | int i; 321 | 322 | /* For each element of the list... */ 323 | for(i = 0; list != NULL; ++i, list = list -> u.LIST.next){ 324 | /* If the list is too long then error */ 325 | if(i == max) 326 | return E_TOOMANY; 327 | 328 | mk_rel_attr(list->u.LIST.curr, relAttrs[i]); 329 | } 330 | 331 | return i; 332 | } 333 | 334 | /* 335 | * mk_rel_attr: converts a single relation-attribute ( pair) into a RelAttr 337 | */ 338 | static void mk_rel_attr(NODE *node, RelAttr &relAttr) 339 | { 340 | relAttr.relName = node->u.RELATTR.relname; 341 | relAttr.attrName = node->u.RELATTR.attrname; 342 | } 343 | 344 | /* 345 | * mk_relations: converts a list of relations into an array of relations 346 | * 347 | * Returns: 348 | * the lengh of the list on success ( >= 0 ) 349 | * error code otherwise 350 | */ 351 | static int mk_relations(NODE *list, int max, char *relations[]) 352 | { 353 | int i; 354 | NODE *current; 355 | 356 | /* for each element of the list... */ 357 | for(i = 0; list != NULL; ++i, list = list -> u.LIST.next){ 358 | /* If the list is too long then error */ 359 | if(i == max) 360 | return E_TOOMANY; 361 | 362 | current = list -> u.LIST.curr; 363 | relations[i] = current->u.RELATION.relname; 364 | } 365 | 366 | return i; 367 | } 368 | 369 | /* 370 | * mk_conditions: converts a list of conditions into an array of conditions 371 | * 372 | * Returns: 373 | * the lengh of the list on success ( >= 0 ) 374 | * error code otherwise 375 | */ 376 | static int mk_conditions(NODE *list, int max, Condition conditions[]) 377 | { 378 | int i; 379 | NODE *current; 380 | 381 | /* for each element of the list... */ 382 | for(i = 0; list != NULL; ++i, list = list -> u.LIST.next){ 383 | /* If the list is too long then error */ 384 | if(i == max) 385 | return E_TOOMANY; 386 | 387 | current = list -> u.LIST.curr; 388 | conditions[i].lhsAttr.relName = 389 | current->u.CONDITION.lhsRelattr->u.RELATTR.relname; 390 | conditions[i].lhsAttr.attrName = 391 | current->u.CONDITION.lhsRelattr->u.RELATTR.attrname; 392 | conditions[i].op = current->u.CONDITION.op; 393 | if (current->u.CONDITION.rhsRelattr) { 394 | conditions[i].bRhsIsAttr = TRUE; 395 | conditions[i].rhsAttr.relName = 396 | current->u.CONDITION.rhsRelattr->u.RELATTR.relname; 397 | conditions[i].rhsAttr.attrName = 398 | current->u.CONDITION.rhsRelattr->u.RELATTR.attrname; 399 | } 400 | else { 401 | conditions[i].bRhsIsAttr = FALSE; 402 | mk_value(current->u.CONDITION.rhsValue, conditions[i].rhsValue); 403 | } 404 | } 405 | 406 | return i; 407 | } 408 | 409 | /* 410 | * mk_values: converts a list of values into an array of values 411 | * 412 | * Returns: 413 | * the lengh of the list on success ( >= 0 ) 414 | * error code otherwise 415 | */ 416 | static int mk_values(NODE *list, int max, Value values[]) 417 | { 418 | int i; 419 | 420 | /* for each element of the list... */ 421 | for(i = 0; list != NULL; ++i, list = list -> u.LIST.next){ 422 | /* If the list is too long then error */ 423 | if(i == max) 424 | return E_TOOMANY; 425 | 426 | mk_value(list->u.LIST.curr, values[i]); 427 | } 428 | 429 | return i; 430 | } 431 | 432 | /* 433 | * mk_values: converts a single value node into a Value 434 | */ 435 | static void mk_value(NODE *node, Value &value) 436 | { 437 | value.type = node->u.VALUE.type; 438 | switch (value.type) { 439 | case INT: 440 | value.data = (void *)&node->u.VALUE.ival; 441 | break; 442 | case FLOAT: 443 | value.data = (void *)&node->u.VALUE.rval; 444 | break; 445 | case STRING: 446 | value.data = (void *)node->u.VALUE.sval; 447 | break; 448 | } 449 | } 450 | 451 | /* 452 | * parse_format_string: deciphers a format string of the form: xl 453 | * where x is a type specification (one of `i' INTEGER, `r' REAL, 454 | * `s' STRING, or `c' STRING (character)) and l is a length (l is 455 | * optional for `i' and `r'), and stores the type in *type and the 456 | * length in *len. 457 | * 458 | * Returns 459 | * E_OK on success 460 | * error code otherwise 461 | */ 462 | static int parse_format_string(char *format_string, AttrType *type, int *len) 463 | { 464 | int n; 465 | char c; 466 | 467 | /* extract the components of the format string */ 468 | n = sscanf(format_string, "%c%d", &c, len); 469 | 470 | /* if no length given... */ 471 | if(n == 1){ 472 | 473 | switch(c){ 474 | case 'i': 475 | *type = INT; 476 | *len = sizeof(int); 477 | break; 478 | case 'f': 479 | case 'r': 480 | *type = FLOAT; 481 | *len = sizeof(float); 482 | break; 483 | case 's': 484 | case 'c': 485 | return E_NOLENGTH; 486 | default: 487 | return E_INVFORMATSTRING; 488 | } 489 | } 490 | 491 | /* if both are given, make sure the length is valid */ 492 | else if(n == 2){ 493 | 494 | switch(c){ 495 | case 'i': 496 | *type = INT; 497 | if(*len != sizeof(int)) 498 | return E_INVINTSIZE; 499 | break; 500 | case 'f': 501 | case 'r': 502 | *type = FLOAT; 503 | if(*len != sizeof(float)) 504 | return E_INVREALSIZE; 505 | break; 506 | case 's': 507 | case 'c': 508 | *type = STRING; 509 | if(*len < 1 || *len > MAXSTRINGLEN) 510 | return E_INVSTRLEN; 511 | break; 512 | default: 513 | return E_INVFORMATSTRING; 514 | } 515 | } 516 | 517 | /* otherwise it's not a valid format string */ 518 | else 519 | return E_INVFORMATSTRING; 520 | 521 | return E_OK; 522 | } 523 | 524 | /* 525 | * print_error: prints an error message corresponding to errval 526 | */ 527 | static void print_error(char *errmsg, RC errval) 528 | { 529 | if(errmsg != NULL) 530 | fprintf(stderr, "%s: ", errmsg); 531 | switch(errval){ 532 | case E_OK: 533 | fprintf(ERRFP, "no error\n"); 534 | break; 535 | case E_INCOMPATIBLE: 536 | fprintf(ERRFP, "attributes must be from selected relation(s)\n"); 537 | break; 538 | case E_TOOMANY: 539 | fprintf(ERRFP, "too many elements\n"); 540 | break; 541 | case E_NOLENGTH: 542 | fprintf(ERRFP, "length must be specified for STRING attribute\n"); 543 | break; 544 | case E_INVINTSIZE: 545 | fprintf(ERRFP, "invalid size for INTEGER attribute (should be %d)\n", 546 | (int)sizeof(int)); 547 | break; 548 | case E_INVREALSIZE: 549 | fprintf(ERRFP, "invalid size for REAL attribute (should be %d)\n", 550 | (int)sizeof(real)); 551 | break; 552 | case E_INVFORMATSTRING: 553 | fprintf(ERRFP, "invalid format string\n"); 554 | break; 555 | case E_INVSTRLEN: 556 | fprintf(ERRFP, "invalid length for string attribute\n"); 557 | break; 558 | case E_DUPLICATEATTR: 559 | fprintf(ERRFP, "duplicated attribute name\n"); 560 | break; 561 | case E_TOOLONG: 562 | fprintf(stderr, "relation name or attribute name too long\n"); 563 | break; 564 | case E_STRINGTOOLONG: 565 | fprintf(stderr, "string attribute too long\n"); 566 | break; 567 | default: 568 | fprintf(ERRFP, "unrecognized errval: %d\n", errval); 569 | } 570 | } 571 | 572 | static void echo_query(NODE *n) 573 | { 574 | switch(n -> kind){ 575 | case N_CREATETABLE: /* for CreateTable() */ 576 | printf("create table %s (", n -> u.CREATETABLE.relname); 577 | print_attrtypes(n -> u.CREATETABLE.attrlist); 578 | printf(")"); 579 | printf(";\n"); 580 | break; 581 | case N_CREATEINDEX: /* for CreateIndex() */ 582 | printf("create index %s(%s);\n", n -> u.CREATEINDEX.relname, 583 | n -> u.CREATEINDEX.attrname); 584 | break; 585 | case N_DROPINDEX: /* for DropIndex() */ 586 | printf("drop index %s(%s);\n", n -> u.DROPINDEX.relname, 587 | n -> u.DROPINDEX.attrname); 588 | break; 589 | case N_DROPTABLE: /* for DropTable() */ 590 | printf("drop table %s;\n", n -> u.DROPTABLE.relname); 591 | break; 592 | case N_LOAD: /* for Load() */ 593 | printf("load %s(\"%s\");\n", 594 | n -> u.LOAD.relname, n -> u.LOAD.filename); 595 | break; 596 | case N_HELP: /* for Help() */ 597 | printf("help"); 598 | if(n -> u.HELP.relname != NULL) 599 | printf(" %s", n -> u.HELP.relname); 600 | printf(";\n"); 601 | break; 602 | case N_PRINT: /* for Print() */ 603 | printf("print %s;\n", n -> u.PRINT.relname); 604 | break; 605 | case N_SET: /* for Set() */ 606 | printf("set %s = \"%s\";\n", n->u.SET.paramName, n->u.SET.string); 607 | break; 608 | case N_QUERY: /* for Query() */ 609 | printf("select "); 610 | print_relattrs(n -> u.QUERY.relattrlist); 611 | printf("\n from "); 612 | print_relations(n -> u.QUERY.rellist); 613 | printf("\n"); 614 | if (n->u.QUERY.conditionlist) { 615 | printf("where "); 616 | print_conditions(n->u.QUERY.conditionlist); 617 | } 618 | printf(";\n"); 619 | break; 620 | case N_INSERT: /* for Insert() */ 621 | printf("insert into %s values ( ",n->u.INSERT.relname); 622 | print_values(n -> u.INSERT.valuelist); 623 | printf(");\n"); 624 | break; 625 | case N_DELETE: /* for Delete() */ 626 | printf("delete %s ",n->u.DELETE.relname); 627 | if (n->u.DELETE.conditionlist) { 628 | printf("where "); 629 | print_conditions(n->u.DELETE.conditionlist); 630 | } 631 | printf(";\n"); 632 | break; 633 | case N_UPDATE: /* for Update() */ 634 | { 635 | printf("update %s set ",n->u.UPDATE.relname); 636 | print_relattr(n->u.UPDATE.relattr); 637 | printf(" = "); 638 | struct node *rhs = n->u.UPDATE.relorvalue; 639 | 640 | /* The RHS can be either a relation.attribute or a value */ 641 | if (rhs->u.RELATTR_OR_VALUE.relattr) { 642 | /* Print out the relation.attribute */ 643 | print_relattr(rhs->u.RELATTR_OR_VALUE.relattr); 644 | } else { 645 | /* Print out the value */ 646 | print_value(rhs->u.RELATTR_OR_VALUE.value); 647 | } 648 | if (n->u.UPDATE.conditionlist) { 649 | printf("where "); 650 | print_conditions(n->u.UPDATE.conditionlist); 651 | } 652 | printf(";\n"); 653 | break; 654 | } 655 | default: // should never get here 656 | break; 657 | } 658 | fflush(stdout); 659 | } 660 | 661 | static void print_attrtypes(NODE *n) 662 | { 663 | NODE *attr; 664 | 665 | for(; n != NULL; n = n -> u.LIST.next){ 666 | attr = n -> u.LIST.curr; 667 | printf("%s = %s", attr -> u.ATTRTYPE.attrname, attr -> u.ATTRTYPE.type); 668 | if(n -> u.LIST.next != NULL) 669 | printf(", "); 670 | } 671 | } 672 | 673 | static void print_op(CompOp op) 674 | { 675 | switch(op){ 676 | case EQ_OP: 677 | printf(" ="); 678 | break; 679 | case NE_OP: 680 | printf(" <>"); 681 | break; 682 | case LT_OP: 683 | printf(" <"); 684 | break; 685 | case LE_OP: 686 | printf(" <="); 687 | break; 688 | case GT_OP: 689 | printf(" >"); 690 | break; 691 | case GE_OP: 692 | printf(" >="); 693 | break; 694 | case NO_OP: 695 | printf(" NO_OP"); 696 | break; 697 | } 698 | } 699 | 700 | static void print_relattr(NODE *n) 701 | { 702 | printf(" "); 703 | if (n->u.RELATTR.relname) 704 | printf("%s.",n->u.RELATTR.relname); 705 | printf("%s",n->u.RELATTR.attrname); 706 | } 707 | 708 | static void print_value(NODE *n) 709 | { 710 | switch(n -> u.VALUE.type){ 711 | case INT: 712 | printf(" %d", n -> u.VALUE.ival); 713 | break; 714 | case FLOAT: 715 | printf(" %f", n -> u.VALUE.rval); 716 | break; 717 | case STRING: 718 | printf(" \"%s\"", n -> u.VALUE.sval); 719 | break; 720 | } 721 | } 722 | 723 | static void print_condition(NODE *n) 724 | { 725 | print_relattr(n->u.CONDITION.lhsRelattr); 726 | print_op(n->u.CONDITION.op); 727 | if (n->u.CONDITION.rhsRelattr) 728 | print_relattr(n->u.CONDITION.rhsRelattr); 729 | else 730 | print_value(n->u.CONDITION.rhsValue); 731 | } 732 | 733 | static void print_relattrs(NODE *n) 734 | { 735 | for(; n != NULL; n = n -> u.LIST.next){ 736 | print_relattr(n->u.LIST.curr); 737 | if(n -> u.LIST.next != NULL) 738 | printf(","); 739 | } 740 | } 741 | 742 | static void print_relations(NODE *n) 743 | { 744 | for(; n != NULL; n = n -> u.LIST.next){ 745 | printf(" %s", n->u.LIST.curr->u.RELATION.relname); 746 | if(n -> u.LIST.next != NULL) 747 | printf(","); 748 | } 749 | } 750 | 751 | static void print_conditions(NODE *n) 752 | { 753 | for(; n != NULL; n = n -> u.LIST.next){ 754 | print_condition(n->u.LIST.curr); 755 | if(n -> u.LIST.next != NULL) 756 | printf(" and"); 757 | } 758 | } 759 | 760 | static void print_values(NODE *n) 761 | { 762 | for(; n != NULL; n = n -> u.LIST.next){ 763 | print_value(n->u.LIST.curr); 764 | if(n -> u.LIST.next != NULL) 765 | printf(","); 766 | } 767 | } 768 | 769 | -------------------------------------------------------------------------------- /src/ql/parser.cpp.bak: -------------------------------------------------------------------------------- 1 | 2 | #include "ql/query.h" 3 | #include "ql/parser.h" 4 | 5 | namespace sqleast { 6 | namespace ql { 7 | 8 | StructuredQuery* Parser::parse(std::string input) { 9 | if (q_ != nullptr) delete q_; 10 | 11 | std::string firstWord = getWord(input); 12 | if(firstWord == "CREATE"){ 13 | std::string secondWord = getWord(input); 14 | if(secondWord == "DATABASE"){ 15 | std::string word = getWord(input); 16 | SingleStringQuery* k = new SingleStringQuery(); 17 | strcpy(k->name, word.c_str()); 18 | k->type = Q_CREATE_DB; 19 | q_ = k; 20 | std::cout << "create db\n"; 21 | std::cout << k->name << std::endl; 22 | } 23 | if(secondWord == "TABLE"){ 24 | std::string word = getWord(input); 25 | CreateTableQuery* k = new CreateTableQuery(); 26 | strcpy(k->name, word.c_str()); 27 | k->type = Q_CREATE_TABLE; 28 | word = getWord(input); 29 | int no = 0; 30 | if(word == "("){ 31 | word = getWord(input); 32 | while(word != ")"){ 33 | (k->attrs)[no].attrName = new char[MAX_NAME_LENGTH + 1]; 34 | strcpy((k->attrs)[no].attrName, word.c_str()); 35 | word = getWord(input); 36 | int type, len; 37 | getAttr(word, type, len); 38 | (k->attrs)[no].attrLength = len; 39 | (k->attrs)[no].isPrimary = false; 40 | if(type == 0) 41 | (k->attrs)[no].attrType = INT; 42 | if(type == 1) 43 | (k->attrs)[no].attrType = STRING; 44 | word = getWord(input); 45 | if(word == "NOT") { 46 | (k->attrs)[no].nullable = false; 47 | word = getWord(input); 48 | word = getWord(input); 49 | if(word == "PRIMARY"){ 50 | word = getWord(input); 51 | if(word == "KEY"){ 52 | word = getWord(input); 53 | if(word[0] == '(' && word[(int)word.size() - 1] == ')') 54 | word = word.substr(1, word.size() - 2); 55 | for(int i = 0 ; i < no ; i ++){ 56 | if((k->attrs)[i].attrName == word){ 57 | (k->attrs)[i].isPrimary = true; 58 | } 59 | } 60 | word = getWord(input); 61 | } 62 | } 63 | } 64 | else{ 65 | (k->attrs)[no].nullable = true; 66 | if(word == "PRIMARY"){ 67 | word = getWord(input); 68 | if(word == "KEY"){ 69 | word = getWord(input); 70 | if(word[0] == '(' && word[(int)word.size() - 1] == ')') 71 | word = word.substr(1, word.size() - 2); 72 | for(int i = 0 ; i < no ; i ++){ 73 | if((k->attrs)[i].attrName == word){ 74 | (k->attrs)[i].isPrimary = true; 75 | } 76 | } 77 | word = getWord(input); 78 | } 79 | } 80 | else{ 81 | } 82 | } 83 | no ++; 84 | } 85 | } 86 | k->attrNum = no; 87 | q_ = k; 88 | std::cout << "create table\n"; 89 | std::cout << k->name << std::endl; 90 | for(int i = 0 ; i < no ; i ++) { 91 | std::cout << (k->attrs)[i].attrName << "|" << (k->attrs)[i].attrType << "|" << (k->attrs)[i].attrLength << "|" 92 | << (k->attrs)[i].nullable << "|" << (k->attrs)[i].isPrimary << std::endl; 93 | } 94 | } 95 | } 96 | if(firstWord == "DROP"){ 97 | std::string secondWord = getWord(input); 98 | if(secondWord == "DATABASE"){ 99 | std::string word = getWord(input); 100 | SingleStringQuery* k = new SingleStringQuery(); 101 | strcpy(k->name, word.c_str()); 102 | k->type = Q_DROP_DB; 103 | q_ = k; 104 | std::cout << "drop db\n"; 105 | std::cout << k->name << std::endl; 106 | } 107 | if(secondWord == "TABLE"){ 108 | std::string word = getWord(input); 109 | SingleStringQuery* k = new SingleStringQuery(); 110 | strcpy(k->name, word.c_str()); 111 | k->type = Q_DROP_TABLE; 112 | q_ = k; 113 | std::cout << "drop table\n"; 114 | std::cout << k->name << std::endl; 115 | } 116 | } 117 | if(firstWord == "USE"){ 118 | std::string word = getWord(input); 119 | SingleStringQuery* k = new SingleStringQuery(); 120 | strcpy(k->name, word.c_str()); 121 | k->type = Q_USE_DB; 122 | q_ = k; 123 | std::cout << "use database\n"; 124 | std::cout << k->name << std::endl; 125 | } 126 | if(firstWord == "SHOW"){ 127 | std::string secondWord = getWord(input); 128 | if(secondWord == "TABLES") { 129 | SingleStringQuery *k = new SingleStringQuery(); 130 | k->type = Q_SHOW_TABLES; 131 | q_ = k; 132 | std::cout << "show table\n"; 133 | } 134 | } 135 | if(firstWord == "DESC"){ 136 | std::string word = getWord(input); 137 | SingleStringQuery* k = new SingleStringQuery(); 138 | strcpy(k->name, word.c_str()); 139 | k->type = Q_DESC_TABLE; 140 | q_ = k; 141 | std::cout << "desc table\n"; 142 | std::cout << k->name << std::endl; 143 | } 144 | 145 | if(firstWord == "INSERT"){ 146 | std::string secondWord = getWord(input); 147 | if(secondWord == "INTO"){ 148 | std::string word = getWord(input); 149 | InsertQuery* k = new InsertQuery(); 150 | strcpy(k->relName, word.c_str()); 151 | k->type = Q_INSERT; 152 | word = getWord(input); 153 | if(word == "VALUES"){ 154 | while(input != ""){ 155 | word = getTuple(input); 156 | int rank1 = word.find("("); 157 | int rank2 = word.find(")"); 158 | word = word.substr(rank1+1, rank2-rank1-1); 159 | std::cout << word << std::endl; 160 | InsertItem ii; 161 | while(true){ 162 | int rank = findRealMark(word, ','); 163 | if(rank == -1){ 164 | if(word[0] == '\''){ 165 | word = word.substr(1, word.size() - 2); 166 | InsertAttrItem iai; 167 | iai.type = STRING; 168 | iai.sValue = word; 169 | ii.push_back(iai); 170 | } 171 | else{ 172 | InsertAttrItem iai; 173 | iai.type = INT; 174 | iai.iValue = atoi(word.c_str()); 175 | ii.push_back(iai); 176 | } 177 | break; 178 | } 179 | else{ 180 | std::string tmp = word.substr(0, rank); 181 | if(tmp[0] == '\''){ 182 | tmp = tmp.substr(1, tmp.size() - 2); 183 | InsertAttrItem iai; 184 | iai.type = STRING; 185 | iai.sValue = tmp; 186 | ii.push_back(iai); 187 | } 188 | else{ 189 | InsertAttrItem iai; 190 | iai.type = INT; 191 | iai.iValue = atoi(tmp.c_str()); 192 | ii.push_back(iai); 193 | } 194 | word = word.substr(rank+1, word.size() - rank - 1); 195 | } 196 | } 197 | k->v.push_back(ii); 198 | } 199 | } 200 | q_ = k; 201 | std::cout << "insert\n"; 202 | std::cout << k->relName << std::endl; 203 | for(int i = 0 ; i < k->v.size() ; i ++){ 204 | for(int j = 0 ; j < (k->v)[i].size(); j ++){ 205 | std::cout << (k->v)[i][j].type << std::endl; 206 | if((k->v)[i][j].type == INT) 207 | std::cout << (k->v)[i][j].iValue << std::endl; 208 | if((k->v)[i][j].type == STRING) 209 | std::cout << (k->v)[i][j].sValue << std::endl; 210 | } 211 | } 212 | } 213 | } 214 | 215 | if(firstWord == "DELETE"){ 216 | std::string secondWord = getWord(input); 217 | if(secondWord == "FROM"){ 218 | std::string word = getWord(input); 219 | DeleteQuery* k = new DeleteQuery(); 220 | k->relName = word; 221 | k->type = Q_DELETE; 222 | WhereClause wc; 223 | word = getWord(input); 224 | if(word == "WHERE"){ 225 | int rank = findRealMark(input, ' '); 226 | while(rank != -1) { 227 | word = input.substr(0, rank); 228 | input = input.substr(rank, input.size() - rank); 229 | int op; 230 | if ((op = findRealMark(word, '=')) != -1) { 231 | if (word[op - 1] == '!') { 232 | std::string first = word.substr(0, op - 1); 233 | std::string second = word.substr(op + 1, word.size() - op - 1); 234 | WhereItem wi; 235 | wi.op = NE_OP; 236 | wi.op1 = first; 237 | if (findRealMark(second, '.') != -1) { 238 | wi.useOp2 = 1; 239 | wi.op2 = second; 240 | } 241 | else { 242 | if (second[0] == '\'') { 243 | wi.type = STRING; 244 | wi.sValue = second.substr(1, second.size() - 2); 245 | } 246 | else { 247 | wi.type = INT; 248 | wi.iValue = atoi(second.c_str()); 249 | } 250 | } 251 | wc.item.push_back(wi); 252 | } 253 | else { 254 | if (word[op - 1] == '<') { 255 | std::string first = word.substr(0, op - 1); 256 | std::string second = word.substr(op + 1, word.size() - op - 1); 257 | WhereItem wi; 258 | wi.op = LE_OP; 259 | wi.op1 = first; 260 | if (findRealMark(second, '.') != -1) { 261 | wi.useOp2 = 1; 262 | wi.op2 = second; 263 | } 264 | else { 265 | if (second[0] == '\'') { 266 | wi.type = STRING; 267 | wi.sValue = second.substr(1, second.size() - 2); 268 | } 269 | else { 270 | wi.type = INT; 271 | wi.iValue = atoi(second.c_str()); 272 | } 273 | } 274 | wc.item.push_back(wi); 275 | } 276 | else { 277 | if (word[op - 1] == '>') { 278 | std::string first = word.substr(0, op - 1); 279 | std::string second = word.substr(op + 1, word.size() - op - 1); 280 | WhereItem wi; 281 | wi.op = GE_OP; 282 | wi.op1 = first; 283 | if (findRealMark(second, '.') != -1) { 284 | wi.useOp2 = 1; 285 | wi.op2 = second; 286 | } 287 | else { 288 | if (second[0] == '\'') { 289 | wi.type = STRING; 290 | wi.sValue = second.substr(1, second.size() - 2); 291 | } 292 | else { 293 | wi.type = INT; 294 | wi.iValue = atoi(second.c_str()); 295 | } 296 | } 297 | wc.item.push_back(wi); 298 | } 299 | else { 300 | std::string first = word.substr(0, op); 301 | std::string second = word.substr(op + 1, word.size() - op - 1); 302 | WhereItem wi; 303 | wi.op = EQ_OP; 304 | wi.op1 = first; 305 | if (findRealMark(second, '.') != -1) { 306 | wi.useOp2 = 1; 307 | wi.op2 = second; 308 | } 309 | else { 310 | if (second[0] == '\'') { 311 | wi.type = STRING; 312 | wi.sValue = second.substr(1, second.size() - 2); 313 | } 314 | else { 315 | wi.type = INT; 316 | wi.iValue = atoi(second.c_str()); 317 | } 318 | } 319 | wc.item.push_back(wi); 320 | } 321 | } 322 | } 323 | } 324 | else { 325 | if ((op = findRealMark(word, '>')) != -1) { 326 | std::string first = word.substr(0, op); 327 | std::string second = word.substr(op + 1, word.size() - op - 1); 328 | WhereItem wi; 329 | wi.op = GT_OP; 330 | wi.op1 = first; 331 | if (findRealMark(second, '.') != -1) { 332 | wi.useOp2 = 1; 333 | wi.op2 = second; 334 | } 335 | else { 336 | if (second[0] == '\'') { 337 | wi.type = STRING; 338 | wi.sValue = second.substr(1, second.size() - 2); 339 | } 340 | else { 341 | wi.type = INT; 342 | wi.iValue = atoi(second.c_str()); 343 | } 344 | } 345 | wc.item.push_back(wi); 346 | } 347 | else { 348 | if ((op = findRealMark(word, '<')) != -1) { 349 | std::string first = word.substr(0, op); 350 | std::string second = word.substr(op + 1, word.size() - op - 1); 351 | WhereItem wi; 352 | wi.op = LT_OP; 353 | wi.op1 = first; 354 | if (findRealMark(second, '.') != -1) { 355 | wi.useOp2 = 1; 356 | wi.op2 = second; 357 | } 358 | else { 359 | if (second[0] == '\'') { 360 | wi.type = STRING; 361 | wi.sValue = second.substr(1, second.size() - 2); 362 | } 363 | else { 364 | wi.type = INT; 365 | wi.iValue = atoi(second.c_str()); 366 | } 367 | } 368 | wc.item.push_back(wi); 369 | } 370 | } 371 | } 372 | word = getWord(input); 373 | if(word == "AND") 374 | wc.op.push_back(AND_OP); 375 | if(word == "OR") 376 | wc.op.push_back(OR_OP); 377 | rank = findRealMark(input, ' '); 378 | } 379 | int op; 380 | word = input; 381 | if ((op = findRealMark(word, '=')) != -1) { 382 | if (word[op - 1] == '!') { 383 | std::string first = word.substr(0, op - 1); 384 | std::string second = word.substr(op + 1, word.size() - op - 1); 385 | WhereItem wi; 386 | wi.op = NE_OP; 387 | wi.op1 = first; 388 | if (findRealMark(second, '.') != -1) { 389 | wi.useOp2 = 1; 390 | wi.op2 = second; 391 | } 392 | else { 393 | if (second[0] == '\'') { 394 | wi.type = STRING; 395 | wi.sValue = second.substr(1, second.size() - 2); 396 | } 397 | else { 398 | wi.type = INT; 399 | wi.iValue = atoi(second.c_str()); 400 | } 401 | } 402 | wc.item.push_back(wi); 403 | } 404 | else { 405 | if (word[op - 1] == '<') { 406 | std::string first = word.substr(0, op - 1); 407 | std::string second = word.substr(op + 1, word.size() - op - 1); 408 | WhereItem wi; 409 | wi.op = LE_OP; 410 | wi.op1 = first; 411 | if (findRealMark(second, '.') != -1) { 412 | wi.useOp2 = 1; 413 | wi.op2 = second; 414 | } 415 | else { 416 | if (second[0] == '\'') { 417 | wi.type = STRING; 418 | wi.sValue = second.substr(1, second.size() - 2); 419 | } 420 | else { 421 | wi.type = INT; 422 | wi.iValue = atoi(second.c_str()); 423 | } 424 | } 425 | wc.item.push_back(wi); 426 | } 427 | else { 428 | if (word[op - 1] == '>') { 429 | std::string first = word.substr(0, op - 1); 430 | std::string second = word.substr(op + 1, word.size() - op - 1); 431 | WhereItem wi; 432 | wi.op = GE_OP; 433 | wi.op1 = first; 434 | if (findRealMark(second, '.') != -1) { 435 | wi.useOp2 = 1; 436 | wi.op2 = second; 437 | } 438 | else { 439 | if (second[0] == '\'') { 440 | wi.type = STRING; 441 | wi.sValue = second.substr(1, second.size() - 2); 442 | } 443 | else { 444 | wi.type = INT; 445 | wi.iValue = atoi(second.c_str()); 446 | } 447 | } 448 | wc.item.push_back(wi); 449 | } 450 | else { 451 | std::string first = word.substr(0, op); 452 | std::string second = word.substr(op + 1, word.size() - op - 1); 453 | WhereItem wi; 454 | wi.op = EQ_OP; 455 | wi.op1 = first; 456 | if (findRealMark(second, '.') != -1) { 457 | wi.useOp2 = 1; 458 | wi.op2 = second; 459 | } 460 | else { 461 | if (second[0] == '\'') { 462 | wi.type = STRING; 463 | wi.sValue = second.substr(1, second.size() - 2); 464 | } 465 | else { 466 | wi.type = INT; 467 | wi.iValue = atoi(second.c_str()); 468 | } 469 | } 470 | wc.item.push_back(wi); 471 | } 472 | } 473 | } 474 | } 475 | else { 476 | if ((op = findRealMark(word, '>')) != -1) { 477 | std::string first = word.substr(0, op); 478 | std::string second = word.substr(op + 1, word.size() - op - 1); 479 | WhereItem wi; 480 | wi.op = GT_OP; 481 | wi.op1 = first; 482 | if (findRealMark(second, '.') != -1) { 483 | wi.useOp2 = 1; 484 | wi.op2 = second; 485 | } 486 | else { 487 | if (second[0] == '\'') { 488 | wi.type = STRING; 489 | wi.sValue = second.substr(1, second.size() - 2); 490 | } 491 | else { 492 | wi.type = INT; 493 | wi.iValue = atoi(second.c_str()); 494 | } 495 | } 496 | wc.item.push_back(wi); 497 | } 498 | else { 499 | if ((op = findRealMark(word, '<')) != -1) { 500 | std::string first = word.substr(0, op); 501 | std::string second = word.substr(op + 1, word.size() - op - 1); 502 | WhereItem wi; 503 | wi.op = LT_OP; 504 | wi.op1 = first; 505 | if (findRealMark(second, '.') != -1) { 506 | wi.useOp2 = 1; 507 | wi.op2 = second; 508 | } 509 | else { 510 | if (second[0] == '\'') { 511 | wi.type = STRING; 512 | wi.sValue = second.substr(1, second.size() - 2); 513 | } 514 | else { 515 | wi.type = INT; 516 | wi.iValue = atoi(second.c_str()); 517 | } 518 | } 519 | wc.item.push_back(wi); 520 | } 521 | } 522 | } 523 | k->where = wc; 524 | } 525 | else 526 | { 527 | } 528 | q_ = k; 529 | std::cout << "delete" << std::endl; 530 | std::cout << k->relName << std::endl; 531 | } 532 | } 533 | 534 | return q_; 535 | } 536 | 537 | std::string Parser::getWord(std::string& input) { 538 | while(input[0] == ' '){ 539 | input.erase(0, 1); 540 | } 541 | int rank = input.find(" "); 542 | if(rank != -1) { 543 | std::string word = input.substr(0, rank); 544 | input = input.substr(rank + 1, input.size() - rank - 1); 545 | return word; 546 | } 547 | else{ 548 | return input; 549 | } 550 | } 551 | 552 | std::string Parser::getTuple(std::string &input) { 553 | while(input[0] == ' '){ 554 | input.erase(0, 1); 555 | } 556 | int rank = input.find("), ("); 557 | 558 | if(rank != -1) { 559 | std::string word = input.substr(0, rank); 560 | input = input.substr(rank + 2, input.size() - rank - 2); 561 | return word; 562 | } 563 | else{ 564 | std::string res = input; 565 | input = ""; 566 | return res; 567 | } 568 | } 569 | 570 | int Parser::findRealMark(std::string &input, char c) { 571 | while(input[0] == ' '){ 572 | input.erase(0, 1); 573 | } 574 | bool isReal = true; 575 | for(int i = 0 ; i < input.size() ; i ++){ 576 | if(input[i] == c){ 577 | if(isReal) 578 | return i; 579 | } 580 | if(input[i] == '\''){ 581 | isReal = !isReal; 582 | } 583 | } 584 | return -1; 585 | } 586 | 587 | void Parser::getAttr(std::string input, int& type, int& len) { 588 | // int 0 refer to int, while 1 refer to varchar 589 | int rank1 = input.find("("); 590 | int rank2 = input.find(")"); 591 | std::string t = input.substr(0, rank1); 592 | if(t == "int") 593 | type = 0; 594 | if(t == "varchar") 595 | type = 1; 596 | std::string l = input.substr(rank1+1, rank2-rank1-1); 597 | len = atoi(l.c_str()); 598 | } 599 | } 600 | } 601 | --------------------------------------------------------------------------------