├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include └── DataStructure.h ├── lib └── BPTree │ └── BPTree.h ├── main.cpp └── src ├── API ├── Api.cpp ├── Api.h ├── ApiHelper.cpp └── ApiHelper.h ├── BufferManager ├── BufferManager.cpp └── BufferManager.h ├── CatalogManager ├── CatalogManager.cpp ├── CatalogManager.h └── convention.md ├── IndexManager ├── IndexManager.cpp └── IndexManager.h ├── Interpreter ├── Interpreter.cpp ├── Interpreter.h ├── Makefile ├── QueryRequest.cpp ├── QueryRequest.h ├── operator.h ├── parse.cpp ├── parse.y ├── parser_public.h ├── scan.cpp ├── scan.l ├── scanhelp.cpp ├── test.sql └── y.tab.h └── RecordManager ├── RecordManager.cpp └── RecordManager.h /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Clion 2 | */cmake-build-* 3 | */.idea/* 4 | cmake-build-* 5 | .idea/* 6 | 7 | 8 | # ---> C++ 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | *.lib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | 38 | # ---> C 39 | # Object files 40 | *.o 41 | *.ko 42 | *.obj 43 | *.elf 44 | 45 | # Precompiled Headers 46 | *.gch 47 | *.pch 48 | 49 | # Libraries 50 | *.lib 51 | *.a 52 | *.la 53 | *.lo 54 | 55 | # Shared objects (inc. Windows DLLs) 56 | *.dll 57 | *.so 58 | *.so.* 59 | *.dylib 60 | 61 | # Executables 62 | *.exe 63 | *.out 64 | *.app 65 | *.i*86 66 | *.x86_64 67 | *.hex 68 | 69 | # Debug files 70 | *.dSYM/ 71 | 72 | # vim temp file 73 | *.un~ 74 | *~ 75 | *.swp 76 | 77 | # generated file 78 | #scan.c 79 | #parse.c 80 | #y.tab.h 81 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | project(MiniSQL) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -lreadline) 7 | 8 | set(SOURCE_FILES main.cpp 9 | src/RecordManager/RecordManager.cpp 10 | src/RecordManager/RecordManager.h 11 | src/IndexManager/IndexManager.cpp 12 | src/IndexManager/IndexManager.h 13 | lib/BPTree/BPTree.h 14 | src/API/Api.cpp 15 | src/API/Api.h 16 | src/Interpreter/Interpreter.cpp 17 | src/Interpreter/Interpreter.h 18 | src/Interpreter/QueryRequest.cpp 19 | src/Interpreter/QueryRequest.h 20 | src/Interpreter/scan.cpp 21 | src/Interpreter/parse.cpp 22 | src/Interpreter/parser_public.h 23 | src/BufferManager/BufferManager.cpp 24 | include/DataStructure.h 25 | src/API/ApiHelper.cpp 26 | src/CatalogManager/CatalogManager.cpp 27 | ) 28 | 29 | add_executable(MiniSQL ${SOURCE_FILES}) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniSQL 2 | 3 | the MiniSQL course lab for Database System course in Zhejiang University. 4 | 5 | ## Architecture 6 | 7 | ### Modules 8 | 9 | - Buffer Manager 10 | - Catalog Manager 11 | - Index Manager 12 | - Record Manager 13 | - Interpreter 14 | - API 15 | 16 | BM, IM and RM designed and implemented by VEXIO, while other parts by yzyDavid. 17 | 18 | Index is implemented as memory B+ Tree, Interpreter using tokenizer and parser design, by flex and bison toolchains. -------------------------------------------------------------------------------- /include/DataStructure.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Haotian on 17/6/10. 3 | // 4 | 5 | #ifndef MINISQL_DATASTRUCTURE_H 6 | #define MINISQL_DATASTRUCTURE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "../src/Interpreter/operator.h" 14 | 15 | // define basic types we support 16 | #define MINISQL_TYPE_INT 0 17 | #define MINISQL_TYPE_CHAR 1 18 | #define MINISQL_TYPE_DATES 2 19 | #define MINISQL_TYPE_FLOAT 3 20 | #define MINISQL_TYPE_NULL 4 21 | 22 | // define basic cond we support 23 | 24 | #define MINISQL_COND_EQUAL 0 25 | #define MINISQL_COND_UEQUAL 1 26 | #define MINISQL_COND_LEQUAL 2 27 | #define MINISQL_COND_GEQUAL 3 28 | #define MINISQL_COND_LESS 4 29 | #define MINISQL_COND_MORE 5 30 | 31 | #define INT_LENGTH 4 32 | #define FLOAT_LENGTH 4 33 | 34 | namespace MINISQL_BASE { 35 | const int BlockSize = 4096; 36 | const int MaxBlocks = 128; 37 | const char UnUsed = 0; 38 | const char Used = 1; 39 | 40 | inline std::string dbFile(const std::string &db) { return db + ".db"; } 41 | 42 | inline std::string tableFile(const std::string &table) { return table + ".tb"; } 43 | 44 | inline std::string indexFile(const std::string &table, const std::string &index) { 45 | return table + "_" + index + ".ind"; 46 | } 47 | 48 | enum class SqlValueTypeBase { 49 | Integer, 50 | String, 51 | Float 52 | }; 53 | 54 | struct SqlValueType { 55 | std::string attrName; 56 | SqlValueTypeBase type; 57 | 58 | ///following fileds only for attribute type, not a single sql value type. 59 | bool primary = false; 60 | size_t charSize; // charSize does not include the terminating zero of string! 61 | bool unique = false; 62 | 63 | inline int M() const { 64 | switch (type) { 65 | case SqlValueTypeBase::Integer: 66 | return MINISQL_TYPE_INT; 67 | case SqlValueTypeBase::Float: 68 | return MINISQL_TYPE_FLOAT; 69 | case SqlValueTypeBase::String: 70 | return MINISQL_TYPE_CHAR; 71 | } 72 | } 73 | 74 | inline size_t getSize() const { 75 | switch (M()) { 76 | case MINISQL_TYPE_INT: 77 | return sizeof(int); 78 | case MINISQL_TYPE_FLOAT: 79 | return sizeof(float); 80 | case MINISQL_TYPE_CHAR: 81 | return charSize + 1; 82 | } 83 | } 84 | 85 | inline int getDegree() const { 86 | size_t keySize = getSize(); 87 | int degree = BlockSize / (keySize + sizeof(int)); 88 | 89 | return degree; 90 | } 91 | }; 92 | 93 | struct SqlValue { 94 | SqlValueType type; 95 | int i; 96 | float r; 97 | std::string str; 98 | 99 | inline size_t M() const { 100 | switch (type.type) { 101 | case SqlValueTypeBase::Integer: 102 | return MINISQL_TYPE_INT; 103 | case SqlValueTypeBase::Float: 104 | return MINISQL_TYPE_FLOAT; 105 | case SqlValueTypeBase::String: 106 | return MINISQL_TYPE_CHAR; 107 | } 108 | } 109 | 110 | bool operator<(const SqlValue &e) const { 111 | switch (M()) { 112 | case MINISQL_TYPE_INT: 113 | return i < e.i; 114 | case MINISQL_TYPE_FLOAT: 115 | return r < e.r; 116 | case MINISQL_TYPE_CHAR: 117 | return str < e.str; 118 | default: 119 | throw std::runtime_error("Undefined Type!"); 120 | } 121 | } 122 | 123 | bool operator==(const SqlValue &e) const { 124 | switch (M()) { 125 | case MINISQL_TYPE_INT: 126 | return i == e.i; 127 | case MINISQL_TYPE_FLOAT: 128 | return r == e.r; 129 | case MINISQL_TYPE_CHAR: 130 | return str == e.str; 131 | default: 132 | throw std::runtime_error("Undefined Type!"); 133 | } 134 | } 135 | 136 | bool operator!=(const SqlValue &e) const { return !operator==(e); } 137 | 138 | bool operator>(const SqlValue &e) const { return !operator<(e) && operator!=(e); } 139 | 140 | bool operator<=(const SqlValue &e) const { return operator<(e) || operator==(e); } 141 | 142 | bool operator>=(const SqlValue &e) const { return !operator<(e); } 143 | 144 | void reset() { 145 | str.clear(); 146 | i = 0; 147 | r = 0; 148 | } 149 | 150 | std::string toStr() const { 151 | switch (M()) { 152 | case MINISQL_TYPE_INT: 153 | return std::to_string(i); 154 | case MINISQL_TYPE_FLOAT: 155 | return std::to_string(r); 156 | case MINISQL_TYPE_CHAR: 157 | return this->str; 158 | } 159 | } 160 | }; 161 | 162 | typedef struct SqlValue Element; 163 | 164 | struct Row { 165 | std::vector col; 166 | }; 167 | 168 | struct Result { 169 | std::vector row; 170 | }; 171 | 172 | struct Tuple { 173 | std::vector element; 174 | 175 | Row fetchRow(const std::vector &attrTable, const std::vector &attrFetch) const { 176 | Row row; 177 | bool attrFound; 178 | row.col.reserve(attrFetch.size()); 179 | for (auto fetch : attrFetch) { 180 | attrFound = false; 181 | for (int i = 0; i < attrTable.size(); i++) { 182 | if (fetch == attrTable[i]) { 183 | row.col.push_back(element[i].toStr()); 184 | attrFound = true; 185 | break; 186 | } 187 | } 188 | if (!attrFound) { 189 | std::cerr << "Undefined attr in row fetching!!" << std::endl; 190 | } 191 | } 192 | return row; 193 | } 194 | 195 | const Element &fetchElement(const std::vector &attrTable, const std::string &attrFetch) const { 196 | for (int i = 0; i < attrTable.size(); i++) { 197 | if (attrFetch == attrTable[i]) { 198 | return element[i]; 199 | } 200 | } 201 | std::cerr << "Undefined attr in element fetching from tuple!!" << std::endl; 202 | } 203 | }; 204 | 205 | struct Table { 206 | Table() {}; 207 | 208 | /* Table(const Table &T) : Name(T.Name), attrCnt(T.attrCnt), recordLength(T.recordLength), 209 | recordCnt(T.recordCnt), size(T.size), attrType(T.attrType), attrNames(T.attrNames), 210 | indexNames(T.indexNames) {};*/ 211 | 212 | std::string Name; 213 | int attrCnt, recordLength, recordCnt, size; 214 | 215 | std::vector attrType; 216 | std::vector attrNames; 217 | /// for index, first stands for attr name, second stands for index name. 218 | std::vector> index; 219 | 220 | friend std::ostream &operator<<(std::ostream &os, const Table &table) { 221 | os << "Name: " << table.Name << " attrCnt: " << table.attrCnt << " recordLength: " << table.recordLength 222 | << " recordCnt: " << table.recordCnt << " size: " << table.size 223 | << " attrNames: " << table.attrNames.size(); 224 | return os; 225 | } 226 | }; 227 | 228 | struct Condition { 229 | std::string name; 230 | Operator op; 231 | SqlValue val; 232 | }; 233 | 234 | struct Cond { 235 | Cond() = default; 236 | 237 | Cond(const std::string &attr, const Element &value, int cond) : attr(attr), value(value), cond(cond) {} 238 | 239 | Cond(const Condition &condition) 240 | : attr(condition.name), 241 | value(condition.val) { 242 | switch (condition.op) { 243 | case Operator::GE_OP: 244 | cond = MINISQL_COND_GEQUAL; 245 | break; 246 | case Operator::LE_OP: 247 | cond = MINISQL_COND_LEQUAL; 248 | break; 249 | case Operator::GT_OP: 250 | cond = MINISQL_COND_MORE; 251 | break; 252 | case Operator::LT_OP: 253 | cond = MINISQL_COND_LESS; 254 | break; 255 | case Operator::EQ_OP: 256 | cond = MINISQL_COND_EQUAL; 257 | break; 258 | case Operator::NE_OP: 259 | cond = MINISQL_COND_UEQUAL; 260 | break; 261 | } 262 | } 263 | 264 | int cond; 265 | std::string attr; 266 | Element value; 267 | 268 | bool test(const Element &e) const { 269 | switch (cond) { 270 | case MINISQL_COND_EQUAL: 271 | return e == value; 272 | case MINISQL_COND_UEQUAL: 273 | return e != value; 274 | case MINISQL_COND_LEQUAL: 275 | return e <= value; 276 | case MINISQL_COND_GEQUAL: 277 | return e >= value; 278 | case MINISQL_COND_LESS: 279 | return e < value; 280 | case MINISQL_COND_MORE: 281 | return e > value; 282 | default: 283 | std::cerr << "Undefined condition width cond " << cond << "!" << std::endl; 284 | } 285 | } 286 | }; 287 | 288 | struct IndexHint { 289 | Cond cond; 290 | std::string attrName; 291 | int attrType; 292 | }; 293 | 294 | } 295 | #endif //MINISQL_DATASTRUCTURE_H 296 | -------------------------------------------------------------------------------- /lib/BPTree/BPTree.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Haotian on 17/6/5. 3 | // 4 | 5 | #ifndef MINISQL_BPTREE_H 6 | #define MINISQL_BPTREE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "../../include/DataStructure.h" 12 | 13 | using namespace std; 14 | 15 | template 16 | class BPTreeNode { 17 | public: 18 | BPTreeNode() = default; 19 | 20 | BPTreeNode(int degree, bool isLeaf); 21 | 22 | ~BPTreeNode() {} 23 | 24 | bool search(const T &key, int &index) const; 25 | 26 | BPTreeNode *split(T &key); 27 | 28 | int add(const T &key); 29 | 30 | int add(const T &key, int offset); 31 | 32 | void removeAt(int index); 33 | 34 | bool isRoot() const { return parent == nullptr; } 35 | 36 | bool isLeaf; 37 | int degree, cnt; 38 | BPTreeNode *parent, *sibling; 39 | vector keys; 40 | vector keyOffset; 41 | vector *> children; 42 | 43 | void debug(int id) { 44 | cout << "Keys [" << id << "]: "; 45 | for (int i = 0; i < this->cnt; i++) { 46 | cout << keys[i] << " "; 47 | } 48 | cout << endl; 49 | } 50 | 51 | private: 52 | bool binarySearch(const T &key, int &index) const; 53 | }; 54 | 55 | template 56 | BPTreeNode::BPTreeNode(int degree, bool isLeaf) : degree(degree), isLeaf(isLeaf), cnt(0), parent(nullptr), 57 | sibling(nullptr) { 58 | children.resize(degree + 1); 59 | keys.resize(degree); 60 | keyOffset.resize(degree); 61 | } 62 | 63 | template 64 | bool BPTreeNode::search(const T &key, int &index) const { 65 | if (cnt == 0) { 66 | index = 0; 67 | return false; 68 | } 69 | if (keys[0] > key) { 70 | index = 0; 71 | return false; 72 | } 73 | if (keys[cnt - 1] < key) { 74 | index = cnt; 75 | return false; 76 | } 77 | return binarySearch(key, index); 78 | } 79 | 80 | template 81 | bool BPTreeNode::binarySearch(const T &key, int &index) const { 82 | int left = 0, right = cnt - 1, pos; 83 | while (left <= right) { 84 | pos = left + (right - left) / 2; 85 | if (keys[pos] < key) { 86 | left = pos + 1; 87 | } else { 88 | right = pos - 1; 89 | } 90 | } 91 | index = left; 92 | return keys[index] == key; 93 | } 94 | 95 | template 96 | BPTreeNode *BPTreeNode::split(T &key) { 97 | BPTreeNode *newNode = new BPTreeNode(degree, isLeaf); 98 | int minimal = (degree - 1) / 2; 99 | 100 | if (isLeaf) { 101 | key = keys[minimal + 1]; 102 | for (int i = minimal + 1; i < degree; i++) { 103 | newNode->keys[i - minimal - 1] = keys[i]; 104 | newNode->keyOffset[i - minimal - 1] = keyOffset[i]; 105 | } 106 | newNode->sibling = this->sibling; 107 | this->sibling = newNode; 108 | this->cnt = minimal + 1; 109 | } else { 110 | key = keys[minimal]; 111 | for (int i = minimal + 1; i <= degree; i++) { 112 | newNode->children[i - minimal - 1] = this->children[i]; 113 | this->children[i]->parent = newNode; 114 | this->children[i] = nullptr; 115 | } 116 | for (int i = minimal + 1; i < degree; i++) { 117 | newNode->keys[i - minimal - 1] = keys[i]; 118 | } 119 | this->cnt = minimal; 120 | } 121 | newNode->parent = this->parent; 122 | newNode->cnt = degree - minimal - 1; 123 | return newNode; 124 | } 125 | 126 | template 127 | int BPTreeNode::add(const T &key) { 128 | int index; 129 | bool keyExists = search(key, index); 130 | if (keyExists) { 131 | cerr << "Key is not unique!" << endl; 132 | exit(10); 133 | } 134 | for (int i = cnt; i > index; i--) { 135 | keys[i] = keys[i - 1]; 136 | children[i + 1] = children[i]; 137 | } 138 | keys[index] = key; 139 | children[index + 1] = nullptr; 140 | cnt++; 141 | return index; 142 | } 143 | 144 | template 145 | int BPTreeNode::add(const T &key, int offset) { 146 | int index; 147 | bool keyExists = search(key, index); 148 | if (keyExists) { 149 | cerr << "Key is not unique!" << endl; 150 | exit(10); 151 | } 152 | for (int i = cnt; i > index; i--) { 153 | keys[i] = keys[i - 1]; 154 | keyOffset[i] = keyOffset[i - 1]; 155 | } 156 | keys[index] = key; 157 | keyOffset[index] = offset; 158 | cnt++; 159 | return index; 160 | } 161 | 162 | template 163 | void BPTreeNode::removeAt(int index) { 164 | for (int i = index; i < cnt - 1; i++) { 165 | keys[i] = keys[i + 1]; 166 | } 167 | if (isLeaf) { 168 | for (int i = index; i < cnt - 1; i++) { 169 | keyOffset[i] = keyOffset[i + 1]; 170 | } 171 | keyOffset[cnt - 1] = 0; 172 | keys[cnt - 1] = T(); 173 | } else { 174 | for (int i = index + 1; i < cnt; i++) { 175 | children[i] = children[i + 1]; 176 | } 177 | keys[cnt - 1] = T(); 178 | children[cnt] = nullptr; 179 | } 180 | cnt--; 181 | } 182 | 183 | template 184 | struct NodeSearchParse { 185 | int index; 186 | BPTreeNode *node; 187 | }; 188 | 189 | template 190 | class BPTree { 191 | public: 192 | typedef BPTreeNode *TreeNode; 193 | 194 | BPTree(string fileName, int sizeofKey, int degree); 195 | 196 | ~BPTree(); 197 | 198 | TreeNode getHeadNode() const { return head; } 199 | 200 | int find(const T &key); 201 | 202 | NodeSearchParse findNode(const T &key); 203 | 204 | bool insert(const T &key, int offset); 205 | 206 | bool remove(const T &key); 207 | 208 | private: 209 | string fileName; 210 | TreeNode root, head; 211 | int sizeofKey, level, keyCount, nodeCount, degree; 212 | 213 | void initBPTree(); 214 | 215 | bool findKeyFromNode(TreeNode node, const T &key, NodeSearchParse &res); 216 | 217 | void cascadeInsert(TreeNode node); 218 | 219 | bool cascadeDelete(TreeNode node); 220 | 221 | bool deleteBranchLL(TreeNode node, TreeNode parent, TreeNode sibling, int index); 222 | 223 | bool deleteBranchLR(TreeNode node, TreeNode parent, TreeNode sibling, int index); 224 | 225 | bool deleteBranchRL(TreeNode node, TreeNode parent, TreeNode sibling, int index); 226 | 227 | bool deleteBranchRR(TreeNode node, TreeNode parent, TreeNode sibling, int index); 228 | 229 | bool deleteLeafLL(TreeNode node, TreeNode parent, TreeNode sibling, int index); 230 | 231 | bool deleteLeafLR(TreeNode node, TreeNode parent, TreeNode sibling, int index); 232 | 233 | bool deleteLeafRL(TreeNode node, TreeNode parent, TreeNode sibling, int index); 234 | 235 | bool deleteLeafRR(TreeNode node, TreeNode parent, TreeNode sibling, int index); 236 | 237 | void debug(TreeNode node, int id) { 238 | node->debug(id); 239 | if (!node->isLeaf) { 240 | for (int i = 0; i <= node->cnt; i++) { 241 | debug(node->children[i], i); 242 | } 243 | } 244 | } 245 | }; 246 | 247 | template 248 | BPTree::BPTree(string fileName, int sizeofKey, int degree) : fileName(fileName), sizeofKey(sizeofKey), 249 | degree(degree), keyCount(0), nodeCount(0), level(0), 250 | root( 251 | nullptr), head(nullptr) { 252 | initBPTree(); 253 | } 254 | 255 | template 256 | BPTree::~BPTree() { 257 | 258 | } 259 | 260 | template 261 | void BPTree::initBPTree() { 262 | root = new BPTreeNode(degree, true); 263 | keyCount = 0; 264 | level = 1; 265 | nodeCount = 1; 266 | head = root; 267 | } 268 | 269 | template 270 | bool BPTree::findKeyFromNode(TreeNode node, const T &key, NodeSearchParse &res) { 271 | int index; 272 | if (node->search(key, index)) { 273 | if (node->isLeaf) { 274 | res.index = index; 275 | } else { 276 | node = node->children[index + 1]; 277 | while (!node->isLeaf) { node = node->children[0]; } 278 | res.index = 0; 279 | } 280 | res.node = node; 281 | return true; 282 | } else { 283 | if (node->isLeaf) { 284 | res.node = node; 285 | res.index = index; 286 | return false; 287 | } else { 288 | return findKeyFromNode(node->children[index], key, res); 289 | } 290 | } 291 | } 292 | 293 | template 294 | int BPTree::find(const T &key) { 295 | NodeSearchParse res; 296 | if (!root) { return -1; } 297 | if (findKeyFromNode(root, key, res)) { return res.node->keyOffset[res.index]; } 298 | else { return -1; } 299 | } 300 | 301 | template 302 | NodeSearchParse BPTree::findNode(const T &key) { 303 | NodeSearchParse res; 304 | if (!root) { return res; } 305 | if (findKeyFromNode(root, key, res)) { return res; } 306 | else { return res; } 307 | } 308 | 309 | template 310 | bool BPTree::insert(const T &key, int offset) { 311 | NodeSearchParse res; 312 | if (!root) { initBPTree(); } 313 | if (findKeyFromNode(root, key, res)) { 314 | cerr << "Insert duplicate key!" << endl; 315 | return false; 316 | } 317 | res.node->add(key, offset); 318 | if (res.node->cnt == degree) { 319 | cascadeInsert(res.node); 320 | } 321 | keyCount++; 322 | return true; 323 | } 324 | 325 | template 326 | void BPTree::cascadeInsert(BPTree::TreeNode node) { 327 | T key; 328 | TreeNode sibling = node->split(key); 329 | nodeCount++; 330 | 331 | if (node->isRoot()) { 332 | TreeNode root = new BPTreeNode(degree, false); 333 | level++; 334 | nodeCount++; 335 | this->root = root; 336 | node->parent = root; 337 | sibling->parent = root; 338 | root->add(key); 339 | root->children[0] = node; 340 | root->children[1] = sibling; 341 | } else { 342 | TreeNode parent = node->parent; 343 | int index = parent->add(key); 344 | 345 | parent->children[index + 1] = sibling; 346 | sibling->parent = parent; 347 | if (parent->cnt == degree) { 348 | cascadeInsert(parent); 349 | } 350 | } 351 | } 352 | 353 | template 354 | bool BPTree::remove(const T &key) { 355 | NodeSearchParse res; 356 | if (!root) { 357 | cerr << "Dequeuing empty BPTree!" << endl; 358 | return false; 359 | } 360 | if (!findKeyFromNode(root, key, res)) { 361 | cerr << "Key not found!" << endl; 362 | return false; 363 | } 364 | if (res.node->isRoot()) { 365 | res.node->removeAt(res.index); 366 | keyCount--; 367 | return cascadeDelete(res.node); 368 | } else { 369 | if (res.index == 0 && head != res.node) { 370 | // cascadingly update parent node 371 | int index; 372 | TreeNode currentParent = res.node->parent; 373 | bool keyFound = currentParent->search(key, index); 374 | while (!keyFound) { 375 | if (!currentParent->parent) { break; } 376 | currentParent = currentParent->parent; 377 | keyFound = currentParent->search(key, index); 378 | } 379 | currentParent->keys[index] = res.node->keys[1]; 380 | res.node->removeAt(res.index); 381 | keyCount--; 382 | return cascadeDelete(res.node); 383 | } else { 384 | res.node->removeAt(res.index); 385 | keyCount--; 386 | return cascadeDelete(res.node); 387 | } 388 | } 389 | } 390 | 391 | template 392 | bool BPTree::cascadeDelete(BPTree::TreeNode node) { 393 | int minimal = degree / 2, minimalBranch = (degree - 1) / 2; 394 | if ((node->isLeaf && node->cnt >= minimal) // leaf node 395 | || (node->isRoot() && node->cnt) // root node 396 | || (!node->isLeaf && !node->isRoot() && node->cnt >= minimal) // branch node 397 | ) { 398 | return true; // no need to update 399 | } 400 | 401 | if (node->isRoot()) { 402 | if (root->isLeaf) { 403 | // tree completely removed 404 | root = nullptr; 405 | head = nullptr; 406 | } else { 407 | // reduce level by one 408 | root = node->children[0]; 409 | root->parent = nullptr; 410 | } 411 | delete node; 412 | nodeCount--; 413 | level--; 414 | return true; 415 | } 416 | 417 | 418 | TreeNode currentParent = node->parent, sibling; 419 | int index; 420 | 421 | if (node->isLeaf) { 422 | // merge if it is leaf node 423 | currentParent->search(node->keys[0], index); 424 | if (currentParent->children[0] != node && currentParent->cnt == index + 1) { 425 | // rightest, also not first, merge with left sibling 426 | sibling = currentParent->children[index]; 427 | if (sibling->cnt > minimal) { 428 | // transfer rightest of left to the leftest to meet the requirement 429 | return deleteLeafLL(node, currentParent, sibling, index); 430 | } else { 431 | // have to merge and cascadingly merge 432 | return deleteLeafLR(node, currentParent, sibling, index); 433 | } 434 | } else { 435 | // can merge with right brother 436 | if (currentParent->children[0] == node) { 437 | // on the leftest 438 | sibling = currentParent->children[1]; 439 | } else { 440 | // normally 441 | sibling = currentParent->children[index + 2]; 442 | } 443 | if (sibling->cnt > minimal) { 444 | // add the leftest of sibling to the right 445 | return deleteLeafRL(node, currentParent, sibling, index); 446 | } else { 447 | // merge and cascadingly delete 448 | return deleteLeafRR(node, currentParent, sibling, index); 449 | } 450 | } 451 | } else { 452 | // merge if it is branch node 453 | currentParent->search(node->children[0]->keys[0], index); 454 | if (currentParent->children[0] != node && currentParent->cnt == index + 1) { 455 | // can only be updated with left sibling 456 | sibling = currentParent->children[index]; 457 | if (sibling->cnt > minimalBranch) { 458 | // add rightest key to the first node to avoid cascade operation 459 | return deleteBranchLL(node, currentParent, sibling, index); 460 | } else { 461 | // delete this and merge 462 | return deleteBranchLR(node, currentParent, sibling, index); 463 | } 464 | } else { 465 | // update with right sibling 466 | if (currentParent->children[0] == node) { 467 | sibling = currentParent->children[1]; 468 | } else { 469 | sibling = currentParent->children[index + 2]; 470 | } 471 | 472 | if (sibling->cnt > minimalBranch) { 473 | // add first key of sibling to the right 474 | return deleteBranchRL(node, currentParent, sibling, index); 475 | } else { 476 | // merge the sibling to current node 477 | return deleteBranchRR(node, currentParent, sibling, index); 478 | } 479 | } 480 | } 481 | } 482 | 483 | template 484 | bool BPTree::deleteBranchLL(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 485 | node->children[node->cnt + 1] = node->children[node->cnt]; 486 | for (int i = node->cnt; i > 0; i--) { 487 | node->children[i] = node->children[i - 1]; 488 | node->keys[i] = node->keys[i - 1]; 489 | } 490 | node->children[0] = sibling->children[sibling->cnt]; 491 | node->keys[0] = parent->keys[index]; 492 | parent->keys[index] = sibling->keys[sibling->cnt - 1]; 493 | node->cnt++; 494 | // !!!! fix this 495 | // if(sibling->children[sibling->cnt]) 496 | sibling->children[sibling->cnt]->parent = node; 497 | sibling->removeAt(sibling->cnt - 1); 498 | return true; 499 | } 500 | 501 | template 502 | bool BPTree::deleteBranchLR(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 503 | sibling->keys[sibling->cnt] = parent->keys[index]; // add one node 504 | parent->removeAt(index); 505 | sibling->cnt++; 506 | for (int i = 0; i < node->cnt; i++) { 507 | node->children[i]->parent = sibling; 508 | sibling->children[sibling->cnt + i] = node->children[i]; 509 | sibling->keys[sibling->cnt + i] = node->keys[i]; 510 | } 511 | // rightest children 512 | sibling->children[sibling->cnt + node->cnt] = node->children[node->cnt]; 513 | sibling->children[sibling->cnt + node->cnt]->parent = sibling; 514 | sibling->cnt += node->cnt; 515 | 516 | delete node; 517 | nodeCount--; 518 | 519 | return cascadeDelete(parent); 520 | } 521 | 522 | template 523 | bool BPTree::deleteBranchRL(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 524 | sibling->children[0]->parent = node; 525 | node->children[node->cnt + 1] = sibling->children[0]; 526 | node->keys[node->cnt] = sibling->children[0]->keys[0]; 527 | node->cnt++; 528 | 529 | if (node == parent->children[0]) { 530 | parent->keys[0] = sibling->keys[0]; 531 | } else { 532 | parent->keys[index + 1] = sibling->keys[0]; 533 | } 534 | 535 | sibling->children[0] = sibling->children[1]; 536 | sibling->removeAt(0); 537 | return true; 538 | } 539 | 540 | template 541 | bool BPTree::deleteBranchRR(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 542 | 543 | node->keys[node->cnt] = parent->keys[index]; 544 | if (node == parent->children[0]) { 545 | parent->removeAt(0); 546 | } else { 547 | parent->removeAt(index + 1); 548 | } 549 | node->cnt++; 550 | for (int i = 0; i < sibling->cnt; i++) { 551 | sibling->children[i]->parent = node; 552 | node->children[node->cnt + i] = sibling->children[i]; 553 | node->keys[node->cnt + i] = sibling->keys[i]; 554 | } 555 | // rightest child 556 | sibling->children[sibling->cnt]->parent = node; 557 | node->children[node->cnt + sibling->cnt] = sibling->children[sibling->cnt]; 558 | node->cnt += sibling->cnt; 559 | 560 | delete sibling; 561 | nodeCount--; 562 | 563 | return cascadeDelete(parent); 564 | } 565 | 566 | template 567 | bool BPTree::deleteLeafLL(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 568 | for (int i = node->cnt; i > 0; i--) { 569 | node->keys[i] = node->keys[i - 1]; 570 | node->keyOffset[i] = node->keyOffset[i - 1]; 571 | } 572 | node->keys[0] = sibling->keys[sibling->cnt - 1]; 573 | node->keyOffset[0] = sibling->keyOffset[sibling->cnt - 1]; 574 | sibling->removeAt(sibling->cnt - 1); 575 | 576 | node->cnt++; 577 | parent->keys[index] = node->keys[0]; 578 | 579 | return true; 580 | } 581 | 582 | template 583 | bool BPTree::deleteLeafLR(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 584 | parent->removeAt(index); 585 | for (int i = 0; i < node->cnt; i++) { 586 | sibling->keys[i + sibling->cnt] = node->keys[i]; 587 | sibling->keyOffset[i + sibling->cnt] = node->keyOffset[i]; 588 | } 589 | sibling->cnt += node->cnt; 590 | sibling->sibling = node->sibling; 591 | 592 | delete node; 593 | nodeCount--; 594 | 595 | return cascadeDelete(parent); 596 | } 597 | 598 | template 599 | bool BPTree::deleteLeafRL(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 600 | node->keys[node->cnt] = sibling->keys[0]; 601 | node->keyOffset[node->cnt] = sibling->keyOffset[0]; 602 | node->cnt++; 603 | sibling->removeAt(0); 604 | if (parent->children[0] == node) { 605 | parent->keys[0] = sibling->keys[0]; // if it is leftest, change key at index zero 606 | } else { 607 | parent->keys[index + 1] = sibling->keys[0]; // or next sibling should be updated 608 | } 609 | return true; 610 | } 611 | 612 | template 613 | bool BPTree::deleteLeafRR(BPTree::TreeNode node, BPTree::TreeNode parent, BPTree::TreeNode sibling, int index) { 614 | for (int i = 0; i < sibling->cnt; i++) { 615 | node->keys[node->cnt + i] = sibling->keys[i]; 616 | node->keyOffset[node->cnt + i] = sibling->keyOffset[i]; 617 | } 618 | if (node == parent->children[0]) { 619 | parent->removeAt(0); // if leftest, merge with first sibling 620 | } else { 621 | parent->removeAt(index + 1); // or merge with next 622 | } 623 | node->cnt += sibling->cnt; 624 | node->sibling = sibling->sibling; 625 | delete sibling; 626 | nodeCount--; 627 | return cascadeDelete(parent); 628 | } 629 | 630 | #endif //MINISQL_BPTREE_H 631 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "lib/BPTree/BPTree.h" 8 | using namespace std; 9 | 10 | #include "src/Interpreter/Interpreter.h" 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | std::string file_name; 15 | switch (argc) 16 | { 17 | case 1: 18 | main_repl_loop(); 19 | break; 20 | case 2: 21 | file_name = argv[1]; 22 | break; 23 | default: 24 | return 1; 25 | } 26 | return 0; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/API/Api.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/8/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../RecordManager/RecordManager.h" 10 | #include "../CatalogManager/CatalogManager.h" 11 | #include "Api.h" 12 | #include "ApiHelper.h" 13 | 14 | namespace Api 15 | { 16 | bool use_database(const std::string &db_name) 17 | { 18 | Api::is_database_assigned = true; 19 | /* Api::database_name = db_name;*/ 20 | } 21 | 22 | std::string get_db_name_prefix() 23 | { 24 | if (!is_database_assigned) 25 | { 26 | return std::string(); 27 | //throw std::runtime_error("database not assigned!"); 28 | } 29 | 30 | return std::string("db_") + database_name + "_"; 31 | } 32 | 33 | size_t insert(const std::string &table_name, std::vector &value_list) 34 | { 35 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 36 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 37 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 38 | 39 | if (!cm->TableExist(table_name)) 40 | { 41 | std::cout << "Table not found!" << std::endl; 42 | return 0; 43 | } 44 | 45 | auto &tb = cm->GetTable(table_name); 46 | if (tb.attrNames.size() != value_list.size()) 47 | { 48 | std::cout << "Number of values not fit!" << std::endl; 49 | return 0; 50 | } 51 | 52 | size_t i; 53 | for (i = 0; i < value_list.size(); ++i) 54 | { 55 | if (value_list[i].type.type != tb.attrType[i].type) 56 | { 57 | if (value_list[i].type.type == SqlValueTypeBase::Integer && 58 | tb.attrType[i].type == SqlValueTypeBase::Float) 59 | { 60 | value_list[i].type.type = SqlValueTypeBase::Float; 61 | value_list[i].r = value_list[i].i; 62 | } else 63 | { 64 | std::cout << "Type mismatch!" << std::endl; 65 | return 0; 66 | } 67 | } 68 | value_list[i].type.attrName = tb.attrNames[i]; 69 | if (value_list[i].type.type == SqlValueTypeBase::String) 70 | { 71 | if (value_list[i].str.length() > tb.attrType[i].charSize) 72 | { 73 | std::cout << "String too long!" << std::endl; 74 | return 0; 75 | } 76 | value_list[i].type.charSize = tb.attrType[i].charSize; 77 | } 78 | } 79 | 80 | // Assert unique 81 | IndexHint uniqueTest; 82 | std::vector conds; 83 | conds.emplace_back(); 84 | Cond &cond = conds[0]; 85 | cond.cond = MINISQL_COND_EQUAL; 86 | 87 | for (auto &index : tb.index) { 88 | for (auto &val : value_list) { 89 | if (val.type.attrName != index.second) { continue; } 90 | cond.value = val; 91 | cond.attr = val.type.attrName; 92 | uniqueTest.attrName = cond.attr; 93 | uniqueTest.cond = cond; 94 | uniqueTest.attrType = cond.value.type.M(); 95 | if (rm->selectRecord(tb, tb.attrNames, conds, uniqueTest, false)) { 96 | std::cerr << "Insert failed. Duplicate key!" << std::endl; 97 | return 0; 98 | } 99 | } 100 | } 101 | 102 | Tuple t; 103 | t.element = value_list; 104 | auto offset = rm->insertRecord(tb, t); 105 | 106 | for (const auto &id: tb.index) 107 | { 108 | auto it = std::find(tb.attrNames.begin(), tb.attrNames.end(), id.first); 109 | im->insert(indexFile(tb.Name, id.first), 110 | value_list[it - tb.attrNames.begin()], offset); 111 | } 112 | 113 | ++tb.recordCnt; 114 | std::cout << "Insert finished. 1 row affected." << std::endl; 115 | return 1; 116 | } 117 | 118 | bool create_table(const std::string &table_name, 119 | const std::vector> &schema_list, 120 | const std::string &primary_key_name 121 | ) 122 | { 123 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 124 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 125 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 126 | 127 | if (cm->TableExist(table_name)) 128 | { 129 | std::cerr << "Table already exists!" << std::endl; 130 | return false; 131 | } 132 | 133 | auto num_of_attr = schema_list.size(); 134 | 135 | for (auto &it: schema_list) 136 | { 137 | if (it.second.type == SqlValueTypeBase::String) 138 | { 139 | if (it.second.charSize < 1 || it.second.charSize > 255) 140 | { 141 | std::cerr << "Char count out of range" << std::endl; 142 | return false; 143 | } 144 | } 145 | } 146 | 147 | bool is_pri_index{false}; 148 | SqlValueType primary_key_type; 149 | std::string ind_name; 150 | if (!primary_key_name.empty()) 151 | { 152 | bool pri_schema_found = false; 153 | for (auto &it: schema_list) 154 | { 155 | if (it.first == primary_key_name) 156 | { 157 | pri_schema_found = true; 158 | primary_key_type = it.second; 159 | 160 | primary_key_type.unique = true; 161 | primary_key_type.charSize = it.second.getSize(); 162 | primary_key_type.attrName = it.first; 163 | ind_name = "pri_" + table_name + "_" + it.first; 164 | is_pri_index = true; 165 | } 166 | } 167 | if (!pri_schema_found) 168 | { 169 | std::cerr << "Primary key not found!" << std::endl; 170 | return false; 171 | } 172 | } 173 | auto rval = rm->createTable(table_name); 174 | cm->CreateTable(table_name, schema_list, primary_key_name); 175 | auto &tb = cm->GetTable(table_name); 176 | if (is_pri_index) 177 | { 178 | rm->createIndex(tb, primary_key_type); 179 | tb.index.push_back(std::make_pair(primary_key_name, ind_name)); 180 | } 181 | std::cout << "Create table " << table_name << " success." << std::endl; 182 | cm->Flush(); 183 | 184 | return rval; 185 | } 186 | 187 | bool create_index(const std::string &table_name, const std::string &attribute_name, const std::string &index_name, 188 | bool usercall) 189 | { 190 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 191 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 192 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 193 | 194 | if (cm->CheckIndexNameExists(index_name)) 195 | { 196 | std::cerr << "Index name exists!" << std::endl; 197 | return false; 198 | } 199 | 200 | if (!cm->TableExist(table_name)) 201 | { 202 | std::cerr << "Table not found!" << std::endl; 203 | return false; 204 | } 205 | auto &tb = cm->GetTable(table_name); 206 | 207 | for (auto &i: tb.index) 208 | { 209 | if (i.first == attribute_name) 210 | { 211 | if (i.second.find("auto_ind") == 0) 212 | { 213 | i.second = index_name; 214 | std::cout << "Create index success" << std::endl; 215 | return true; 216 | } 217 | std::cerr << "Index on the attribute exists!" << std::endl; 218 | return false; 219 | } 220 | } 221 | 222 | SqlValueType type; 223 | size_t i{0}; 224 | for (; i < tb.attrNames.size(); ++i) 225 | { 226 | if (tb.attrNames[i] == attribute_name) 227 | { 228 | if (tb.attrType[i].unique) 229 | { 230 | type = tb.attrType[i]; 231 | type.attrName = tb.attrNames[i]; 232 | break; 233 | } else 234 | { 235 | std::cout << "Not a unique attribute!!" << std::endl; 236 | return false; 237 | } 238 | } 239 | } 240 | 241 | auto b1 = rm->createIndex(tb, type); 242 | tb.index.push_back(std::make_pair(attribute_name, index_name)); 243 | cm->Flush(); 244 | if (b1) 245 | { 246 | std::cout << "Create index success" << std::endl; 247 | return true; 248 | } else 249 | { 250 | std::cerr << "Unknown failure!" << std::endl; 251 | return false; 252 | } 253 | } 254 | 255 | bool drop_table(const std::string &table_name) 256 | { 257 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 258 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 259 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 260 | 261 | if (!cm->TableExist(table_name)) 262 | { 263 | std::cerr << "Table not found!" << std::endl; 264 | return false; 265 | } 266 | 267 | auto &tb = cm->GetTable(table_name); 268 | 269 | for (auto &it: tb.index) 270 | { 271 | auto ifn = it.first; 272 | rm->dropIndex(tb, ifn); 273 | } 274 | 275 | std::cout << "Table " << table_name << " dropped." << std::endl; 276 | cm->RemoveTable(tb); 277 | cm->Flush(); 278 | return rm->dropTable(table_name); 279 | } 280 | 281 | bool drop_index(const std::string &index_name) 282 | { 283 | std::string table_name; 284 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 285 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 286 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 287 | 288 | bool e = cm->CheckIndexNameExists(index_name); 289 | if (!e) 290 | { 291 | std::cerr << "Index not found!" << std::endl; 292 | return false; 293 | } 294 | auto &tb = cm->GetTableWithIndex(index_name); 295 | for (auto &ind: tb.index) 296 | { 297 | if (ind.second == index_name) 298 | { 299 | rm->dropIndex(tb, ind.first); 300 | tb.index.erase(std::find_if(tb.index.begin(), tb.index.end(), 301 | [&index_name](const std::pair &it) 302 | { return it.second == index_name; })); 303 | std::cout << "Index " << table_name << " dropped." << std::endl; 304 | cm->Flush(); 305 | return true; 306 | } 307 | } 308 | return false; 309 | } 310 | 311 | bool select(const std::string &table_name, const std::vector &condition_list) 312 | { 313 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 314 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 315 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 316 | 317 | if (!cm->TableExist(table_name)) 318 | { 319 | std::cerr << "Table not found!" << std::endl; 320 | return false; 321 | } 322 | 323 | auto &tb = cm->GetTable(table_name); 324 | 325 | for (const auto &cond: condition_list) 326 | { 327 | auto it = std::find(tb.attrNames.begin(), tb.attrNames.end(), cond.name); 328 | if (it == tb.attrNames.end()) 329 | { 330 | std::cerr << "Attribute in conditions mismatch!" << std::endl; 331 | return false; 332 | } 333 | auto type = tb.attrType[it - tb.attrNames.begin()]; 334 | if (type.type != cond.val.type.type) 335 | { 336 | std::cerr << "Type in conditions mismatch!" << std::endl; 337 | return false; 338 | } 339 | } 340 | 341 | return select(table_name, condition_list, tb.attrNames); 342 | } 343 | 344 | bool select(const std::string &table_name, const std::vector &condition_list, 345 | const std::vector &attr_list) 346 | { 347 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 348 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 349 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 350 | 351 | if (!cm->TableExist(table_name)) 352 | { 353 | std::cerr << "Table not found!" << std::endl; 354 | return false; 355 | } 356 | 357 | auto &tb = cm->GetTable(table_name); 358 | auto cond_list = std::vector(); 359 | for (const auto &it: condition_list) 360 | { 361 | cond_list.push_back(it); 362 | } 363 | 364 | //check attr_list valid. 365 | for (auto &at: attr_list) 366 | { 367 | if (std::find(tb.attrNames.begin(), tb.attrNames.end(), at) == tb.attrNames.end()) 368 | { 369 | std::cerr << "Attribute mismatch!" << std::endl; 370 | return false; 371 | } 372 | } 373 | 374 | if (tb.index.size() == 0 || cond_list.size() == 0) 375 | { 376 | return rm->selectRecord(tb, attr_list, cond_list); 377 | } else 378 | { 379 | for (const auto &ind: tb.index) 380 | { 381 | for (const auto &cond: cond_list) 382 | { 383 | if (ind.first == cond.attr) 384 | { 385 | IndexHint hint; 386 | hint.attrName = cond.attr; 387 | hint.cond = cond; 388 | hint.attrType = cond.value.type.M(); 389 | return rm->selectRecord(tb, attr_list, cond_list, hint); 390 | } 391 | } 392 | } 393 | return rm->selectRecord(tb, attr_list, cond_list); 394 | } 395 | } 396 | 397 | bool delete_op(const std::string &table_name, const std::vector &condition_list) 398 | { 399 | auto rm = ApiHelper::getApiHelper()->getRecordManager(); 400 | auto im = ApiHelper::getApiHelper()->getIndexManager(); 401 | auto cm = ApiHelper::getApiHelper()->getCatalogManager(); 402 | 403 | if (!cm->TableExist(table_name)) 404 | { 405 | std::cerr << "Table not found!" << std::endl; 406 | return false; 407 | } 408 | 409 | auto &tb = cm->GetTable(table_name); 410 | 411 | for (const auto &cond: condition_list) 412 | { 413 | auto it = std::find(tb.attrNames.begin(), tb.attrNames.end(), cond.name); 414 | if (it == tb.attrNames.end()) 415 | { 416 | std::cerr << "Attribute in conditions mismatch!" << std::endl; 417 | return false; 418 | } 419 | auto type = tb.attrType[it - tb.attrNames.begin()]; 420 | if (type.type != cond.val.type.type) 421 | { 422 | std::cerr << "Type in conditions mismatch!" << std::endl; 423 | return false; 424 | } 425 | } 426 | 427 | auto cond_list = std::vector(); 428 | for (const auto &it: condition_list) 429 | { 430 | cond_list.push_back(it); 431 | } 432 | 433 | auto r = rm->deleteRecord(tb, cond_list); 434 | if (r) 435 | { 436 | std::cout << "Delete success" << std::endl; 437 | } else 438 | { 439 | std::cerr << "Delete failed!" << std::endl; 440 | } 441 | return r; 442 | } 443 | 444 | bool update(const std::string &table_name, const std::string &attr, const SqlValue &value, 445 | const std::vector &condition_list) 446 | { 447 | std::cerr << "Not supported." << std::endl; 448 | return false; 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /src/API/Api.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/8/17. 3 | // 4 | 5 | #ifndef MINISQL_API_H 6 | #define MINISQL_API_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../Interpreter/QueryRequest.h" 15 | 16 | namespace Api 17 | { 18 | bool use_database(const std::string &db_name); 19 | 20 | size_t insert(const std::string &table_name, std::vector &value_list); 21 | 22 | bool delete_op(const std::string &table_name, const std::vector &condition_list); 23 | 24 | bool select(const std::string &table_name, const std::vector &condition_list); 25 | 26 | bool select(const std::string &table_name, 27 | const std::vector &condition_list, 28 | const std::vector &attr_list 29 | ); 30 | 31 | bool update(const std::string &table_name, 32 | const std::string &attr, 33 | const SqlValue &value, 34 | const std::vector &condition_list 35 | ); 36 | 37 | bool create_table(const std::string &table_name, 38 | const std::vector> &schema_list, 39 | const std::string &primary_key_name = "" 40 | ); 41 | 42 | bool create_index(const std::string &table_name, const std::string &attribute_name, const std::string &index_name, bool usercall = true); 43 | 44 | bool drop_table(const std::string &table_name); 45 | 46 | bool drop_index(const std::string &index_name); 47 | 48 | std::string get_db_name_prefix(); 49 | } 50 | 51 | 52 | #endif //MINISQL_API_H 53 | -------------------------------------------------------------------------------- /src/API/ApiHelper.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/21/17. 3 | // 4 | #include 5 | 6 | #include "ApiHelper.h" 7 | 8 | namespace Api 9 | { 10 | std::string database_name; 11 | bool is_database_assigned = false; 12 | 13 | RecordManager *ApiHelper::getRecordManager() 14 | { 15 | if (rm == nullptr) 16 | { 17 | auto im = getIndexManager(); 18 | auto bm = getBufferManager(); 19 | rm = new RecordManager(bm, im); 20 | } 21 | return rm; 22 | } 23 | 24 | IndexManager *ApiHelper::getIndexManager() 25 | { 26 | return (im == nullptr) ? im = new IndexManager() : im; 27 | } 28 | 29 | BufferManager *ApiHelper::getBufferManager() 30 | { 31 | return (bm == nullptr) ? bm = new BufferManager() : bm; 32 | } 33 | 34 | CatalogManager *ApiHelper::getCatalogManager() 35 | { 36 | return (cm == nullptr) ? cm = new CatalogManager() : cm; 37 | } 38 | 39 | ApiHelper *ApiHelper::helper = nullptr; 40 | } -------------------------------------------------------------------------------- /src/API/ApiHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/21/17. 3 | // 4 | 5 | #ifndef MINISQL_APIHELPER_H 6 | #define MINISQL_APIHELPER_H 7 | 8 | #include 9 | #include "../RecordManager/RecordManager.h" 10 | #include "../IndexManager/IndexManager.h" 11 | #include "../CatalogManager/CatalogManager.h" 12 | 13 | namespace Api 14 | { 15 | extern std::string database_name; 16 | extern bool is_database_assigned; 17 | 18 | class ApiHelper 19 | { 20 | public: 21 | static ApiHelper *getApiHelper() 22 | { 23 | return (helper == nullptr) ? helper = new ApiHelper() : helper; 24 | } 25 | 26 | RecordManager *getRecordManager(); 27 | 28 | IndexManager *getIndexManager(); 29 | 30 | BufferManager *getBufferManager(); 31 | 32 | CatalogManager *getCatalogManager(); 33 | 34 | private: 35 | ApiHelper() = default; 36 | 37 | static ApiHelper *helper; 38 | 39 | RecordManager *rm = nullptr; 40 | IndexManager *im = nullptr; 41 | BufferManager *bm = nullptr; 42 | CatalogManager *cm = nullptr; 43 | }; 44 | } 45 | 46 | 47 | #endif //MINISQL_APIHELPER_H 48 | -------------------------------------------------------------------------------- /src/BufferManager/BufferManager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "BufferManager.h" 5 | 6 | using namespace std; 7 | 8 | BufferManager::BufferManager() { 9 | blockBuffer.resize(MaxBlocks); 10 | maxLRU = 0; 11 | int id = 0; 12 | for (auto &block : blockBuffer) { 13 | block.id = id; 14 | } 15 | } 16 | 17 | int BufferManager::getBlockTail(string filename) { 18 | struct stat st; 19 | if (stat(filename.c_str(), &st) == 0) { 20 | return (st.st_size / BlockSize - 1); 21 | } 22 | cerr << "Failed to get file tail" << endl; 23 | } 24 | 25 | void BufferManager::setDirty(const string &filename, unsigned int blockID) { 26 | Block &block = findBlockPair(filename, blockID); 27 | block.dirty = true; 28 | } 29 | 30 | void BufferManager::setBusy(int id) { 31 | ++maxLRU; 32 | Block &block = blockBuffer[id]; 33 | block.busy = true; 34 | block.LRUCount = maxLRU; 35 | } 36 | 37 | Block &BufferManager::getLRU() { 38 | int max = maxLRU; 39 | Block *detect = nullptr; 40 | for (auto const &block : blockBuffer) { 41 | if (block.busy) { continue; } 42 | if (block.LRUCount <= max) { 43 | max = block.LRUCount; 44 | detect = const_cast(&block); 45 | } 46 | } 47 | if (detect == nullptr) { cerr << "No LRU block found!"; } 48 | return *detect; 49 | } 50 | 51 | // remove the buffer node with file instance 52 | void BufferManager::removeFile(string filename) { 53 | for (auto &block : blockBuffer) { 54 | if (block.filename == filename) { 55 | block.reset(); 56 | } 57 | } 58 | if (remove(filename.c_str())) { 59 | cerr << "Fail to remove file: " << filename << endl; 60 | } 61 | } 62 | 63 | void BufferManager::createFile(string in) { 64 | ofstream f1(in); 65 | } 66 | 67 | // Find clean blocks, if failed use LRU replacement 68 | Block &BufferManager::getFreeBlock() { 69 | for (auto &block : blockBuffer) { 70 | if (!block.dirty && !block.busy) { 71 | block.reset(); 72 | setBusy(block.id); 73 | return block; 74 | } 75 | } 76 | 77 | Block &bb = getLRU(); 78 | bb.flush().reset(); 79 | setBusy(bb.id); 80 | 81 | return bb; 82 | } 83 | 84 | char *BufferManager::getBlock(string filename, unsigned int offset, bool allocate) { 85 | for (auto &block : blockBuffer) { 86 | if (block.filename == filename && block.blockID == offset) { 87 | setBusy(block.id); 88 | blockMap.insert(TypeBlockMap::value_type(make_pair(filename, offset), block)); 89 | return block.content; 90 | } 91 | } 92 | 93 | fstream fp; 94 | fp.open(filename, ios::in | ios::out | ios::binary); 95 | if (!fp.good()) 96 | cerr << "Fail to open file: " << filename << "." << endl; 97 | fp.seekg(ios_base::end); 98 | //int blockOffset = fp.tellg() / BlockSize; 99 | int blockOffset = getBlockTail(filename) + 1; 100 | // cout << "Detected blockOffset: " << blockOffset << endl; 101 | if (offset >= blockOffset) { 102 | if (!allocate) { return nullptr; } 103 | if (blockOffset != offset) { 104 | cerr << fp.tellg() << " " << blockOffset << " " << offset << endl; 105 | cerr << "Requesting way beyond the tail!" << endl; 106 | return nullptr; 107 | } 108 | // cout << "Requesting new block..." << endl; 109 | } 110 | 111 | Block &block = getFreeBlock(); 112 | block.mark(filename, offset); 113 | blockMap.insert(TypeBlockMap::value_type(make_pair(filename, offset), block)); 114 | 115 | fp.seekg(offset * BlockSize, ios::beg); 116 | fp.read(block.content, BlockSize); 117 | fp.close(); 118 | setBusy(block.id); 119 | block.flush(); 120 | return block.content; 121 | } 122 | 123 | void BufferManager::setFree(string filename, unsigned int blockID) { 124 | Block &block = findBlockPair(filename, blockID); 125 | block.busy = false; 126 | blockMap.erase(make_pair(block.filename, block.blockID)); 127 | } 128 | 129 | Block &BufferManager::findBlockPair(string filename, unsigned int blockID) const { 130 | auto parent = blockMap.find(make_pair(filename, blockID)); 131 | if (parent == blockMap.end()) { 132 | cerr << "Element not found!"; 133 | } 134 | return parent->second; 135 | } 136 | 137 | void BufferManager::flushAllBlocks() { 138 | for (auto &block : blockBuffer) { 139 | if (block.dirty) { 140 | block.flush(); 141 | } 142 | block.reset(); 143 | } 144 | } -------------------------------------------------------------------------------- /src/BufferManager/BufferManager.h: -------------------------------------------------------------------------------- 1 | #ifndef MINISQL_BUFFERMANAGER_H 2 | #define MINISQL_BUFFERMANAGER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "../../include/DataStructure.h" 11 | 12 | using namespace std; 13 | using namespace MINISQL_BASE; 14 | 15 | struct Block { 16 | string filename; 17 | int id; // id in buffer list 18 | unsigned int blockID; // block offset 19 | bool dirty; // whether need to write to disk 20 | bool busy; // whether is free or not 21 | int LRUCount; 22 | char content[BlockSize]; 23 | 24 | Block() { reset(); } 25 | 26 | void reset() { 27 | dirty = busy = false; 28 | memset(content, 0, BlockSize); 29 | } 30 | 31 | void mark(string filename, unsigned int blockID) { 32 | this->filename = filename; 33 | this->blockID = blockID; 34 | } 35 | 36 | Block &flush() { 37 | fstream fp; 38 | fp.open(filename, ios::in | ios::out | ios::ate | ios::binary); 39 | fp.seekg(blockID * BlockSize, ios::beg); 40 | // cout << "Flushing: " << content << endl; 41 | fp.write(content, BlockSize); 42 | fp.close(); 43 | return *this; 44 | } 45 | }; 46 | 47 | 48 | class BufferManager { 49 | public: 50 | BufferManager(); 51 | 52 | ~BufferManager() = default; 53 | 54 | int getBlockTail(string filename); 55 | 56 | void setDirty(const string &filename, unsigned int blockID); 57 | 58 | char *getBlock(string filename, unsigned int blockID, bool allocate = false); 59 | 60 | void flushAllBlocks(); // write all content in block to disk 61 | 62 | void createFile(string); 63 | 64 | Block &getLRU(); 65 | 66 | void removeFile(string filename); 67 | 68 | void setFree(string filename, unsigned int blockID); 69 | 70 | private: 71 | typedef map, Block &> TypeBlockMap; 72 | 73 | TypeBlockMap blockMap; 74 | 75 | std::vector blockBuffer; 76 | 77 | void setBusy(int id); // mark as using 78 | 79 | // find block id which is available 80 | Block &getFreeBlock(); 81 | 82 | Block &findBlockPair(string filename, unsigned int blockID) const; 83 | 84 | int maxLRU; 85 | }; 86 | 87 | #endif //MINISQL_BUFFERMANAGER_H -------------------------------------------------------------------------------- /src/CatalogManager/CatalogManager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "CatalogManager.h" 8 | #include "../API/ApiHelper.h" 9 | 10 | using namespace std; 11 | 12 | 13 | void CatalogManager::CreateTable(const std::string &table_name, 14 | const std::vector> &schema_list, 15 | const std::string &primary_key_name 16 | ) 17 | { 18 | Table tb; 19 | tb.Name = table_name; 20 | tb.attrCnt = (int) schema_list.size(); 21 | uint32_t len{0}; 22 | char auto_ind{'A'}; 23 | 24 | auto rm = Api::ApiHelper::getApiHelper()->getRecordManager(); 25 | 26 | for (const auto &sch: schema_list) 27 | { 28 | len += sch.second.getSize(); 29 | tb.attrNames.push_back(sch.first); 30 | auto t = sch.second; 31 | t.attrName = sch.first; 32 | tb.attrType.push_back(t); 33 | if (sch.first == primary_key_name) 34 | { 35 | (tb.attrType.end() - 1)->primary = true; 36 | (tb.attrType.end() - 1)->unique = true; 37 | } 38 | } 39 | 40 | tb.recordLength = len; 41 | tb.recordCnt = 0; 42 | for (auto &t: tb.attrType) 43 | { 44 | if (t.unique && !t.primary) 45 | { 46 | tb.index.push_back(std::make_pair(t.attrName, std::string("auto_ind_") + (auto_ind++))); 47 | rm->createIndex(tb, t); 48 | } 49 | } 50 | tables.push_back(tb); 51 | kv[tb.Name] = 0; 52 | } 53 | 54 | CatalogManager::CatalogManager() 55 | : tables(std::vector()) 56 | { 57 | LoadFromFile(); 58 | } 59 | 60 | CatalogManager::~CatalogManager() 61 | { 62 | Flush(); 63 | } 64 | 65 | void CatalogManager::Flush() const 66 | { 67 | std::ofstream ofs(meta_file_name); 68 | ofs << tables.size() << std::endl; 69 | 70 | for (const auto &tb: tables) 71 | { 72 | ofs << tb.Name << std::endl; 73 | ofs << tb.recordCnt << std::endl; 74 | ofs << kv[tb.Name] << std::endl; 75 | std::ofstream otbfs(tb.Name + ".catalog"); 76 | uint16_t i{0}; 77 | 78 | otbfs << tb.attrNames.size() << std::endl; 79 | for (const auto &attr_name: tb.attrNames) 80 | { 81 | otbfs << attr_name << std::endl; 82 | const auto &attr = tb.attrType[i]; 83 | switch (attr.type) 84 | { 85 | case SqlValueTypeBase::Integer: 86 | otbfs << "int" << std::endl; 87 | break; 88 | case SqlValueTypeBase::Float: 89 | otbfs << "float" << std::endl; 90 | break; 91 | case SqlValueTypeBase::String: 92 | otbfs << "char" << std::endl; 93 | break; 94 | } 95 | if (attr.type == SqlValueTypeBase::String) 96 | { 97 | otbfs << attr.charSize << std::endl; 98 | } else 99 | { 100 | otbfs << 0 << std::endl; 101 | } 102 | otbfs << (attr.primary ? 1 : 0) << std::endl; 103 | otbfs << (attr.unique ? 1 : 0) << std::endl; 104 | auto ind = std::find_if(tb.index.begin(), tb.index.end(), 105 | [&attr_name](const std::pair &p) 106 | { 107 | return p.first == attr_name; 108 | }); 109 | if (ind != tb.index.end()) 110 | { 111 | otbfs << 1 << std::endl << ind->second << std::endl; 112 | } else 113 | { 114 | otbfs << 0 << std::endl << "-" << std::endl; 115 | } 116 | ++i; 117 | } 118 | otbfs.close(); 119 | } 120 | ofs.close(); 121 | } 122 | 123 | void CatalogManager::LoadFromFile() 124 | { 125 | std::ifstream ifs(meta_file_name); 126 | if (!ifs.is_open()) 127 | { 128 | std::ofstream touch(meta_file_name); 129 | return; 130 | } 131 | size_t tables_count{0}; 132 | ifs >> tables_count; 133 | 134 | auto rm = Api::ApiHelper::getApiHelper()->getRecordManager(); 135 | 136 | std::string tb_name; 137 | for (auto i = 0; i < tables_count; ++i) 138 | { 139 | ifs >> tb_name; 140 | auto file_name = tb_name + ".catalog"; 141 | std::ifstream itbfs(file_name); 142 | 143 | Table tb; 144 | std::vector ind_vec; 145 | //size_t len{0}; 146 | ifs >> tb.recordCnt; 147 | 148 | int v; 149 | ifs >> v; 150 | kv[tb_name] = v; 151 | 152 | tb.Name = tb_name; 153 | 154 | uint16_t attr_cnts{0}; 155 | uint16_t record_length{0}; 156 | 157 | size_t attr_counts{0}; 158 | itbfs >> attr_counts; 159 | for (auto ci = 0; ci < attr_counts; ++ci) 160 | { 161 | std::string attr_name, type_name, index_name; 162 | uint16_t isPri, isUni, isInd, size; 163 | SqlValueType type; 164 | 165 | itbfs >> attr_name >> type_name >> size >> isPri >> isUni >> isInd >> index_name; 166 | ++attr_cnts; 167 | 168 | tb.attrNames.push_back(attr_name); 169 | if (type_name == "int") 170 | { 171 | type.type = SqlValueTypeBase::Integer; 172 | } else if (type_name == "char") 173 | { 174 | type.type = SqlValueTypeBase::String; 175 | type.charSize = size; 176 | } else if (type_name == "float") 177 | { 178 | type.type = SqlValueTypeBase::Float; 179 | } else 180 | { 181 | valid_assert(false); 182 | } 183 | record_length += type.getSize(); 184 | type.primary = isPri != 0; 185 | type.unique = isUni != 0; 186 | type.attrName = attr_name; 187 | tb.attrType.push_back(type); 188 | if (isInd) 189 | { 190 | auto ind = std::make_pair(attr_name, index_name); 191 | tb.index.push_back(ind); 192 | ind_vec.push_back(type); 193 | } 194 | } 195 | tb.attrCnt = attr_cnts; 196 | tb.recordLength = record_length; 197 | tables.push_back(tb); 198 | for(auto &it: ind_vec) 199 | { 200 | rm->createIndex(tb, it); 201 | } 202 | } 203 | } 204 | 205 | bool CatalogManager::TableExist(const std::string &table_name) const 206 | { 207 | return std::find_if(tables.begin(), tables.end(), [&table_name](const Table &tb) 208 | { 209 | return (tb.Name == table_name); 210 | }) != tables.end(); 211 | } 212 | 213 | Table &CatalogManager::GetTable(const std::string &table_name) 214 | { 215 | return *std::find_if(tables.begin(), tables.end(), [&table_name](const Table &tb) 216 | { 217 | return (tb.Name == table_name); 218 | }); 219 | } 220 | 221 | bool CatalogManager::CheckIndexNameExists(const std::string &index_name) const 222 | { 223 | for (const auto &t: tables) 224 | { 225 | for (auto &i: t.index) 226 | { 227 | if (i.second == index_name) return true; 228 | } 229 | } 230 | return false; 231 | } 232 | 233 | bool CatalogManager::RemoveTable(const Table &table) 234 | { 235 | if (std::find_if(tables.begin(), tables.end(), [&table](const Table &tb) 236 | { return tb.Name == table.Name; }) == tables.end()) 237 | return false; 238 | tables.erase(std::find_if(tables.begin(), tables.end(), [&table](const Table &tb) 239 | { return tb.Name == table.Name; })); 240 | return true; 241 | } 242 | 243 | Table &CatalogManager::GetTableWithIndex(const std::string &index_name) 244 | { 245 | for (auto &t: tables) 246 | { 247 | for (const auto &i: t.index) 248 | { 249 | if (i.second == index_name) return t; 250 | } 251 | } 252 | } 253 | 254 | -------------------------------------------------------------------------------- /src/CatalogManager/CatalogManager.h: -------------------------------------------------------------------------------- 1 | #ifndef _catalog_H_ 2 | #define _catalog_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "../../include/DataStructure.h" 8 | 9 | using namespace MINISQL_BASE; 10 | using namespace std; 11 | 12 | class CatalogManager final 13 | { 14 | public: 15 | void CreateTable(const std::string &table_name, 16 | const std::vector> &schema_list, 17 | const std::string &primary_key_name 18 | ); 19 | 20 | bool TableExist(const std::string &table_name) const; 21 | 22 | Table &GetTable(const std::string &table_name); 23 | 24 | bool CheckIndexNameExists(const std::string &index_name) const; 25 | 26 | Table &GetTableWithIndex(const std::string &index_name); 27 | 28 | bool RemoveTable(const Table &table); 29 | 30 | CatalogManager(); 31 | 32 | ~CatalogManager(); 33 | 34 | void Flush() const; 35 | 36 | bool isValid() const 37 | { return validFlag; } 38 | 39 | mutable std::map kv; 40 | 41 | private: 42 | void LoadFromFile(); 43 | 44 | bool valid_assert(bool cond) 45 | { 46 | return cond ? cond : (validFlag = cond); 47 | } 48 | 49 | private: 50 | std::vector
tables; 51 | 52 | static constexpr auto meta_file_name = "tables.meta"; 53 | 54 | bool validFlag = true; 55 | }; 56 | #endif 57 | -------------------------------------------------------------------------------- /src/CatalogManager/convention.md: -------------------------------------------------------------------------------- 1 | # Catalog Text File format convention 2 | 3 | ## meta file: 4 | * each line for a table 5 | * only a table name in each line 6 | 7 | ## Catalog file: 8 | ### for each attribute 9 | * attribute name 10 | * type: int char float 11 | * size: 0 for int and float, 1~255 for char 12 | * is primary key: 0 or 1 13 | * is unique: 0 or 1 14 | * is index: 0 or 1 15 | * index name: a string, anything for no index, index name for index. 16 | -------------------------------------------------------------------------------- /src/IndexManager/IndexManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Haotian on 17/6/5. 3 | // 4 | 5 | #include "IndexManager.h" 6 | 7 | IndexManager::~IndexManager() { 8 | 9 | } 10 | 11 | bool IndexManager::create(const string &filename, const SqlValueType &type) { 12 | int itemSize = type.getSize(); 13 | int treeDegree = type.getDegree(); 14 | switch (type.M()) { 15 | case MINISQL_TYPE_INT: 16 | intBPTree = new BPTree(filename, itemSize, treeDegree); 17 | intIndexMap.insert(intMap::value_type(filename, intBPTree)); 18 | break; 19 | case MINISQL_TYPE_FLOAT: 20 | floatBPTree = new BPTree(filename, itemSize, treeDegree); 21 | floatIndexMap.insert(floatMap::value_type(filename, floatBPTree)); 22 | break; 23 | case MINISQL_TYPE_CHAR: 24 | charBPTree = new BPTree(filename, itemSize, treeDegree); 25 | charIndexMap.insert(charMap::value_type(filename, charBPTree)); 26 | break; 27 | default: 28 | cerr << "Undefined type!" << endl; 29 | break; 30 | } 31 | } 32 | 33 | bool IndexManager::drop(const string &filename, const SqlValueType &type) { 34 | switch (type.M()) { 35 | case MINISQL_TYPE_INT: 36 | intBPIterator = intIndexMap.find(filename); 37 | delete intBPIterator->second; 38 | intIndexMap.erase(intBPIterator); 39 | break; 40 | case MINISQL_TYPE_FLOAT: 41 | floatBPIterator = floatIndexMap.find(filename); 42 | delete floatBPIterator->second; 43 | floatIndexMap.erase(floatBPIterator); 44 | break; 45 | case MINISQL_TYPE_CHAR: 46 | charBPIterator = charIndexMap.find(filename); 47 | delete charBPIterator->second; 48 | charIndexMap.erase(charBPIterator); 49 | break; 50 | default: 51 | cerr << "Undefined type!" << endl; 52 | break; 53 | } 54 | } 55 | 56 | int IndexManager::search(const string &filename, const Element &e) { 57 | NodeSearchParse intNode; 58 | NodeSearchParse floatNode; 59 | NodeSearchParse charNode; 60 | switch (e.type.M()) { 61 | case MINISQL_TYPE_INT: 62 | intNode = intIndexMap.find(filename)->second->findNode(e.i); 63 | intOffsetMap[filename] = intNode; 64 | return intNode.node->keyOffset[intNode.index]; 65 | case MINISQL_TYPE_FLOAT: 66 | floatNode = floatIndexMap.find(filename)->second->findNode(e.r); 67 | floatOffsetMap[filename] = floatNode; 68 | return floatNode.node->keyOffset[floatNode.index]; 69 | case MINISQL_TYPE_CHAR: 70 | charNode = charIndexMap.find(filename)->second->findNode(e.str); 71 | charOffsetMap[filename] = charNode; 72 | return charNode.node->keyOffset[charNode.index]; 73 | default: 74 | cerr << "Undefined type!" << endl; 75 | return -1; 76 | } 77 | } 78 | 79 | int IndexManager::searchNext(const string &filename, int attrType) { 80 | NodeSearchParse intNode; 81 | NodeSearchParse floatNode; 82 | NodeSearchParse charNode; 83 | 84 | switch (attrType) { 85 | case MINISQL_TYPE_INT: 86 | intNode = intOffsetMap.find(filename)->second; 87 | intNode.index++; 88 | if (intNode.index == intNode.node->cnt) { 89 | intNode.node = intNode.node->sibling; 90 | intNode.index = 0; 91 | } 92 | intOffsetMap[filename] = intNode; 93 | if (intNode.node != nullptr) { 94 | return intNode.node->keyOffset[intNode.index]; 95 | } 96 | break; 97 | case MINISQL_TYPE_FLOAT: 98 | floatNode = floatOffsetMap.find(filename)->second; 99 | floatNode.index++; 100 | if (floatNode.index == floatNode.node->cnt) { 101 | floatNode.node = floatNode.node->sibling; 102 | floatNode.index = 0; 103 | } 104 | floatOffsetMap[filename] = floatNode; 105 | if (floatNode.node != nullptr) { 106 | return floatNode.node->keyOffset[floatNode.index]; 107 | } 108 | break; 109 | case MINISQL_TYPE_CHAR: 110 | charNode = charOffsetMap.find(filename)->second; 111 | charNode.index++; 112 | if (charNode.index == charNode.node->cnt) { 113 | charNode.node = charNode.node->sibling; 114 | charNode.index = 0; 115 | } 116 | charOffsetMap[filename] = charNode; 117 | if (charNode.node != nullptr) { 118 | return charNode.node->keyOffset[charNode.index]; 119 | } 120 | break; 121 | default: 122 | cerr << "Undefined type!" << endl; 123 | return -1; 124 | } 125 | return -1; 126 | } 127 | 128 | bool IndexManager::finishSearch(const string &filename, int attrType) { 129 | switch (attrType) { 130 | case MINISQL_TYPE_INT: 131 | intOffsetMap.erase(filename); 132 | break; 133 | case MINISQL_TYPE_FLOAT: 134 | floatOffsetMap.erase(filename); 135 | break; 136 | case MINISQL_TYPE_CHAR: 137 | charOffsetMap.erase(filename); 138 | break; 139 | default: 140 | cerr << "Undefined type!" << endl; 141 | break; 142 | } 143 | return true; 144 | } 145 | 146 | bool IndexManager::insert(const string &filename, const Element &e, int offset) { 147 | switch (e.type.M()) { 148 | case MINISQL_TYPE_INT: 149 | return intIndexMap.find(filename)->second->insert(e.i, offset); 150 | case MINISQL_TYPE_FLOAT: 151 | return floatIndexMap.find(filename)->second->insert(e.r, offset); 152 | case MINISQL_TYPE_CHAR: 153 | return charIndexMap.find(filename)->second->insert(e.str, offset); 154 | default: 155 | cerr << "Undefined type!" << endl; 156 | break; 157 | } 158 | } 159 | 160 | bool IndexManager::removeKey(const string &filename, const Element &e) { 161 | switch (e.type.M()) { 162 | case MINISQL_TYPE_INT: 163 | return intIndexMap.find(filename)->second->remove(e.i); 164 | case MINISQL_TYPE_FLOAT: 165 | return floatIndexMap.find(filename)->second->remove(e.r); 166 | case MINISQL_TYPE_CHAR: 167 | return charIndexMap.find(filename)->second->remove(e.str); 168 | default: 169 | cerr << "Undefined type!" << endl; 170 | break; 171 | } 172 | } 173 | 174 | int IndexManager::searchHead(const string &filename, int attrType) { 175 | NodeSearchParse intNode; 176 | NodeSearchParse floatNode; 177 | NodeSearchParse charNode; 178 | switch (attrType) { 179 | case MINISQL_TYPE_INT: 180 | intNode.node = intIndexMap.find(filename)->second->getHeadNode(); 181 | intNode.index = 0; 182 | intOffsetMap[filename] = intNode; 183 | return intNode.node->keyOffset[intNode.index]; 184 | case MINISQL_TYPE_FLOAT: 185 | floatNode.node = floatIndexMap.find(filename)->second->getHeadNode(); 186 | floatNode.index = 0; 187 | floatOffsetMap[filename] = floatNode; 188 | return floatNode.node->keyOffset[floatNode.index]; 189 | case MINISQL_TYPE_CHAR: 190 | charNode.node = charIndexMap.find(filename)->second->getHeadNode(); 191 | charNode.index = 0; 192 | charOffsetMap[filename] = charNode; 193 | return charNode.node->keyOffset[charNode.index]; 194 | default: 195 | cerr << "Undefined type!" << endl; 196 | break; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/IndexManager/IndexManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Haotian on 17/6/5. 3 | // 4 | 5 | #ifndef MINISQL_INDEXMANAGER_H 6 | #define MINISQL_INDEXMANAGER_H 7 | 8 | #include 9 | #include 10 | #include "../../include/DataStructure.h" 11 | #include "../../lib/BPTree/BPTree.h" 12 | 13 | using namespace std; 14 | using namespace MINISQL_BASE; 15 | 16 | class IndexManager { 17 | public: 18 | IndexManager() = default; 19 | 20 | // fixme!!! destructor needs to be implemented to release memory of B+Tree instances 21 | ~IndexManager(); 22 | 23 | bool create(const string &filename, const SqlValueType &type); 24 | 25 | bool drop(const string &filename, const SqlValueType &type); 26 | 27 | int search(const string &filename, const Element &e); 28 | 29 | int searchHead(const string &filename, int attrType); 30 | 31 | int searchNext(const string &filename, int attrType); 32 | 33 | bool finishSearch(const string &filename, int attrType); 34 | 35 | bool insert(const string &filename, const Element &e, int offset); 36 | 37 | bool removeKey(const string &filename, const Element &e); 38 | 39 | private: 40 | typedef map *> intMap; 41 | typedef map *> floatMap; 42 | typedef map *> charMap; 43 | typedef map> intOMap; 44 | typedef map> floatOMap; 45 | typedef map> charOMap; 46 | 47 | intMap intIndexMap; 48 | floatMap floatIndexMap; 49 | charMap charIndexMap; 50 | intOMap intOffsetMap; 51 | floatOMap floatOffsetMap; 52 | charOMap charOffsetMap; 53 | 54 | BPTree *intBPTree; 55 | BPTree *floatBPTree; 56 | BPTree *charBPTree; 57 | 58 | intMap::iterator intBPIterator; 59 | floatMap::iterator floatBPIterator; 60 | charMap::iterator charBPIterator; 61 | }; 62 | 63 | 64 | #endif //MINISQL_INDEXMANAGER_H 65 | -------------------------------------------------------------------------------- /src/Interpreter/Interpreter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/8/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef NO_GNU_READLINE 15 | #warning GNU readline is not enabled 16 | #else 17 | 18 | #include 19 | 20 | #endif 21 | 22 | #include "Interpreter.h" 23 | #include "QueryRequest.h" 24 | #include "../API/Api.h" 25 | 26 | #include "parser_public.h" 27 | #include "../API/ApiHelper.h" 28 | 29 | QueryRequest *query = nullptr; 30 | 31 | char input_s[INPUT_LENGTH]; 32 | size_t input_len; 33 | 34 | char input_tmp[INPUT_LENGTH]; 35 | 36 | char half[INPUT_LENGTH]; 37 | char *half_ptr; 38 | 39 | std::string file_name; 40 | 41 | bool isExit = false; 42 | 43 | #pragma clang diagnostic push 44 | #pragma clang diagnostic ignored "-Wmissing-noreturn" 45 | 46 | void main_repl_loop [[noreturn]]() 47 | { 48 | half_ptr = &half[0]; 49 | while (true) 50 | { 51 | memset(input_tmp, 0, INPUT_LENGTH); 52 | #if defined(__APPLE__) || defined(NO_GNU_READLINE) 53 | std::cout << "\nMiniSQL> "; 54 | std::cin.getline(input_tmp, INPUT_LENGTH); 55 | #else 56 | char *rl = readline("MiniSQL>"); 57 | if (!rl) 58 | continue; 59 | std::strcpy(input_tmp, rl); 60 | std::free(rl); 61 | #endif 62 | 63 | //copy temp input to buffer to form a multi-line completed statement. 64 | input_tmp[strlen(input_tmp)] = ' '; 65 | auto tmp_len = strlen(input_tmp); 66 | bool complete{false}; 67 | for (int i = 0; i < tmp_len; ++i) 68 | { 69 | *half_ptr++ = input_tmp[i]; 70 | if (half_ptr[-1] == ';') 71 | { 72 | complete = true; 73 | memset(input_s, 0, INPUT_LENGTH); 74 | std::strncpy(input_s, half, half_ptr - half); 75 | 76 | half_ptr = half; 77 | memset(half, 0, INPUT_LENGTH); 78 | 79 | input_len = std::strlen(input_s); 80 | do_parse(); 81 | } 82 | } 83 | if (!complete) 84 | { 85 | //I just forget what i should do here. 86 | } 87 | } 88 | } 89 | 90 | #pragma clang diagnostic pop 91 | 92 | ///before do_parse() invoke, we should set input_s and input_len to a correct value. 93 | void do_parse() 94 | { 95 | yyparse(); 96 | if (isExit) 97 | { 98 | std::cout << "Bye!\n"; 99 | auto cm = Api::ApiHelper::getApiHelper()->getCatalogManager(); 100 | auto bm = Api::ApiHelper::getApiHelper()->getBufferManager(); 101 | cm->Flush(); 102 | bm->flushAllBlocks(); 103 | exit(0); 104 | } 105 | if (query == nullptr) 106 | { 107 | return; 108 | } else 109 | { 110 | auto start_time = std::chrono::system_clock::now(); 111 | dispatch(); 112 | auto finish_time = std::chrono::system_clock::now(); 113 | } 114 | } 115 | 116 | void dispatch() 117 | { 118 | auto e_file = dynamic_cast(query); 119 | if (e_file) 120 | { 121 | exec_file(file_name); 122 | delete e_file; 123 | query = nullptr; 124 | return; 125 | } 126 | auto use_db_query = dynamic_cast(query); 127 | if (use_db_query) 128 | { 129 | Api::use_database(use_db_query->database_name); 130 | std::cout << "Database " << use_db_query->database_name << " used." << std::endl; 131 | 132 | delete use_db_query; 133 | query = nullptr; 134 | return; 135 | } 136 | auto insert_query = dynamic_cast(query); 137 | if (insert_query) 138 | { 139 | auto insert_count = Api::insert(Api::get_db_name_prefix() + insert_query->table_name, insert_query->value_list); 140 | 141 | delete insert_query; 142 | query = nullptr; 143 | return; 144 | } 145 | 146 | auto select_query = dynamic_cast(query); 147 | if (select_query) 148 | { 149 | bool r; 150 | if (select_query->isSelectAll) 151 | { 152 | r = Api::select(Api::get_db_name_prefix() + select_query->table_name, select_query->condition_list); 153 | } else 154 | { 155 | r = Api::select(Api::get_db_name_prefix() + select_query->table_name, 156 | select_query->condition_list, 157 | select_query->attr_list); 158 | } 159 | 160 | delete select_query; 161 | query = nullptr; 162 | return; 163 | } 164 | 165 | auto delete_query = dynamic_cast(query); 166 | if (delete_query) 167 | { 168 | Api::delete_op(Api::get_db_name_prefix() + delete_query->table_name, delete_query->condition_list); 169 | delete delete_query; 170 | query = nullptr; 171 | return; 172 | } 173 | 174 | auto update_query = dynamic_cast(query); 175 | if (update_query) 176 | { 177 | Api::update(Api::get_db_name_prefix() + update_query->table_name, update_query->attr, update_query->value, 178 | update_query->condition_list); 179 | delete update_query; 180 | query = nullptr; 181 | return; 182 | } 183 | 184 | auto create_table_query = dynamic_cast(query); 185 | if (create_table_query) 186 | { 187 | auto r = Api::create_table(Api::get_db_name_prefix() + create_table_query->table_name, 188 | create_table_query->table_schema_list, 189 | create_table_query->primary_key_name); 190 | 191 | delete create_table_query; 192 | query = nullptr; 193 | return; 194 | } 195 | 196 | auto create_index_query = dynamic_cast(query); 197 | if (create_index_query) 198 | { 199 | auto r = Api::create_index(Api::get_db_name_prefix() + create_index_query->table_name, 200 | create_index_query->attr_name, create_index_query->index_name); 201 | 202 | delete create_index_query; 203 | query = nullptr; 204 | return; 205 | } 206 | 207 | auto drop_table_query = dynamic_cast(query); 208 | if (drop_table_query) 209 | { 210 | auto r = Api::drop_table(Api::get_db_name_prefix() + drop_table_query->table_name); 211 | 212 | delete drop_table_query; 213 | query = nullptr; 214 | return; 215 | } 216 | 217 | auto drop_index_query = dynamic_cast(query); 218 | if (drop_index_query) 219 | { 220 | auto r = Api::drop_index(Api::get_db_name_prefix() + drop_index_query->index_name); 221 | 222 | delete drop_index_query; 223 | query = nullptr; 224 | return; 225 | } 226 | return; 227 | } 228 | 229 | void exec_file(const std::string &file_name) 230 | { 231 | std::ifstream file(file_name); 232 | 233 | if (!file.is_open()) 234 | { 235 | std::cerr << "File not found!" << std::endl; 236 | return; 237 | } 238 | std::cout << "Executing SQL file: " << file_name << std::endl; 239 | 240 | while (!file.eof()) 241 | { 242 | memset(input_tmp, 0, INPUT_LENGTH); 243 | file.getline(input_tmp, INPUT_LENGTH); 244 | 245 | // std::cout << input_tmp << std::endl; 246 | 247 | //code below copied from main loop: 248 | //copy temp input to buffer to form a multi-line completed statement. 249 | input_tmp[strlen(input_tmp)] = ' '; 250 | auto tmp_len = strlen(input_tmp); 251 | bool complete{false}; 252 | for (int i = 0; i < tmp_len; ++i) 253 | { 254 | *half_ptr++ = input_tmp[i]; 255 | if (half_ptr[-1] == ';') 256 | { 257 | complete = true; 258 | memset(input_s, 0, INPUT_LENGTH); 259 | std::strncpy(input_s, half, half_ptr - half); 260 | 261 | half_ptr = half; 262 | memset(half, 0, INPUT_LENGTH); 263 | 264 | input_len = std::strlen(input_s); 265 | do_parse(); 266 | } 267 | } 268 | if (!complete) 269 | { 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/Interpreter/Interpreter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/8/17. 3 | // 4 | 5 | #ifndef MINISQL_INTERPRETER_H 6 | #define MINISQL_INTERPRETER_H 7 | 8 | #include "QueryRequest.h" 9 | 10 | extern QueryRequest *query; 11 | 12 | class Interpreter 13 | { 14 | 15 | }; 16 | 17 | void main_repl_loop [[noreturn]](); 18 | 19 | void do_parse(); 20 | 21 | void dispatch(); 22 | 23 | void exec_file(const std::string &file_name); 24 | 25 | #endif //MINISQL_INTERPRETER_H 26 | -------------------------------------------------------------------------------- /src/Interpreter/Makefile: -------------------------------------------------------------------------------- 1 | # This makefile is for the target generated source code files complied by flex and bison. 2 | 3 | YACC=bison -dy 4 | LEX=flex 5 | 6 | all: scan.cpp parse.cpp 7 | 8 | parse.cpp: parse.y 9 | $(YACC) parse.y; mv y.tab.c parse.cpp 10 | 11 | scan.cpp: scan.l 12 | $(LEX) scan.l; mv lex.yy.c scan.cpp 13 | 14 | clean: 15 | rm parse.cpp scan.cpp 16 | -------------------------------------------------------------------------------- /src/Interpreter/QueryRequest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/13/17. 3 | // 4 | 5 | #include "QueryRequest.h" 6 | 7 | QueryRequest::~QueryRequest() 8 | {} 9 | -------------------------------------------------------------------------------- /src/Interpreter/QueryRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/13/17. 3 | // 4 | 5 | #ifndef MINISQL_QUERYREQUEST_H 6 | #define MINISQL_QUERYREQUEST_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "operator.h" 15 | #include "../../include/DataStructure.h" 16 | 17 | using namespace MINISQL_BASE; 18 | 19 | enum class QueryType 20 | { 21 | INSERT, 22 | DELETE, 23 | SELECT, 24 | UPDATE, 25 | CREATE_TABLE, 26 | CREATE_INDEX, 27 | DROP_TABLE, 28 | DROP_INDEX, 29 | USE_DATABASE 30 | }; 31 | 32 | class QueryRequest 33 | { 34 | protected: 35 | QueryType type; 36 | 37 | public: 38 | QueryType getQueryType() const 39 | { 40 | return this->type; 41 | } 42 | 43 | virtual ~QueryRequest() = 0; 44 | }; 45 | 46 | class ExecFileQuery final : public QueryRequest 47 | {}; 48 | 49 | class SelectQuery final : public QueryRequest 50 | { 51 | public: 52 | SelectQuery() 53 | : isSelectAll(false) 54 | { 55 | type = QueryType::SELECT; 56 | } 57 | 58 | ~SelectQuery() override 59 | {} 60 | 61 | bool isSelectAll; 62 | std::string table_name; 63 | std::vector attr_list; 64 | std::vector condition_list; 65 | }; 66 | 67 | class UseDatabaseQuery final : public QueryRequest 68 | { 69 | public: 70 | UseDatabaseQuery() 71 | { 72 | type = QueryType::USE_DATABASE; 73 | } 74 | 75 | std::string database_name; 76 | }; 77 | 78 | class InsertQuery final : public QueryRequest 79 | { 80 | public: 81 | InsertQuery() 82 | { 83 | type = QueryType::INSERT; 84 | } 85 | 86 | ~InsertQuery() override 87 | {} 88 | 89 | std::string table_name; 90 | std::vector value_list; 91 | }; 92 | 93 | class UpdateQuery final : public QueryRequest 94 | { 95 | public: 96 | UpdateQuery() 97 | { 98 | type = QueryType::UPDATE; 99 | } 100 | std::string table_name; 101 | std::string attr; 102 | SqlValue value; 103 | std::vector condition_list; 104 | }; 105 | 106 | class DeleteQuery final : public QueryRequest 107 | { 108 | public: 109 | DeleteQuery() 110 | { 111 | type = QueryType::DELETE; 112 | } 113 | std::string table_name; 114 | std::vector condition_list; 115 | }; 116 | 117 | class CreateTableQuery final : public QueryRequest 118 | { 119 | public: 120 | CreateTableQuery() 121 | { 122 | type = QueryType::CREATE_TABLE; 123 | } 124 | std::string table_name; 125 | std::vector> table_schema_list; 126 | std::string primary_key_name; 127 | }; 128 | 129 | class CreateIndexQuery final : public QueryRequest 130 | { 131 | public: 132 | CreateIndexQuery() 133 | { 134 | type = QueryType::CREATE_INDEX; 135 | } 136 | std::string table_name; 137 | std::string attr_name; 138 | std::string index_name; 139 | }; 140 | 141 | class DropTableQuery final : public QueryRequest 142 | { 143 | public: 144 | DropTableQuery() 145 | { 146 | type = QueryType::DROP_TABLE; 147 | } 148 | std::string table_name; 149 | }; 150 | 151 | class DropIndexQuery final : public QueryRequest 152 | { 153 | public: 154 | DropIndexQuery() 155 | { 156 | type = QueryType::DROP_INDEX; 157 | } 158 | std::string index_name; 159 | }; 160 | #endif //MINISQL_QUERYREQUEST_H 161 | -------------------------------------------------------------------------------- /src/Interpreter/operator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/15/17. 3 | // 4 | 5 | #ifndef MINISQL_OPERATOR_H 6 | #define MINISQL_OPERATOR_H 7 | 8 | 9 | enum class Operator 10 | { 11 | GT_OP, 12 | GE_OP, 13 | LT_OP, 14 | LE_OP, 15 | EQ_OP, 16 | NE_OP 17 | }; 18 | 19 | inline Operator flip_operator(Operator op) 20 | { 21 | switch (op) 22 | { 23 | case Operator::GT_OP: 24 | return Operator::LT_OP; 25 | case Operator::GE_OP: 26 | return Operator::LE_OP; 27 | case Operator ::LT_OP: 28 | return Operator ::GT_OP; 29 | case Operator ::LE_OP: 30 | return Operator ::GE_OP; 31 | } 32 | return op; // = and != need not flip. 33 | } 34 | 35 | 36 | #endif //MINISQL_OPERATOR_H 37 | -------------------------------------------------------------------------------- /src/Interpreter/parse.y: -------------------------------------------------------------------------------- 1 | %require "3.0.4" 2 | 3 | %{ /* -*- C++ -*- */ 4 | 5 | /* 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "QueryRequest.h" 16 | #include "Interpreter.h" 17 | #include "parser_public.h" 18 | 19 | #include "y.tab.h" 20 | 21 | %} 22 | 23 | %define parse.error verbose 24 | %define parse.lac full 25 | 26 | //%define api.pure full 27 | 28 | %token 29 | RW_CREATE 30 | RW_DROP 31 | RW_TABLE 32 | RW_INDEX 33 | RW_SELECT 34 | RW_FROM 35 | RW_WHERE 36 | RW_INSERT 37 | RW_DELETE 38 | RW_UPDATE 39 | RW_AND 40 | RW_INTO 41 | RW_VALUES 42 | RW_SET 43 | RW_UNIQUE 44 | RW_PRIMARY 45 | RW_KEY 46 | RW_ON 47 | 48 | RW_USE 49 | RW_DATABASE 50 | RW_EXECFILE 51 | 52 | RW_INTEGER 53 | RW_FLOAT 54 | RW_CHAR 55 | 56 | RW_EXIT 57 | RW_TEST 58 | 59 | T_LT 60 | T_GT 61 | T_GE 62 | T_LE 63 | T_EQ 64 | T_NE 65 | 66 | T_EOF 67 | 68 | NO_TOKEN 69 | 70 | %token T_INT 71 | %token T_REAL 72 | %token T_STRING 73 | %token T_QSTRING 74 | %token T_RQSTRING 75 | 76 | %type T_ASTRING 77 | %type T_NSTRING 78 | 79 | %type operator 80 | 81 | %type value 82 | 83 | %type condition 84 | 85 | %type attr_list; 86 | 87 | %type condition_list; 88 | %type op_where; 89 | 90 | %type value_list; 91 | 92 | %type op_unique; 93 | %type schema_item; 94 | %type table_schema_definition; 95 | 96 | %type op_primary_key; 97 | 98 | %type value_type; 99 | 100 | %type top_stmt 101 | test 102 | exit 103 | dml 104 | ddl 105 | use_database 106 | execfile 107 | 108 | %type 109 | insert 110 | query 111 | delete_op 112 | update 113 | create_table 114 | create_index 115 | drop_table 116 | drop_index 117 | 118 | %% 119 | 120 | top_input: top_stmt ';' { YYACCEPT; }; 121 | 122 | top_stmt: exit 123 | | test 124 | | dml 125 | | ddl 126 | | use_database 127 | | execfile 128 | ; 129 | 130 | dml: insert 131 | | query 132 | | delete_op 133 | | update 134 | ; 135 | 136 | ddl: create_table 137 | | create_index 138 | | drop_table 139 | | drop_index 140 | ; 141 | 142 | execfile: RW_EXECFILE T_ASTRING 143 | { 144 | file_name = $2; 145 | auto e = new ExecFileQuery(); 146 | query = e; 147 | } 148 | ; 149 | 150 | use_database: RW_USE RW_DATABASE T_NSTRING 151 | { 152 | auto use_db_query = new UseDatabaseQuery(); 153 | use_db_query->database_name = $3; 154 | 155 | query = use_db_query; 156 | } 157 | ; 158 | 159 | create_table: RW_CREATE RW_TABLE T_STRING '(' table_schema_definition op_primary_key ')' 160 | { 161 | auto create_table_query = new CreateTableQuery(); 162 | create_table_query->table_name = $3; 163 | create_table_query->table_schema_list = $5; 164 | create_table_query->primary_key_name = $6; 165 | 166 | query = create_table_query; 167 | } 168 | ; 169 | 170 | create_index: RW_CREATE RW_INDEX T_STRING RW_ON T_STRING '(' T_STRING ')' 171 | { 172 | auto create_index_query = new CreateIndexQuery(); 173 | create_index_query->index_name = $3; 174 | create_index_query->table_name = $5; 175 | create_index_query->attr_name = $7; 176 | 177 | query = create_index_query; 178 | } 179 | ; 180 | 181 | drop_table: RW_DROP RW_TABLE T_STRING 182 | { 183 | auto drop_table_query = new DropTableQuery(); 184 | drop_table_query->table_name = $3; 185 | 186 | query = drop_table_query; 187 | } 188 | ; 189 | 190 | drop_index: RW_DROP RW_INDEX T_STRING 191 | { 192 | auto drop_index_query = new DropIndexQuery(); 193 | drop_index_query->index_name = $3; 194 | 195 | query = drop_index_query; 196 | } 197 | ; 198 | 199 | update: RW_UPDATE T_NSTRING RW_SET T_NSTRING T_EQ value op_where 200 | { 201 | auto update_query = new UpdateQuery(); 202 | update_query->table_name = $2; 203 | update_query->condition_list = $7; 204 | update_query->attr = $4; 205 | update_query->value = $6; 206 | 207 | query = update_query; 208 | } 209 | ; 210 | 211 | delete_op: RW_DELETE RW_FROM T_NSTRING op_where 212 | { 213 | auto delete_query = new DeleteQuery(); 214 | delete_query->table_name = $3; 215 | delete_query->condition_list = $4; 216 | 217 | query = delete_query; 218 | } 219 | ; 220 | 221 | query: RW_SELECT attr_list RW_FROM T_NSTRING op_where 222 | { 223 | auto select_query = new SelectQuery(); 224 | select_query->table_name = $4; 225 | select_query->attr_list = $2; 226 | select_query->condition_list = $5; 227 | 228 | query = select_query; 229 | } 230 | | RW_SELECT '*' RW_FROM T_NSTRING op_where 231 | { 232 | auto select_query = new SelectQuery(); 233 | select_query->table_name = $4; 234 | select_query->condition_list = $5; 235 | 236 | select_query->isSelectAll = true; 237 | 238 | query = select_query; 239 | } 240 | ; 241 | 242 | insert: RW_INSERT RW_INTO T_NSTRING RW_VALUES '(' value_list ')' 243 | { 244 | auto insert_query = new InsertQuery(); 245 | insert_query->table_name = $3; 246 | insert_query->value_list = $6; 247 | 248 | query = insert_query; 249 | } 250 | ; 251 | 252 | op_primary_key: ',' RW_PRIMARY RW_KEY '(' T_NSTRING ')' 253 | { 254 | $$ = $5; 255 | } 256 | | nothing 257 | { 258 | $$ = ""; 259 | } 260 | 261 | op_where: RW_WHERE condition_list 262 | { 263 | $$ = $2; 264 | } 265 | | nothing 266 | { 267 | $$ = std::vector(); 268 | } 269 | ; 270 | 271 | table_schema_definition: table_schema_definition ',' schema_item 272 | { 273 | $$ = $1; 274 | $$.push_back($3); 275 | } 276 | | schema_item 277 | { 278 | $$ = std::vector>(); 279 | $$.push_back($1); 280 | } 281 | ; 282 | 283 | schema_item: T_STRING value_type op_unique 284 | { 285 | $$ = std::make_pair($1, $2); 286 | $$.second.unique = $3; 287 | } 288 | ; 289 | 290 | value_type: RW_INTEGER { $$.type = SqlValueTypeBase::Integer; } 291 | | RW_FLOAT { $$.type = SqlValueTypeBase::Float; } 292 | | RW_CHAR '(' T_INT ')' { $$.type = SqlValueTypeBase::String; $$.charSize = $3; } 293 | ; 294 | 295 | op_unique: RW_UNIQUE { $$ = true; } 296 | | nothing { $$ = false; } 297 | ; 298 | 299 | attr_list: attr_list ',' T_NSTRING 300 | { 301 | $1.push_back($3); 302 | $$ = $1; 303 | } 304 | | T_NSTRING 305 | { 306 | $$ = std::vector(); 307 | $$.push_back($1); 308 | } 309 | ; 310 | 311 | condition_list: condition_list RW_AND condition 312 | { 313 | $1.push_back($3); 314 | $$ = $1; 315 | } 316 | | condition 317 | { 318 | $$ = std::vector(); 319 | $$.push_back($1); 320 | } 321 | ; 322 | 323 | condition: T_NSTRING operator value 324 | { 325 | $$.name = $1; 326 | $$.op = $2; 327 | $$.val = $3; 328 | } 329 | ; 330 | 331 | value_list: value_list ',' value 332 | { 333 | $1.push_back($3); 334 | $$ = $1; 335 | } 336 | | value 337 | { 338 | $$ = std::vector(); 339 | $$.push_back($1); 340 | } 341 | ; 342 | 343 | value: 344 | T_INT { $$.type.type = SqlValueTypeBase::Integer; $$.i = $1; } 345 | | T_REAL { $$.type.type = SqlValueTypeBase::Float; $$.r = $1; } 346 | | T_ASTRING { $$.type.type = SqlValueTypeBase::String; $$.str = $1; } 347 | ; 348 | 349 | operator: 350 | T_LT { $$ = Operator::LT_OP; } 351 | | T_LE { $$ = Operator::LE_OP; } 352 | | T_GT { $$ = Operator::GT_OP; } 353 | | T_GE { $$ = Operator::GE_OP; } 354 | | T_EQ { $$ = Operator::EQ_OP; } 355 | | T_NE { $$ = Operator::NE_OP; } 356 | ; 357 | 358 | T_ASTRING: T_QSTRING | T_STRING; 359 | T_NSTRING: T_RQSTRING | T_STRING; 360 | 361 | exit: RW_EXIT { isExit = true; }; 362 | 363 | nothing: 364 | ; 365 | 366 | test: RW_TEST { std::cout << "YOUR INPUT IS TOOOOOO SIMPLE, SOMETIMES NAIVE!\n"; }; 367 | 368 | %% 369 | 370 | bool bFlag; /* no meanings. */ 371 | 372 | inline int yyerror(const char *s) 373 | { 374 | //std::cerr << "error: "; 375 | std::cerr << s << std::endl; 376 | //std::cerr << "line: " << yylineno << " on token " << yytext << std::endl; 377 | yywrap(); 378 | return 1; 379 | } 380 | 381 | -------------------------------------------------------------------------------- /src/Interpreter/parser_public.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/12/17. 3 | // 4 | 5 | #ifndef MINISQL_PARSER_PUBLIC_H 6 | #define MINISQL_PARSER_PUBLIC_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "operator.h" 16 | #include "QueryRequest.h" 17 | 18 | #define INPUT_LENGTH 409600 19 | 20 | // generated functions 21 | int yylex(); 22 | 23 | int yyparse(); 24 | 25 | extern char input_s[INPUT_LENGTH]; 26 | extern size_t input_len; 27 | 28 | extern std::string file_name; 29 | 30 | extern bool isExit; 31 | 32 | #ifdef YYSTYPE 33 | #undef YYSTYPE 34 | #endif 35 | 36 | #ifdef YY_INPUT 37 | #error YY_INPUT defined 38 | #endif 39 | 40 | #define YY_INPUT(i_s, i_len, I_LEN) do { std::strcpy(i_s, input_s); i_len = input_len; } while(0); 41 | // let us assume the buffer should be read-only. 42 | 43 | int get_id(const std::string &s); 44 | 45 | typedef struct yystype 46 | { 47 | int i; 48 | double r; 49 | std::string str; 50 | bool b; 51 | void *dummy; //for non-final nodes dummy part in AST. 52 | Operator op; 53 | SqlValue val; 54 | Condition cond; 55 | std::pair schema; 56 | std::vector str_list; 57 | std::vector cond_list; 58 | std::vector val_list; 59 | std::vector> schema_list; 60 | SqlValueType val_type; 61 | } YYSTYPE; 62 | 63 | int yyerror(const char *s); 64 | 65 | extern "C" int yywrap(); 66 | 67 | #endif //MINISQL_PARSER_PUBLIC_H 68 | -------------------------------------------------------------------------------- /src/Interpreter/scan.cpp: -------------------------------------------------------------------------------- 1 | 2 | #line 2 "lex.yy.c" 3 | 4 | #define YY_INT_ALIGNED short int 5 | 6 | /* A lexical scanner generated by flex */ 7 | 8 | #define FLEX_SCANNER 9 | #define YY_FLEX_MAJOR_VERSION 2 10 | #define YY_FLEX_MINOR_VERSION 6 11 | #define YY_FLEX_SUBMINOR_VERSION 4 12 | #if YY_FLEX_SUBMINOR_VERSION > 0 13 | #define FLEX_BETA 14 | #endif 15 | 16 | /* First, we deal with platform-specific or compiler-specific issues. */ 17 | 18 | /* begin standard C headers. */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /* end standard C headers. */ 25 | 26 | /* flex integer type definitions */ 27 | 28 | #ifndef FLEXINT_H 29 | #define FLEXINT_H 30 | 31 | /* C99 systems have . Non-C99 systems may or may not. */ 32 | 33 | #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 34 | 35 | /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, 36 | * if you want the limit (max/min) macros for int types. 37 | */ 38 | #ifndef __STDC_LIMIT_MACROS 39 | #define __STDC_LIMIT_MACROS 1 40 | #endif 41 | 42 | #include 43 | typedef int8_t flex_int8_t; 44 | typedef uint8_t flex_uint8_t; 45 | typedef int16_t flex_int16_t; 46 | typedef uint16_t flex_uint16_t; 47 | typedef int32_t flex_int32_t; 48 | typedef uint32_t flex_uint32_t; 49 | #else 50 | typedef signed char flex_int8_t; 51 | typedef short int flex_int16_t; 52 | typedef int flex_int32_t; 53 | typedef unsigned char flex_uint8_t; 54 | typedef unsigned short int flex_uint16_t; 55 | typedef unsigned int flex_uint32_t; 56 | 57 | /* Limits of integral types. */ 58 | #ifndef INT8_MIN 59 | #define INT8_MIN (-128) 60 | #endif 61 | #ifndef INT16_MIN 62 | #define INT16_MIN (-32767-1) 63 | #endif 64 | #ifndef INT32_MIN 65 | #define INT32_MIN (-2147483647-1) 66 | #endif 67 | #ifndef INT8_MAX 68 | #define INT8_MAX (127) 69 | #endif 70 | #ifndef INT16_MAX 71 | #define INT16_MAX (32767) 72 | #endif 73 | #ifndef INT32_MAX 74 | #define INT32_MAX (2147483647) 75 | #endif 76 | #ifndef UINT8_MAX 77 | #define UINT8_MAX (255U) 78 | #endif 79 | #ifndef UINT16_MAX 80 | #define UINT16_MAX (65535U) 81 | #endif 82 | #ifndef UINT32_MAX 83 | #define UINT32_MAX (4294967295U) 84 | #endif 85 | 86 | #ifndef SIZE_MAX 87 | #define SIZE_MAX (~(size_t)0) 88 | #endif 89 | 90 | #endif /* ! C99 */ 91 | 92 | #endif /* ! FLEXINT_H */ 93 | 94 | /* begin standard C++ headers. */ 95 | 96 | /* TODO: this is always defined, so inline it */ 97 | #define yyconst const 98 | 99 | #if defined(__GNUC__) && __GNUC__ >= 3 100 | #define yynoreturn __attribute__((__noreturn__)) 101 | #else 102 | #define yynoreturn 103 | #endif 104 | 105 | /* Returned upon end-of-file. */ 106 | #define YY_NULL 0 107 | 108 | /* Promotes a possibly negative, possibly signed char to an 109 | * integer in range [0..255] for use as an array index. 110 | */ 111 | #define YY_SC_TO_UI(c) ((YY_CHAR) (c)) 112 | 113 | /* Enter a start condition. This macro really ought to take a parameter, 114 | * but we do it the disgusting crufty way forced on us by the ()-less 115 | * definition of BEGIN. 116 | */ 117 | #define BEGIN (yy_start) = 1 + 2 * 118 | /* Translate the current start state into a value that can be later handed 119 | * to BEGIN to return to the state. The YYSTATE alias is for lex 120 | * compatibility. 121 | */ 122 | #define YY_START (((yy_start) - 1) / 2) 123 | #define YYSTATE YY_START 124 | /* Action number for EOF rule of a given start state. */ 125 | #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) 126 | /* Special action meaning "start processing a new file". */ 127 | #define YY_NEW_FILE yyrestart( yyin ) 128 | #define YY_END_OF_BUFFER_CHAR 0 129 | 130 | /* Size of default input buffer. */ 131 | #ifndef YY_BUF_SIZE 132 | #ifdef __ia64__ 133 | /* On IA-64, the buffer size is 16k, not 8k. 134 | * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. 135 | * Ditto for the __ia64__ case accordingly. 136 | */ 137 | #define YY_BUF_SIZE 32768 138 | #else 139 | #define YY_BUF_SIZE 16384 140 | #endif /* __ia64__ */ 141 | #endif 142 | 143 | /* The state buf must be large enough to hold one state per character in the main buffer. 144 | */ 145 | #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) 146 | 147 | #ifndef YY_TYPEDEF_YY_BUFFER_STATE 148 | #define YY_TYPEDEF_YY_BUFFER_STATE 149 | typedef struct yy_buffer_state *YY_BUFFER_STATE; 150 | #endif 151 | 152 | #ifndef YY_TYPEDEF_YY_SIZE_T 153 | #define YY_TYPEDEF_YY_SIZE_T 154 | typedef size_t yy_size_t; 155 | #endif 156 | 157 | extern int yyleng; 158 | 159 | extern FILE *yyin, *yyout; 160 | 161 | #define EOB_ACT_CONTINUE_SCAN 0 162 | #define EOB_ACT_END_OF_FILE 1 163 | #define EOB_ACT_LAST_MATCH 2 164 | 165 | #define YY_LESS_LINENO(n) 166 | #define YY_LINENO_REWIND_TO(ptr) 167 | 168 | /* Return all but the first "n" matched characters back to the input stream. */ 169 | #define yyless(n) \ 170 | do \ 171 | { \ 172 | /* Undo effects of setting up yytext. */ \ 173 | int yyless_macro_arg = (n); \ 174 | YY_LESS_LINENO(yyless_macro_arg);\ 175 | *yy_cp = (yy_hold_char); \ 176 | YY_RESTORE_YY_MORE_OFFSET \ 177 | (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ 178 | YY_DO_BEFORE_ACTION; /* set up yytext again */ \ 179 | } \ 180 | while ( 0 ) 181 | #define unput(c) yyunput( c, (yytext_ptr) ) 182 | 183 | #ifndef YY_STRUCT_YY_BUFFER_STATE 184 | #define YY_STRUCT_YY_BUFFER_STATE 185 | struct yy_buffer_state 186 | { 187 | FILE *yy_input_file; 188 | 189 | char *yy_ch_buf; /* input buffer */ 190 | char *yy_buf_pos; /* current position in input buffer */ 191 | 192 | /* Size of input buffer in bytes, not including room for EOB 193 | * characters. 194 | */ 195 | int yy_buf_size; 196 | 197 | /* Number of characters read into yy_ch_buf, not including EOB 198 | * characters. 199 | */ 200 | int yy_n_chars; 201 | 202 | /* Whether we "own" the buffer - i.e., we know we created it, 203 | * and can realloc() it to grow it, and should free() it to 204 | * delete it. 205 | */ 206 | int yy_is_our_buffer; 207 | 208 | /* Whether this is an "interactive" input source; if so, and 209 | * if we're using stdio for input, then we want to use getc() 210 | * instead of fread(), to make sure we stop fetching input after 211 | * each newline. 212 | */ 213 | int yy_is_interactive; 214 | 215 | /* Whether we're considered to be at the beginning of a line. 216 | * If so, '^' rules will be active on the next match, otherwise 217 | * not. 218 | */ 219 | int yy_at_bol; 220 | 221 | int yy_bs_lineno; /**< The line count. */ 222 | int yy_bs_column; /**< The column count. */ 223 | 224 | /* Whether to try to fill the input buffer when we reach the 225 | * end of it. 226 | */ 227 | int yy_fill_buffer; 228 | 229 | int yy_buffer_status; 230 | 231 | #define YY_BUFFER_NEW 0 232 | #define YY_BUFFER_NORMAL 1 233 | /* When an EOF's been seen but there's still some text to process 234 | * then we mark the buffer as YY_EOF_PENDING, to indicate that we 235 | * shouldn't try reading from the input source any more. We might 236 | * still have a bunch of tokens to match, though, because of 237 | * possible backing-up. 238 | * 239 | * When we actually see the EOF, we change the status to "new" 240 | * (via yyrestart()), so that the user can continue scanning by 241 | * just pointing yyin at a new input file. 242 | */ 243 | #define YY_BUFFER_EOF_PENDING 2 244 | 245 | }; 246 | #endif /* !YY_STRUCT_YY_BUFFER_STATE */ 247 | 248 | /* Stack of input buffers. */ 249 | static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ 250 | static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ 251 | static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ 252 | 253 | /* We provide macros for accessing buffer states in case in the 254 | * future we want to put the buffer states in a more general 255 | * "scanner state". 256 | * 257 | * Returns the top of the stack, or NULL. 258 | */ 259 | #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ 260 | ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ 261 | : NULL) 262 | /* Same as previous macro, but useful when we know that the buffer stack is not 263 | * NULL or when we need an lvalue. For internal use only. 264 | */ 265 | #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] 266 | 267 | /* yy_hold_char holds the character lost when yytext is formed. */ 268 | static char yy_hold_char; 269 | static int yy_n_chars; /* number of characters read into yy_ch_buf */ 270 | int yyleng; 271 | 272 | /* Points to current character in buffer. */ 273 | static char *yy_c_buf_p = NULL; 274 | static int yy_init = 0; /* whether we need to initialize */ 275 | static int yy_start = 0; /* start state number */ 276 | 277 | /* Flag which is used to allow yywrap()'s to do buffer switches 278 | * instead of setting up a fresh yyin. A bit of a hack ... 279 | */ 280 | static int yy_did_buffer_switch_on_eof; 281 | 282 | void yyrestart ( FILE *input_file ); 283 | void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); 284 | YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); 285 | void yy_delete_buffer ( YY_BUFFER_STATE b ); 286 | void yy_flush_buffer ( YY_BUFFER_STATE b ); 287 | void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); 288 | void yypop_buffer_state ( void ); 289 | 290 | static void yyensure_buffer_stack ( void ); 291 | static void yy_load_buffer_state ( void ); 292 | static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); 293 | #define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) 294 | 295 | YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); 296 | YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); 297 | YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); 298 | 299 | void *yyalloc ( yy_size_t ); 300 | void *yyrealloc ( void *, yy_size_t ); 301 | void yyfree ( void * ); 302 | 303 | #define yy_new_buffer yy_create_buffer 304 | #define yy_set_interactive(is_interactive) \ 305 | { \ 306 | if ( ! YY_CURRENT_BUFFER ){ \ 307 | yyensure_buffer_stack (); \ 308 | YY_CURRENT_BUFFER_LVALUE = \ 309 | yy_create_buffer( yyin, YY_BUF_SIZE ); \ 310 | } \ 311 | YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ 312 | } 313 | #define yy_set_bol(at_bol) \ 314 | { \ 315 | if ( ! YY_CURRENT_BUFFER ){\ 316 | yyensure_buffer_stack (); \ 317 | YY_CURRENT_BUFFER_LVALUE = \ 318 | yy_create_buffer( yyin, YY_BUF_SIZE ); \ 319 | } \ 320 | YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ 321 | } 322 | #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) 323 | 324 | /* Begin user sect3 */ 325 | typedef flex_uint8_t YY_CHAR; 326 | 327 | FILE *yyin = NULL, *yyout = NULL; 328 | 329 | typedef int yy_state_type; 330 | 331 | extern int yylineno; 332 | int yylineno = 1; 333 | 334 | extern char *yytext; 335 | #ifdef yytext_ptr 336 | #undef yytext_ptr 337 | #endif 338 | #define yytext_ptr yytext 339 | 340 | static yy_state_type yy_get_previous_state ( void ); 341 | static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); 342 | static int yy_get_next_buffer ( void ); 343 | static void yynoreturn yy_fatal_error ( const char* msg ); 344 | 345 | /* Done after the current pattern has been matched and before the 346 | * corresponding action - sets up yytext. 347 | */ 348 | #define YY_DO_BEFORE_ACTION \ 349 | (yytext_ptr) = yy_bp; \ 350 | yyleng = (int) (yy_cp - yy_bp); \ 351 | (yy_hold_char) = *yy_cp; \ 352 | *yy_cp = '\0'; \ 353 | (yy_c_buf_p) = yy_cp; 354 | #define YY_NUM_RULES 17 355 | #define YY_END_OF_BUFFER 18 356 | /* This struct is not used in this scanner, 357 | but its presence is necessary. */ 358 | struct yy_trans_info 359 | { 360 | flex_int32_t yy_verify; 361 | flex_int32_t yy_nxt; 362 | }; 363 | static const flex_int16_t yy_accept[34] = 364 | { 0, 365 | 0, 0, 18, 16, 1, 1, 16, 16, 15, 15, 366 | 2, 8, 12, 10, 7, 16, 13, 0, 5, 2, 367 | 0, 9, 14, 11, 7, 0, 0, 6, 3, 0, 368 | 0, 4, 0 369 | } ; 370 | 371 | static const YY_CHAR yy_ec[256] = 372 | { 0, 373 | 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 374 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 375 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 376 | 1, 2, 4, 5, 1, 1, 1, 6, 6, 6, 377 | 6, 6, 7, 6, 7, 8, 6, 9, 9, 9, 378 | 9, 9, 9, 9, 9, 9, 9, 6, 6, 10, 379 | 11, 12, 1, 1, 13, 13, 13, 13, 14, 13, 380 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 381 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 382 | 1, 1, 1, 1, 15, 16, 13, 13, 13, 13, 383 | 384 | 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 385 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 386 | 13, 13, 1, 6, 1, 1, 1, 1, 1, 1, 387 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 388 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 389 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 390 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 391 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 392 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 393 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 394 | 395 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 396 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 397 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 398 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 399 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 400 | 1, 1, 1, 1, 1 401 | } ; 402 | 403 | static const YY_CHAR yy_meta[17] = 404 | { 0, 405 | 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 406 | 1, 1, 1, 1, 1, 1 407 | } ; 408 | 409 | static const flex_int16_t yy_base[36] = 410 | { 0, 411 | 0, 0, 49, 50, 50, 50, 37, 42, 50, 37, 412 | 9, 8, 50, 34, 12, 17, 50, 39, 38, 15, 413 | 33, 50, 50, 50, 0, 23, 36, 24, 21, 25, 414 | 29, 28, 50, 35, 30 415 | } ; 416 | 417 | static const flex_int16_t yy_def[36] = 418 | { 0, 419 | 33, 1, 33, 33, 33, 33, 33, 34, 33, 33, 420 | 33, 33, 33, 33, 33, 35, 33, 34, 33, 33, 421 | 33, 33, 33, 33, 15, 35, 33, 35, 33, 33, 422 | 33, 33, 0, 33, 33 423 | } ; 424 | 425 | static const flex_int16_t yy_nxt[67] = 426 | { 0, 427 | 4, 5, 6, 7, 8, 9, 10, 9, 11, 12, 428 | 13, 14, 15, 15, 4, 16, 21, 20, 22, 23, 429 | 25, 27, 21, 20, 25, 25, 25, 27, 27, 29, 430 | 26, 31, 28, 32, 30, 18, 32, 32, 28, 28, 431 | 26, 29, 18, 19, 24, 20, 19, 17, 33, 3, 432 | 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 433 | 33, 33, 33, 33, 33, 33 434 | } ; 435 | 436 | static const flex_int16_t yy_chk[67] = 437 | { 0, 438 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 439 | 1, 1, 1, 1, 1, 1, 11, 11, 12, 12, 440 | 15, 16, 20, 20, 15, 15, 15, 26, 28, 29, 441 | 35, 30, 16, 30, 29, 34, 32, 31, 26, 28, 442 | 27, 21, 19, 18, 14, 10, 8, 7, 3, 33, 443 | 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 444 | 33, 33, 33, 33, 33, 33 445 | } ; 446 | 447 | static yy_state_type yy_last_accepting_state; 448 | static char *yy_last_accepting_cpos; 449 | 450 | extern int yy_flex_debug; 451 | int yy_flex_debug = 0; 452 | 453 | /* The intent behind this definition is that it'll catch 454 | * any uses of REJECT which flex missed. 455 | */ 456 | #define REJECT reject_used_but_not_detected 457 | #define yymore() yymore_used_but_not_detected 458 | #define YY_MORE_ADJ 0 459 | #define YY_RESTORE_YY_MORE_OFFSET 460 | char *yytext; 461 | #line 1 "scan.l" 462 | #line 2 "scan.l" 463 | /* 464 | * scan.l 465 | * flex scanner file for SQL interpreter 466 | * 467 | * write in C++ 468 | * Author: yzy 469 | */ 470 | 471 | #include "parser_public.h" 472 | #include "y.tab.h" 473 | 474 | #include 475 | #include 476 | #include 477 | #include 478 | #include 479 | 480 | #line 480 "lex.yy.c" 481 | #line 481 "lex.yy.c" 482 | 483 | #define INITIAL 0 484 | 485 | #ifndef YY_NO_UNISTD_H 486 | /* Special case for "unistd.h", since it is non-ANSI. We include it way 487 | * down here because we want the user's section 1 to have been scanned first. 488 | * The user has a chance to override it with an option. 489 | */ 490 | #include 491 | #endif 492 | 493 | #ifndef YY_EXTRA_TYPE 494 | #define YY_EXTRA_TYPE void * 495 | #endif 496 | 497 | static int yy_init_globals ( void ); 498 | 499 | /* Accessor methods to globals. 500 | These are made visible to non-reentrant scanners for convenience. */ 501 | 502 | int yylex_destroy ( void ); 503 | 504 | int yyget_debug ( void ); 505 | 506 | void yyset_debug ( int debug_flag ); 507 | 508 | YY_EXTRA_TYPE yyget_extra ( void ); 509 | 510 | void yyset_extra ( YY_EXTRA_TYPE user_defined ); 511 | 512 | FILE *yyget_in ( void ); 513 | 514 | void yyset_in ( FILE * _in_str ); 515 | 516 | FILE *yyget_out ( void ); 517 | 518 | void yyset_out ( FILE * _out_str ); 519 | 520 | int yyget_leng ( void ); 521 | 522 | char *yyget_text ( void ); 523 | 524 | int yyget_lineno ( void ); 525 | 526 | void yyset_lineno ( int _line_number ); 527 | 528 | /* Macros after this point can all be overridden by user definitions in 529 | * section 1. 530 | */ 531 | 532 | #ifndef YY_SKIP_YYWRAP 533 | #ifdef __cplusplus 534 | extern "C" int yywrap ( void ); 535 | #else 536 | extern int yywrap ( void ); 537 | #endif 538 | #endif 539 | 540 | #ifndef YY_NO_UNPUT 541 | 542 | static void yyunput ( int c, char *buf_ptr ); 543 | 544 | #endif 545 | 546 | #ifndef yytext_ptr 547 | static void yy_flex_strncpy ( char *, const char *, int ); 548 | #endif 549 | 550 | #ifdef YY_NEED_STRLEN 551 | static int yy_flex_strlen ( const char * ); 552 | #endif 553 | 554 | #ifndef YY_NO_INPUT 555 | #ifdef __cplusplus 556 | static int yyinput ( void ); 557 | #else 558 | static int input ( void ); 559 | #endif 560 | 561 | #endif 562 | 563 | /* Amount of stuff to slurp up with each read. */ 564 | #ifndef YY_READ_BUF_SIZE 565 | #ifdef __ia64__ 566 | /* On IA-64, the buffer size is 16k, not 8k */ 567 | #define YY_READ_BUF_SIZE 16384 568 | #else 569 | #define YY_READ_BUF_SIZE 8192 570 | #endif /* __ia64__ */ 571 | #endif 572 | 573 | /* Copy whatever the last rule matched to the standard output. */ 574 | #ifndef ECHO 575 | /* This used to be an fputs(), but since the string might contain NUL's, 576 | * we now use fwrite(). 577 | */ 578 | #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) 579 | #endif 580 | 581 | /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, 582 | * is returned in "result". 583 | */ 584 | #ifndef YY_INPUT 585 | #define YY_INPUT(buf,result,max_size) \ 586 | if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ 587 | { \ 588 | int c = '*'; \ 589 | int n; \ 590 | for ( n = 0; n < max_size && \ 591 | (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ 592 | buf[n] = (char) c; \ 593 | if ( c == '\n' ) \ 594 | buf[n++] = (char) c; \ 595 | if ( c == EOF && ferror( yyin ) ) \ 596 | YY_FATAL_ERROR( "input in flex scanner failed" ); \ 597 | result = n; \ 598 | } \ 599 | else \ 600 | { \ 601 | errno=0; \ 602 | while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ 603 | { \ 604 | if( errno != EINTR) \ 605 | { \ 606 | YY_FATAL_ERROR( "input in flex scanner failed" ); \ 607 | break; \ 608 | } \ 609 | errno=0; \ 610 | clearerr(yyin); \ 611 | } \ 612 | }\ 613 | \ 614 | 615 | #endif 616 | 617 | /* No semi-colon after return; correct usage is to write "yyterminate();" - 618 | * we don't want an extra ';' after the "return" because that will cause 619 | * some compilers to complain about unreachable statements. 620 | */ 621 | #ifndef yyterminate 622 | #define yyterminate() return YY_NULL 623 | #endif 624 | 625 | /* Number of entries by which start-condition stack grows. */ 626 | #ifndef YY_START_STACK_INCR 627 | #define YY_START_STACK_INCR 25 628 | #endif 629 | 630 | /* Report a fatal error. */ 631 | #ifndef YY_FATAL_ERROR 632 | #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) 633 | #endif 634 | 635 | /* end tables serialization structures and prototypes */ 636 | 637 | /* Default declaration of generated scanner - a define so the user can 638 | * easily add parameters. 639 | */ 640 | #ifndef YY_DECL 641 | #define YY_DECL_IS_OURS 1 642 | 643 | extern int yylex (void); 644 | 645 | #define YY_DECL int yylex (void) 646 | #endif /* !YY_DECL */ 647 | 648 | /* Code executed at the beginning of each rule, after yytext and yyleng 649 | * have been set up. 650 | */ 651 | #ifndef YY_USER_ACTION 652 | #define YY_USER_ACTION 653 | #endif 654 | 655 | /* Code executed at the end of each rule. */ 656 | #ifndef YY_BREAK 657 | #define YY_BREAK /*LINTED*/break; 658 | #endif 659 | 660 | #define YY_RULE_SETUP \ 661 | YY_USER_ACTION 662 | 663 | /** The main scanner function which does all the work. 664 | */ 665 | YY_DECL 666 | { 667 | yy_state_type yy_current_state; 668 | char *yy_cp, *yy_bp; 669 | int yy_act; 670 | 671 | if ( !(yy_init) ) 672 | { 673 | (yy_init) = 1; 674 | 675 | #ifdef YY_USER_INIT 676 | YY_USER_INIT; 677 | #endif 678 | 679 | if ( ! (yy_start) ) 680 | (yy_start) = 1; /* first start state */ 681 | 682 | if ( ! yyin ) 683 | yyin = stdin; 684 | 685 | if ( ! yyout ) 686 | yyout = stdout; 687 | 688 | if ( ! YY_CURRENT_BUFFER ) { 689 | yyensure_buffer_stack (); 690 | YY_CURRENT_BUFFER_LVALUE = 691 | yy_create_buffer( yyin, YY_BUF_SIZE ); 692 | } 693 | 694 | yy_load_buffer_state( ); 695 | } 696 | 697 | { 698 | #line 27 "scan.l" 699 | 700 | 701 | #line 701 "lex.yy.c" 702 | 703 | while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ 704 | { 705 | yy_cp = (yy_c_buf_p); 706 | 707 | /* Support of yytext. */ 708 | *yy_cp = (yy_hold_char); 709 | 710 | /* yy_bp points to the position in yy_ch_buf of the start of 711 | * the current run. 712 | */ 713 | yy_bp = yy_cp; 714 | 715 | yy_current_state = (yy_start); 716 | yy_match: 717 | do 718 | { 719 | YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; 720 | if ( yy_accept[yy_current_state] ) 721 | { 722 | (yy_last_accepting_state) = yy_current_state; 723 | (yy_last_accepting_cpos) = yy_cp; 724 | } 725 | while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) 726 | { 727 | yy_current_state = (int) yy_def[yy_current_state]; 728 | if ( yy_current_state >= 34 ) 729 | yy_c = yy_meta[yy_c]; 730 | } 731 | yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; 732 | ++yy_cp; 733 | } 734 | while ( yy_base[yy_current_state] != 50 ); 735 | 736 | yy_find_action: 737 | yy_act = yy_accept[yy_current_state]; 738 | if ( yy_act == 0 ) 739 | { /* have to back up */ 740 | yy_cp = (yy_last_accepting_cpos); 741 | yy_current_state = (yy_last_accepting_state); 742 | yy_act = yy_accept[yy_current_state]; 743 | } 744 | 745 | YY_DO_BEFORE_ACTION; 746 | 747 | do_action: /* This label is used only to access EOF actions. */ 748 | 749 | switch ( yy_act ) 750 | { /* beginning of action switch */ 751 | case 0: /* must back up */ 752 | /* undo the effects of YY_DO_BEFORE_ACTION */ 753 | *yy_cp = (yy_hold_char); 754 | yy_cp = (yy_last_accepting_cpos); 755 | yy_current_state = (yy_last_accepting_state); 756 | goto yy_find_action; 757 | 758 | case 1: 759 | /* rule 1 can match eol */ 760 | YY_RULE_SETUP 761 | #line 29 "scan.l" 762 | {} 763 | YY_BREAK 764 | case 2: 765 | YY_RULE_SETUP 766 | #line 31 "scan.l" 767 | { /* std::cout << "T_INT\n"; */ sscanf(yytext, "%d", &yylval.i); return T_INT; } 768 | YY_BREAK 769 | case 3: 770 | YY_RULE_SETUP 771 | #line 32 "scan.l" 772 | { /* std::cout << "T_REAL\n"; */ sscanf(yytext, "%lf", &yylval.r); return T_REAL; } 773 | YY_BREAK 774 | case 4: 775 | YY_RULE_SETUP 776 | #line 33 "scan.l" 777 | { sscanf(yytext, "%lf", &yylval.r); return T_REAL; } 778 | YY_BREAK 779 | case 5: 780 | YY_RULE_SETUP 781 | #line 34 "scan.l" 782 | { yylval.str = std::string(yytext + 1, yyleng - 2); return T_QSTRING; } 783 | YY_BREAK 784 | case 6: 785 | YY_RULE_SETUP 786 | #line 35 "scan.l" 787 | { yylval.str = std::string(yytext + 1, yyleng - 2); return T_RQSTRING; } 788 | YY_BREAK 789 | case 7: 790 | YY_RULE_SETUP 791 | #line 37 "scan.l" 792 | { return get_id(yylval.str = std::string(yytext)); } 793 | YY_BREAK 794 | case 8: 795 | YY_RULE_SETUP 796 | #line 39 "scan.l" 797 | {return T_LT;} 798 | YY_BREAK 799 | case 9: 800 | YY_RULE_SETUP 801 | #line 40 "scan.l" 802 | {return T_LE;} 803 | YY_BREAK 804 | case 10: 805 | YY_RULE_SETUP 806 | #line 41 "scan.l" 807 | {return T_GT;} 808 | YY_BREAK 809 | case 11: 810 | YY_RULE_SETUP 811 | #line 42 "scan.l" 812 | {return T_GE;} 813 | YY_BREAK 814 | case 12: 815 | YY_RULE_SETUP 816 | #line 43 "scan.l" 817 | {return T_EQ;} 818 | YY_BREAK 819 | case 13: 820 | YY_RULE_SETUP 821 | #line 44 "scan.l" 822 | {return T_NE;} 823 | YY_BREAK 824 | case 14: 825 | YY_RULE_SETUP 826 | #line 45 "scan.l" 827 | {return T_NE;} 828 | YY_BREAK 829 | case 15: 830 | YY_RULE_SETUP 831 | #line 47 "scan.l" 832 | {return yytext[0];} 833 | YY_BREAK 834 | case YY_STATE_EOF(INITIAL): 835 | #line 48 "scan.l" 836 | {return T_EOF;} 837 | YY_BREAK 838 | case 16: 839 | YY_RULE_SETUP 840 | #line 50 "scan.l" 841 | { std::cerr << "illegal character [" << yytext[0] << "]\n"; } 842 | YY_BREAK 843 | case 17: 844 | YY_RULE_SETUP 845 | #line 52 "scan.l" 846 | ECHO; 847 | YY_BREAK 848 | #line 848 "lex.yy.c" 849 | 850 | case YY_END_OF_BUFFER: 851 | { 852 | /* Amount of text matched not including the EOB char. */ 853 | int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; 854 | 855 | /* Undo the effects of YY_DO_BEFORE_ACTION. */ 856 | *yy_cp = (yy_hold_char); 857 | YY_RESTORE_YY_MORE_OFFSET 858 | 859 | if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) 860 | { 861 | /* We're scanning a new file or input source. It's 862 | * possible that this happened because the user 863 | * just pointed yyin at a new source and called 864 | * yylex(). If so, then we have to assure 865 | * consistency between YY_CURRENT_BUFFER and our 866 | * globals. Here is the right place to do so, because 867 | * this is the first action (other than possibly a 868 | * back-up) that will match for the new input source. 869 | */ 870 | (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; 871 | YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; 872 | YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; 873 | } 874 | 875 | /* Note that here we test for yy_c_buf_p "<=" to the position 876 | * of the first EOB in the buffer, since yy_c_buf_p will 877 | * already have been incremented past the NUL character 878 | * (since all states make transitions on EOB to the 879 | * end-of-buffer state). Contrast this with the test 880 | * in input(). 881 | */ 882 | if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) 883 | { /* This was really a NUL. */ 884 | yy_state_type yy_next_state; 885 | 886 | (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; 887 | 888 | yy_current_state = yy_get_previous_state( ); 889 | 890 | /* Okay, we're now positioned to make the NUL 891 | * transition. We couldn't have 892 | * yy_get_previous_state() go ahead and do it 893 | * for us because it doesn't know how to deal 894 | * with the possibility of jamming (and we don't 895 | * want to build jamming into it because then it 896 | * will run more slowly). 897 | */ 898 | 899 | yy_next_state = yy_try_NUL_trans( yy_current_state ); 900 | 901 | yy_bp = (yytext_ptr) + YY_MORE_ADJ; 902 | 903 | if ( yy_next_state ) 904 | { 905 | /* Consume the NUL. */ 906 | yy_cp = ++(yy_c_buf_p); 907 | yy_current_state = yy_next_state; 908 | goto yy_match; 909 | } 910 | 911 | else 912 | { 913 | yy_cp = (yy_c_buf_p); 914 | goto yy_find_action; 915 | } 916 | } 917 | 918 | else switch ( yy_get_next_buffer( ) ) 919 | { 920 | case EOB_ACT_END_OF_FILE: 921 | { 922 | (yy_did_buffer_switch_on_eof) = 0; 923 | 924 | if ( yywrap( ) ) 925 | { 926 | /* Note: because we've taken care in 927 | * yy_get_next_buffer() to have set up 928 | * yytext, we can now set up 929 | * yy_c_buf_p so that if some total 930 | * hoser (like flex itself) wants to 931 | * call the scanner after we return the 932 | * YY_NULL, it'll still work - another 933 | * YY_NULL will get returned. 934 | */ 935 | (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; 936 | 937 | yy_act = YY_STATE_EOF(YY_START); 938 | goto do_action; 939 | } 940 | 941 | else 942 | { 943 | if ( ! (yy_did_buffer_switch_on_eof) ) 944 | YY_NEW_FILE; 945 | } 946 | break; 947 | } 948 | 949 | case EOB_ACT_CONTINUE_SCAN: 950 | (yy_c_buf_p) = 951 | (yytext_ptr) + yy_amount_of_matched_text; 952 | 953 | yy_current_state = yy_get_previous_state( ); 954 | 955 | yy_cp = (yy_c_buf_p); 956 | yy_bp = (yytext_ptr) + YY_MORE_ADJ; 957 | goto yy_match; 958 | 959 | case EOB_ACT_LAST_MATCH: 960 | (yy_c_buf_p) = 961 | &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; 962 | 963 | yy_current_state = yy_get_previous_state( ); 964 | 965 | yy_cp = (yy_c_buf_p); 966 | yy_bp = (yytext_ptr) + YY_MORE_ADJ; 967 | goto yy_find_action; 968 | } 969 | break; 970 | } 971 | 972 | default: 973 | YY_FATAL_ERROR( 974 | "fatal flex scanner internal error--no action found" ); 975 | } /* end of action switch */ 976 | } /* end of scanning one token */ 977 | } /* end of user's declarations */ 978 | } /* end of yylex */ 979 | 980 | /* yy_get_next_buffer - try to read in a new buffer 981 | * 982 | * Returns a code representing an action: 983 | * EOB_ACT_LAST_MATCH - 984 | * EOB_ACT_CONTINUE_SCAN - continue scanning from current position 985 | * EOB_ACT_END_OF_FILE - end of file 986 | */ 987 | static int yy_get_next_buffer (void) 988 | { 989 | char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; 990 | char *source = (yytext_ptr); 991 | int number_to_move, i; 992 | int ret_val; 993 | 994 | if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) 995 | YY_FATAL_ERROR( 996 | "fatal flex scanner internal error--end of buffer missed" ); 997 | 998 | if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) 999 | { /* Don't try to fill the buffer, so this is an EOF. */ 1000 | if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) 1001 | { 1002 | /* We matched a single character, the EOB, so 1003 | * treat this as a final EOF. 1004 | */ 1005 | return EOB_ACT_END_OF_FILE; 1006 | } 1007 | 1008 | else 1009 | { 1010 | /* We matched some text prior to the EOB, first 1011 | * process it. 1012 | */ 1013 | return EOB_ACT_LAST_MATCH; 1014 | } 1015 | } 1016 | 1017 | /* Try to read more data. */ 1018 | 1019 | /* First move last chars to start of buffer. */ 1020 | number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); 1021 | 1022 | for ( i = 0; i < number_to_move; ++i ) 1023 | *(dest++) = *(source++); 1024 | 1025 | if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) 1026 | /* don't do the read, it's not guaranteed to return an EOF, 1027 | * just force an EOF 1028 | */ 1029 | YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; 1030 | 1031 | else 1032 | { 1033 | int num_to_read = 1034 | YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; 1035 | 1036 | while ( num_to_read <= 0 ) 1037 | { /* Not enough room in the buffer - grow it. */ 1038 | 1039 | /* just a shorter name for the current buffer */ 1040 | YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; 1041 | 1042 | int yy_c_buf_p_offset = 1043 | (int) ((yy_c_buf_p) - b->yy_ch_buf); 1044 | 1045 | if ( b->yy_is_our_buffer ) 1046 | { 1047 | int new_size = b->yy_buf_size * 2; 1048 | 1049 | if ( new_size <= 0 ) 1050 | b->yy_buf_size += b->yy_buf_size / 8; 1051 | else 1052 | b->yy_buf_size *= 2; 1053 | 1054 | b->yy_ch_buf = (char *) 1055 | /* Include room in for 2 EOB chars. */ 1056 | yyrealloc( (void *) b->yy_ch_buf, 1057 | (yy_size_t) (b->yy_buf_size + 2) ); 1058 | } 1059 | else 1060 | /* Can't grow it, we don't own it. */ 1061 | b->yy_ch_buf = NULL; 1062 | 1063 | if ( ! b->yy_ch_buf ) 1064 | YY_FATAL_ERROR( 1065 | "fatal error - scanner input buffer overflow" ); 1066 | 1067 | (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; 1068 | 1069 | num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - 1070 | number_to_move - 1; 1071 | 1072 | } 1073 | 1074 | if ( num_to_read > YY_READ_BUF_SIZE ) 1075 | num_to_read = YY_READ_BUF_SIZE; 1076 | 1077 | /* Read in more data. */ 1078 | YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), 1079 | (yy_n_chars), num_to_read ); 1080 | 1081 | YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); 1082 | } 1083 | 1084 | if ( (yy_n_chars) == 0 ) 1085 | { 1086 | if ( number_to_move == YY_MORE_ADJ ) 1087 | { 1088 | ret_val = EOB_ACT_END_OF_FILE; 1089 | yyrestart( yyin ); 1090 | } 1091 | 1092 | else 1093 | { 1094 | ret_val = EOB_ACT_LAST_MATCH; 1095 | YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = 1096 | YY_BUFFER_EOF_PENDING; 1097 | } 1098 | } 1099 | 1100 | else 1101 | ret_val = EOB_ACT_CONTINUE_SCAN; 1102 | 1103 | if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { 1104 | /* Extend the array by 50%, plus the number we really need. */ 1105 | int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); 1106 | YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( 1107 | (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); 1108 | if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) 1109 | YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); 1110 | /* "- 2" to take care of EOB's */ 1111 | YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); 1112 | } 1113 | 1114 | (yy_n_chars) += number_to_move; 1115 | YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; 1116 | YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; 1117 | 1118 | (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; 1119 | 1120 | return ret_val; 1121 | } 1122 | 1123 | /* yy_get_previous_state - get the state just before the EOB char was reached */ 1124 | 1125 | static yy_state_type yy_get_previous_state (void) 1126 | { 1127 | yy_state_type yy_current_state; 1128 | char *yy_cp; 1129 | 1130 | yy_current_state = (yy_start); 1131 | 1132 | for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) 1133 | { 1134 | YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); 1135 | if ( yy_accept[yy_current_state] ) 1136 | { 1137 | (yy_last_accepting_state) = yy_current_state; 1138 | (yy_last_accepting_cpos) = yy_cp; 1139 | } 1140 | while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) 1141 | { 1142 | yy_current_state = (int) yy_def[yy_current_state]; 1143 | if ( yy_current_state >= 34 ) 1144 | yy_c = yy_meta[yy_c]; 1145 | } 1146 | yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; 1147 | } 1148 | 1149 | return yy_current_state; 1150 | } 1151 | 1152 | /* yy_try_NUL_trans - try to make a transition on the NUL character 1153 | * 1154 | * synopsis 1155 | * next_state = yy_try_NUL_trans( current_state ); 1156 | */ 1157 | static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) 1158 | { 1159 | int yy_is_jam; 1160 | char *yy_cp = (yy_c_buf_p); 1161 | 1162 | YY_CHAR yy_c = 1; 1163 | if ( yy_accept[yy_current_state] ) 1164 | { 1165 | (yy_last_accepting_state) = yy_current_state; 1166 | (yy_last_accepting_cpos) = yy_cp; 1167 | } 1168 | while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) 1169 | { 1170 | yy_current_state = (int) yy_def[yy_current_state]; 1171 | if ( yy_current_state >= 34 ) 1172 | yy_c = yy_meta[yy_c]; 1173 | } 1174 | yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; 1175 | yy_is_jam = (yy_current_state == 33); 1176 | 1177 | return yy_is_jam ? 0 : yy_current_state; 1178 | } 1179 | 1180 | #ifndef YY_NO_UNPUT 1181 | 1182 | static void yyunput (int c, char * yy_bp ) 1183 | { 1184 | char *yy_cp; 1185 | 1186 | yy_cp = (yy_c_buf_p); 1187 | 1188 | /* undo effects of setting up yytext */ 1189 | *yy_cp = (yy_hold_char); 1190 | 1191 | if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) 1192 | { /* need to shift things up to make room */ 1193 | /* +2 for EOB chars. */ 1194 | int number_to_move = (yy_n_chars) + 2; 1195 | char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ 1196 | YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; 1197 | char *source = 1198 | &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; 1199 | 1200 | while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) 1201 | *--dest = *--source; 1202 | 1203 | yy_cp += (int) (dest - source); 1204 | yy_bp += (int) (dest - source); 1205 | YY_CURRENT_BUFFER_LVALUE->yy_n_chars = 1206 | (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; 1207 | 1208 | if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) 1209 | YY_FATAL_ERROR( "flex scanner push-back overflow" ); 1210 | } 1211 | 1212 | *--yy_cp = (char) c; 1213 | 1214 | (yytext_ptr) = yy_bp; 1215 | (yy_hold_char) = *yy_cp; 1216 | (yy_c_buf_p) = yy_cp; 1217 | } 1218 | 1219 | #endif 1220 | 1221 | #ifndef YY_NO_INPUT 1222 | #ifdef __cplusplus 1223 | static int yyinput (void) 1224 | #else 1225 | static int input (void) 1226 | #endif 1227 | 1228 | { 1229 | int c; 1230 | 1231 | *(yy_c_buf_p) = (yy_hold_char); 1232 | 1233 | if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) 1234 | { 1235 | /* yy_c_buf_p now points to the character we want to return. 1236 | * If this occurs *before* the EOB characters, then it's a 1237 | * valid NUL; if not, then we've hit the end of the buffer. 1238 | */ 1239 | if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) 1240 | /* This was really a NUL. */ 1241 | *(yy_c_buf_p) = '\0'; 1242 | 1243 | else 1244 | { /* need more input */ 1245 | int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); 1246 | ++(yy_c_buf_p); 1247 | 1248 | switch ( yy_get_next_buffer( ) ) 1249 | { 1250 | case EOB_ACT_LAST_MATCH: 1251 | /* This happens because yy_g_n_b() 1252 | * sees that we've accumulated a 1253 | * token and flags that we need to 1254 | * try matching the token before 1255 | * proceeding. But for input(), 1256 | * there's no matching to consider. 1257 | * So convert the EOB_ACT_LAST_MATCH 1258 | * to EOB_ACT_END_OF_FILE. 1259 | */ 1260 | 1261 | /* Reset buffer status. */ 1262 | yyrestart( yyin ); 1263 | 1264 | /*FALLTHROUGH*/ 1265 | 1266 | case EOB_ACT_END_OF_FILE: 1267 | { 1268 | if ( yywrap( ) ) 1269 | return 0; 1270 | 1271 | if ( ! (yy_did_buffer_switch_on_eof) ) 1272 | YY_NEW_FILE; 1273 | #ifdef __cplusplus 1274 | return yyinput(); 1275 | #else 1276 | return input(); 1277 | #endif 1278 | } 1279 | 1280 | case EOB_ACT_CONTINUE_SCAN: 1281 | (yy_c_buf_p) = (yytext_ptr) + offset; 1282 | break; 1283 | } 1284 | } 1285 | } 1286 | 1287 | c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ 1288 | *(yy_c_buf_p) = '\0'; /* preserve yytext */ 1289 | (yy_hold_char) = *++(yy_c_buf_p); 1290 | 1291 | return c; 1292 | } 1293 | #endif /* ifndef YY_NO_INPUT */ 1294 | 1295 | /** Immediately switch to a different input stream. 1296 | * @param input_file A readable stream. 1297 | * 1298 | * @note This function does not reset the start condition to @c INITIAL . 1299 | */ 1300 | void yyrestart (FILE * input_file ) 1301 | { 1302 | 1303 | if ( ! YY_CURRENT_BUFFER ){ 1304 | yyensure_buffer_stack (); 1305 | YY_CURRENT_BUFFER_LVALUE = 1306 | yy_create_buffer( yyin, YY_BUF_SIZE ); 1307 | } 1308 | 1309 | yy_init_buffer( YY_CURRENT_BUFFER, input_file ); 1310 | yy_load_buffer_state( ); 1311 | } 1312 | 1313 | /** Switch to a different input buffer. 1314 | * @param new_buffer The new input buffer. 1315 | * 1316 | */ 1317 | void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) 1318 | { 1319 | 1320 | /* TODO. We should be able to replace this entire function body 1321 | * with 1322 | * yypop_buffer_state(); 1323 | * yypush_buffer_state(new_buffer); 1324 | */ 1325 | yyensure_buffer_stack (); 1326 | if ( YY_CURRENT_BUFFER == new_buffer ) 1327 | return; 1328 | 1329 | if ( YY_CURRENT_BUFFER ) 1330 | { 1331 | /* Flush out information for old buffer. */ 1332 | *(yy_c_buf_p) = (yy_hold_char); 1333 | YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); 1334 | YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); 1335 | } 1336 | 1337 | YY_CURRENT_BUFFER_LVALUE = new_buffer; 1338 | yy_load_buffer_state( ); 1339 | 1340 | /* We don't actually know whether we did this switch during 1341 | * EOF (yywrap()) processing, but the only time this flag 1342 | * is looked at is after yywrap() is called, so it's safe 1343 | * to go ahead and always set it. 1344 | */ 1345 | (yy_did_buffer_switch_on_eof) = 1; 1346 | } 1347 | 1348 | static void yy_load_buffer_state (void) 1349 | { 1350 | (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; 1351 | (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; 1352 | yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; 1353 | (yy_hold_char) = *(yy_c_buf_p); 1354 | } 1355 | 1356 | /** Allocate and initialize an input buffer state. 1357 | * @param file A readable stream. 1358 | * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. 1359 | * 1360 | * @return the allocated buffer state. 1361 | */ 1362 | YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) 1363 | { 1364 | YY_BUFFER_STATE b; 1365 | 1366 | b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); 1367 | if ( ! b ) 1368 | YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); 1369 | 1370 | b->yy_buf_size = size; 1371 | 1372 | /* yy_ch_buf has to be 2 characters longer than the size given because 1373 | * we need to put in 2 end-of-buffer characters. 1374 | */ 1375 | b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); 1376 | if ( ! b->yy_ch_buf ) 1377 | YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); 1378 | 1379 | b->yy_is_our_buffer = 1; 1380 | 1381 | yy_init_buffer( b, file ); 1382 | 1383 | return b; 1384 | } 1385 | 1386 | /** Destroy the buffer. 1387 | * @param b a buffer created with yy_create_buffer() 1388 | * 1389 | */ 1390 | void yy_delete_buffer (YY_BUFFER_STATE b ) 1391 | { 1392 | 1393 | if ( ! b ) 1394 | return; 1395 | 1396 | if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ 1397 | YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; 1398 | 1399 | if ( b->yy_is_our_buffer ) 1400 | yyfree( (void *) b->yy_ch_buf ); 1401 | 1402 | yyfree( (void *) b ); 1403 | } 1404 | 1405 | /* Initializes or reinitializes a buffer. 1406 | * This function is sometimes called more than once on the same buffer, 1407 | * such as during a yyrestart() or at EOF. 1408 | */ 1409 | static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) 1410 | 1411 | { 1412 | int oerrno = errno; 1413 | 1414 | yy_flush_buffer( b ); 1415 | 1416 | b->yy_input_file = file; 1417 | b->yy_fill_buffer = 1; 1418 | 1419 | /* If b is the current buffer, then yy_init_buffer was _probably_ 1420 | * called from yyrestart() or through yy_get_next_buffer. 1421 | * In that case, we don't want to reset the lineno or column. 1422 | */ 1423 | if (b != YY_CURRENT_BUFFER){ 1424 | b->yy_bs_lineno = 1; 1425 | b->yy_bs_column = 0; 1426 | } 1427 | 1428 | b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; 1429 | 1430 | errno = oerrno; 1431 | } 1432 | 1433 | /** Discard all buffered characters. On the next scan, YY_INPUT will be called. 1434 | * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. 1435 | * 1436 | */ 1437 | void yy_flush_buffer (YY_BUFFER_STATE b ) 1438 | { 1439 | if ( ! b ) 1440 | return; 1441 | 1442 | b->yy_n_chars = 0; 1443 | 1444 | /* We always need two end-of-buffer characters. The first causes 1445 | * a transition to the end-of-buffer state. The second causes 1446 | * a jam in that state. 1447 | */ 1448 | b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; 1449 | b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; 1450 | 1451 | b->yy_buf_pos = &b->yy_ch_buf[0]; 1452 | 1453 | b->yy_at_bol = 1; 1454 | b->yy_buffer_status = YY_BUFFER_NEW; 1455 | 1456 | if ( b == YY_CURRENT_BUFFER ) 1457 | yy_load_buffer_state( ); 1458 | } 1459 | 1460 | /** Pushes the new state onto the stack. The new state becomes 1461 | * the current state. This function will allocate the stack 1462 | * if necessary. 1463 | * @param new_buffer The new state. 1464 | * 1465 | */ 1466 | void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) 1467 | { 1468 | if (new_buffer == NULL) 1469 | return; 1470 | 1471 | yyensure_buffer_stack(); 1472 | 1473 | /* This block is copied from yy_switch_to_buffer. */ 1474 | if ( YY_CURRENT_BUFFER ) 1475 | { 1476 | /* Flush out information for old buffer. */ 1477 | *(yy_c_buf_p) = (yy_hold_char); 1478 | YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); 1479 | YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); 1480 | } 1481 | 1482 | /* Only push if top exists. Otherwise, replace top. */ 1483 | if (YY_CURRENT_BUFFER) 1484 | (yy_buffer_stack_top)++; 1485 | YY_CURRENT_BUFFER_LVALUE = new_buffer; 1486 | 1487 | /* copied from yy_switch_to_buffer. */ 1488 | yy_load_buffer_state( ); 1489 | (yy_did_buffer_switch_on_eof) = 1; 1490 | } 1491 | 1492 | /** Removes and deletes the top of the stack, if present. 1493 | * The next element becomes the new top. 1494 | * 1495 | */ 1496 | void yypop_buffer_state (void) 1497 | { 1498 | if (!YY_CURRENT_BUFFER) 1499 | return; 1500 | 1501 | yy_delete_buffer(YY_CURRENT_BUFFER ); 1502 | YY_CURRENT_BUFFER_LVALUE = NULL; 1503 | if ((yy_buffer_stack_top) > 0) 1504 | --(yy_buffer_stack_top); 1505 | 1506 | if (YY_CURRENT_BUFFER) { 1507 | yy_load_buffer_state( ); 1508 | (yy_did_buffer_switch_on_eof) = 1; 1509 | } 1510 | } 1511 | 1512 | /* Allocates the stack if it does not exist. 1513 | * Guarantees space for at least one push. 1514 | */ 1515 | static void yyensure_buffer_stack (void) 1516 | { 1517 | yy_size_t num_to_alloc; 1518 | 1519 | if (!(yy_buffer_stack)) { 1520 | 1521 | /* First allocation is just for 2 elements, since we don't know if this 1522 | * scanner will even need a stack. We use 2 instead of 1 to avoid an 1523 | * immediate realloc on the next call. 1524 | */ 1525 | num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ 1526 | (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc 1527 | (num_to_alloc * sizeof(struct yy_buffer_state*) 1528 | ); 1529 | if ( ! (yy_buffer_stack) ) 1530 | YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); 1531 | 1532 | memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); 1533 | 1534 | (yy_buffer_stack_max) = num_to_alloc; 1535 | (yy_buffer_stack_top) = 0; 1536 | return; 1537 | } 1538 | 1539 | if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ 1540 | 1541 | /* Increase the buffer to prepare for a possible push. */ 1542 | yy_size_t grow_size = 8 /* arbitrary grow size */; 1543 | 1544 | num_to_alloc = (yy_buffer_stack_max) + grow_size; 1545 | (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc 1546 | ((yy_buffer_stack), 1547 | num_to_alloc * sizeof(struct yy_buffer_state*) 1548 | ); 1549 | if ( ! (yy_buffer_stack) ) 1550 | YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); 1551 | 1552 | /* zero only the new slots.*/ 1553 | memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); 1554 | (yy_buffer_stack_max) = num_to_alloc; 1555 | } 1556 | } 1557 | 1558 | /** Setup the input buffer state to scan directly from a user-specified character buffer. 1559 | * @param base the character buffer 1560 | * @param size the size in bytes of the character buffer 1561 | * 1562 | * @return the newly allocated buffer state object. 1563 | */ 1564 | YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) 1565 | { 1566 | YY_BUFFER_STATE b; 1567 | 1568 | if ( size < 2 || 1569 | base[size-2] != YY_END_OF_BUFFER_CHAR || 1570 | base[size-1] != YY_END_OF_BUFFER_CHAR ) 1571 | /* They forgot to leave room for the EOB's. */ 1572 | return NULL; 1573 | 1574 | b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); 1575 | if ( ! b ) 1576 | YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); 1577 | 1578 | b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ 1579 | b->yy_buf_pos = b->yy_ch_buf = base; 1580 | b->yy_is_our_buffer = 0; 1581 | b->yy_input_file = NULL; 1582 | b->yy_n_chars = b->yy_buf_size; 1583 | b->yy_is_interactive = 0; 1584 | b->yy_at_bol = 1; 1585 | b->yy_fill_buffer = 0; 1586 | b->yy_buffer_status = YY_BUFFER_NEW; 1587 | 1588 | yy_switch_to_buffer( b ); 1589 | 1590 | return b; 1591 | } 1592 | 1593 | /** Setup the input buffer state to scan a string. The next call to yylex() will 1594 | * scan from a @e copy of @a str. 1595 | * @param yystr a NUL-terminated string to scan 1596 | * 1597 | * @return the newly allocated buffer state object. 1598 | * @note If you want to scan bytes that may contain NUL values, then use 1599 | * yy_scan_bytes() instead. 1600 | */ 1601 | YY_BUFFER_STATE yy_scan_string (const char * yystr ) 1602 | { 1603 | 1604 | return yy_scan_bytes( yystr, (int) strlen(yystr) ); 1605 | } 1606 | 1607 | /** Setup the input buffer state to scan the given bytes. The next call to yylex() will 1608 | * scan from a @e copy of @a bytes. 1609 | * @param yybytes the byte buffer to scan 1610 | * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. 1611 | * 1612 | * @return the newly allocated buffer state object. 1613 | */ 1614 | YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) 1615 | { 1616 | YY_BUFFER_STATE b; 1617 | char *buf; 1618 | yy_size_t n; 1619 | int i; 1620 | 1621 | /* Get memory for full buffer, including space for trailing EOB's. */ 1622 | n = (yy_size_t) (_yybytes_len + 2); 1623 | buf = (char *) yyalloc( n ); 1624 | if ( ! buf ) 1625 | YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); 1626 | 1627 | for ( i = 0; i < _yybytes_len; ++i ) 1628 | buf[i] = yybytes[i]; 1629 | 1630 | buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; 1631 | 1632 | b = yy_scan_buffer( buf, n ); 1633 | if ( ! b ) 1634 | YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); 1635 | 1636 | /* It's okay to grow etc. this buffer, and we should throw it 1637 | * away when we're done. 1638 | */ 1639 | b->yy_is_our_buffer = 1; 1640 | 1641 | return b; 1642 | } 1643 | 1644 | #ifndef YY_EXIT_FAILURE 1645 | #define YY_EXIT_FAILURE 2 1646 | #endif 1647 | 1648 | static void yynoreturn yy_fatal_error (const char* msg ) 1649 | { 1650 | fprintf( stderr, "%s\n", msg ); 1651 | exit( YY_EXIT_FAILURE ); 1652 | } 1653 | 1654 | /* Redefine yyless() so it works in section 3 code. */ 1655 | 1656 | #undef yyless 1657 | #define yyless(n) \ 1658 | do \ 1659 | { \ 1660 | /* Undo effects of setting up yytext. */ \ 1661 | int yyless_macro_arg = (n); \ 1662 | YY_LESS_LINENO(yyless_macro_arg);\ 1663 | yytext[yyleng] = (yy_hold_char); \ 1664 | (yy_c_buf_p) = yytext + yyless_macro_arg; \ 1665 | (yy_hold_char) = *(yy_c_buf_p); \ 1666 | *(yy_c_buf_p) = '\0'; \ 1667 | yyleng = yyless_macro_arg; \ 1668 | } \ 1669 | while ( 0 ) 1670 | 1671 | /* Accessor methods (get/set functions) to struct members. */ 1672 | 1673 | /** Get the current line number. 1674 | * 1675 | */ 1676 | int yyget_lineno (void) 1677 | { 1678 | 1679 | return yylineno; 1680 | } 1681 | 1682 | /** Get the input stream. 1683 | * 1684 | */ 1685 | FILE *yyget_in (void) 1686 | { 1687 | return yyin; 1688 | } 1689 | 1690 | /** Get the output stream. 1691 | * 1692 | */ 1693 | FILE *yyget_out (void) 1694 | { 1695 | return yyout; 1696 | } 1697 | 1698 | /** Get the length of the current token. 1699 | * 1700 | */ 1701 | int yyget_leng (void) 1702 | { 1703 | return yyleng; 1704 | } 1705 | 1706 | /** Get the current token. 1707 | * 1708 | */ 1709 | 1710 | char *yyget_text (void) 1711 | { 1712 | return yytext; 1713 | } 1714 | 1715 | /** Set the current line number. 1716 | * @param _line_number line number 1717 | * 1718 | */ 1719 | void yyset_lineno (int _line_number ) 1720 | { 1721 | 1722 | yylineno = _line_number; 1723 | } 1724 | 1725 | /** Set the input stream. This does not discard the current 1726 | * input buffer. 1727 | * @param _in_str A readable stream. 1728 | * 1729 | * @see yy_switch_to_buffer 1730 | */ 1731 | void yyset_in (FILE * _in_str ) 1732 | { 1733 | yyin = _in_str ; 1734 | } 1735 | 1736 | void yyset_out (FILE * _out_str ) 1737 | { 1738 | yyout = _out_str ; 1739 | } 1740 | 1741 | int yyget_debug (void) 1742 | { 1743 | return yy_flex_debug; 1744 | } 1745 | 1746 | void yyset_debug (int _bdebug ) 1747 | { 1748 | yy_flex_debug = _bdebug ; 1749 | } 1750 | 1751 | static int yy_init_globals (void) 1752 | { 1753 | /* Initialization is the same as for the non-reentrant scanner. 1754 | * This function is called from yylex_destroy(), so don't allocate here. 1755 | */ 1756 | 1757 | (yy_buffer_stack) = NULL; 1758 | (yy_buffer_stack_top) = 0; 1759 | (yy_buffer_stack_max) = 0; 1760 | (yy_c_buf_p) = NULL; 1761 | (yy_init) = 0; 1762 | (yy_start) = 0; 1763 | 1764 | /* Defined in main.c */ 1765 | #ifdef YY_STDINIT 1766 | yyin = stdin; 1767 | yyout = stdout; 1768 | #else 1769 | yyin = NULL; 1770 | yyout = NULL; 1771 | #endif 1772 | 1773 | /* For future reference: Set errno on error, since we are called by 1774 | * yylex_init() 1775 | */ 1776 | return 0; 1777 | } 1778 | 1779 | /* yylex_destroy is for both reentrant and non-reentrant scanners. */ 1780 | int yylex_destroy (void) 1781 | { 1782 | 1783 | /* Pop the buffer stack, destroying each element. */ 1784 | while(YY_CURRENT_BUFFER){ 1785 | yy_delete_buffer( YY_CURRENT_BUFFER ); 1786 | YY_CURRENT_BUFFER_LVALUE = NULL; 1787 | yypop_buffer_state(); 1788 | } 1789 | 1790 | /* Destroy the stack itself. */ 1791 | yyfree((yy_buffer_stack) ); 1792 | (yy_buffer_stack) = NULL; 1793 | 1794 | /* Reset the globals. This is important in a non-reentrant scanner so the next time 1795 | * yylex() is called, initialization will occur. */ 1796 | yy_init_globals( ); 1797 | 1798 | return 0; 1799 | } 1800 | 1801 | /* 1802 | * Internal utility routines. 1803 | */ 1804 | 1805 | #ifndef yytext_ptr 1806 | static void yy_flex_strncpy (char* s1, const char * s2, int n ) 1807 | { 1808 | 1809 | int i; 1810 | for ( i = 0; i < n; ++i ) 1811 | s1[i] = s2[i]; 1812 | } 1813 | #endif 1814 | 1815 | #ifdef YY_NEED_STRLEN 1816 | static int yy_flex_strlen (const char * s ) 1817 | { 1818 | int n; 1819 | for ( n = 0; s[n]; ++n ) 1820 | ; 1821 | 1822 | return n; 1823 | } 1824 | #endif 1825 | 1826 | void *yyalloc (yy_size_t size ) 1827 | { 1828 | return malloc(size); 1829 | } 1830 | 1831 | void *yyrealloc (void * ptr, yy_size_t size ) 1832 | { 1833 | 1834 | /* The cast to (char *) in the following accommodates both 1835 | * implementations that use char* generic pointers, and those 1836 | * that use void* generic pointers. It works with the latter 1837 | * because both ANSI C and C++ allow castless assignment from 1838 | * any pointer type to void*, and deal with argument conversions 1839 | * as though doing an assignment. 1840 | */ 1841 | return realloc(ptr, size); 1842 | } 1843 | 1844 | void yyfree (void * ptr ) 1845 | { 1846 | free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ 1847 | } 1848 | 1849 | #define YYTABLES_NAME "yytables" 1850 | 1851 | #line 52 "scan.l" 1852 | 1853 | 1854 | #include "scanhelp.cpp" 1855 | 1856 | inline int yywrap() 1857 | { 1858 | yy_flush_buffer(YY_CURRENT_BUFFER); 1859 | { BEGIN INITIAL; } 1860 | return 1; 1861 | } 1862 | 1863 | 1864 | -------------------------------------------------------------------------------- /src/Interpreter/scan.l: -------------------------------------------------------------------------------- 1 | %{ /* -*- C++ -*- */ 2 | /* 3 | * scan.l 4 | * flex scanner file for SQL interpreter 5 | * 6 | * write in C++ 7 | * Author: yzy 8 | */ 9 | 10 | #include "parser_public.h" 11 | #include "y.tab.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | %} 20 | 21 | letter [A-Za-z] 22 | digit [0-9] 23 | num {digit}+ 24 | s_digit [+\-]?{digit} 25 | s_num [+\-]?{num} 26 | 27 | %% 28 | 29 | [ \t\n] {} 30 | 31 | {s_num} { /* std::cout << "T_INT\n"; */ sscanf(yytext, "%d", &yylval.i); return T_INT; } 32 | {s_num}\.{num} { /* std::cout << "T_REAL\n"; */ sscanf(yytext, "%lf", &yylval.r); return T_REAL; } 33 | {s_num}\.{num}[Ee]{s_num} { sscanf(yytext, "%lf", &yylval.r); return T_REAL; } 34 | \"([^\"\n]|(\"\"))*\" { yylval.str = std::string(yytext + 1, yyleng - 2); return T_QSTRING; } 35 | `([^\"\n]|(\"\"))*` { yylval.str = std::string(yytext + 1, yyleng - 2); return T_RQSTRING; } 36 | 37 | {letter}({letter}|{digit}|_)* { return get_id(yylval.str = std::string(yytext)); } 38 | 39 | "<" {return T_LT;} 40 | "<=" {return T_LE;} 41 | ">" {return T_GT;} 42 | ">=" {return T_GE;} 43 | "=" {return T_EQ;} 44 | "!=" {return T_NE;} 45 | "<>" {return T_NE;} 46 | 47 | [*/+\-=<>':;,.|&()] {return yytext[0];} 48 | <> {return T_EOF;} 49 | 50 | . { std::cerr << "illegal character [" << yytext[0] << "]\n"; } 51 | 52 | %% 53 | 54 | #include "scanhelp.cpp" 55 | 56 | inline int yywrap() 57 | { 58 | yy_flush_buffer(YY_CURRENT_BUFFER); 59 | { BEGIN INITIAL; } 60 | return 1; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/Interpreter/scanhelp.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yzy on 6/12/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /// 13 | /// \param s 14 | /// \return T_STRING as default. 15 | int get_id(const std::string &s) 16 | { 17 | std::string tok{s}; 18 | int r = T_STRING; 19 | 20 | std::transform(tok.begin(), tok.end(), tok.begin(), [](unsigned char c) 21 | { return std::tolower(c); }); 22 | 23 | if (tok == "select")r = RW_SELECT; 24 | if (tok == "create")r = RW_CREATE; 25 | if (tok == "drop")r = RW_DROP; 26 | if (tok == "table")r = RW_TABLE; 27 | if (tok == "index")r = RW_INDEX; 28 | if (tok == "from")r = RW_FROM; 29 | if (tok == "where")r = RW_WHERE; 30 | if (tok == "insert")r = RW_INSERT; 31 | if (tok == "delete")r = RW_DELETE; 32 | if (tok == "update")r = RW_UPDATE; 33 | if (tok == "and")r = RW_AND; 34 | if (tok == "into")r = RW_INTO; 35 | if (tok == "values")r = RW_VALUES; 36 | if (tok == "set")r = RW_SET; 37 | if (tok == "unique")r = RW_UNIQUE; 38 | if (tok == "primary")r = RW_PRIMARY; 39 | if (tok == "key")r = RW_KEY; 40 | if (tok == "on")r = RW_ON; 41 | 42 | if (tok == "use")r = RW_USE; 43 | if (tok == "database")r = RW_DATABASE; 44 | if (tok == "execfile")r = RW_EXECFILE; 45 | 46 | if (tok == "int")r = RW_INTEGER; 47 | if (tok == "char")r = RW_CHAR; 48 | if (tok == "float")r = RW_FLOAT; 49 | 50 | if (tok == "naive")r = RW_TEST; 51 | 52 | if (tok == "exit")r = RW_EXIT; 53 | 54 | return yylval.i = r; 55 | } 56 | -------------------------------------------------------------------------------- /src/Interpreter/test.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `hahaha` VALUES (123, "456", 78.9, sda); 2 | insert into asd values (123); 3 | insert into asd values ("a", b, "c"); 4 | 5 | select asd from dsa; 6 | select sdaasf from `sdfs` where a >= 0 and b = 1; 7 | select sdaasf from `awdfcwds`; 8 | select sdasadf from ewqfwef; 9 | select * from asd; 10 | 11 | update adfsd set sada = 1221 where asdfsd <> 123.1231; 12 | update adfsd set sada = 1221; 13 | 14 | delete from sgfs; 15 | create table asdfg(sfgs char, fsf char, afsdf int); 16 | create index asfd(sdgsdc); 17 | 18 | drop table afaf; 19 | drop index sdfsdf(dfsd); 20 | 21 | execfile "~/n.sql"; 22 | 23 | ================== BATCH TEST BEGIN 24 | 25 | create table t1(a int, b char(20), primary key(a)); 26 | create table t2(a float, b char(20), primary key(a)); 27 | 28 | create table q(k int); 29 | insert into q values(42); 30 | insert into q values(43); 31 | 32 | select * from q; 33 | 34 | insert into tq(24, "asd"); 35 | -------------------------------------------------------------------------------- /src/Interpreter/y.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.0.4. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . */ 19 | 20 | /* As a special exception, you may create a larger work that contains 21 | part or all of the Bison parser skeleton and distribute that work 22 | under terms of your choice, so long as that work isn't itself a 23 | parser generator using the skeleton or a modified version thereof 24 | as a parser skeleton. Alternatively, if you modify or redistribute 25 | the parser skeleton itself, you may (at your option) remove this 26 | special exception, which will cause the skeleton and the resulting 27 | Bison output files to be licensed under the GNU General Public 28 | License without this special exception. 29 | 30 | This special exception was added by the Free Software Foundation in 31 | version 2.2 of Bison. */ 32 | 33 | #ifndef YY_YY_Y_TAB_H_INCLUDED 34 | # define YY_YY_Y_TAB_H_INCLUDED 35 | /* Debug traces. */ 36 | #ifndef YYDEBUG 37 | # define YYDEBUG 0 38 | #endif 39 | #if YYDEBUG 40 | extern int yydebug; 41 | #endif 42 | 43 | /* Token type. */ 44 | #ifndef YYTOKENTYPE 45 | # define YYTOKENTYPE 46 | enum yytokentype 47 | { 48 | RW_CREATE = 258, 49 | RW_DROP = 259, 50 | RW_TABLE = 260, 51 | RW_INDEX = 261, 52 | RW_SELECT = 262, 53 | RW_FROM = 263, 54 | RW_WHERE = 264, 55 | RW_INSERT = 265, 56 | RW_DELETE = 266, 57 | RW_UPDATE = 267, 58 | RW_AND = 268, 59 | RW_INTO = 269, 60 | RW_VALUES = 270, 61 | RW_SET = 271, 62 | RW_UNIQUE = 272, 63 | RW_PRIMARY = 273, 64 | RW_KEY = 274, 65 | RW_ON = 275, 66 | RW_USE = 276, 67 | RW_DATABASE = 277, 68 | RW_EXECFILE = 278, 69 | RW_INTEGER = 279, 70 | RW_FLOAT = 280, 71 | RW_CHAR = 281, 72 | RW_EXIT = 282, 73 | RW_TEST = 283, 74 | T_LT = 284, 75 | T_GT = 285, 76 | T_GE = 286, 77 | T_LE = 287, 78 | T_EQ = 288, 79 | T_NE = 289, 80 | T_EOF = 290, 81 | NO_TOKEN = 291, 82 | T_INT = 292, 83 | T_REAL = 293, 84 | T_STRING = 294, 85 | T_QSTRING = 295, 86 | T_RQSTRING = 296 87 | }; 88 | #endif 89 | /* Tokens. */ 90 | #define RW_CREATE 258 91 | #define RW_DROP 259 92 | #define RW_TABLE 260 93 | #define RW_INDEX 261 94 | #define RW_SELECT 262 95 | #define RW_FROM 263 96 | #define RW_WHERE 264 97 | #define RW_INSERT 265 98 | #define RW_DELETE 266 99 | #define RW_UPDATE 267 100 | #define RW_AND 268 101 | #define RW_INTO 269 102 | #define RW_VALUES 270 103 | #define RW_SET 271 104 | #define RW_UNIQUE 272 105 | #define RW_PRIMARY 273 106 | #define RW_KEY 274 107 | #define RW_ON 275 108 | #define RW_USE 276 109 | #define RW_DATABASE 277 110 | #define RW_EXECFILE 278 111 | #define RW_INTEGER 279 112 | #define RW_FLOAT 280 113 | #define RW_CHAR 281 114 | #define RW_EXIT 282 115 | #define RW_TEST 283 116 | #define T_LT 284 117 | #define T_GT 285 118 | #define T_GE 286 119 | #define T_LE 287 120 | #define T_EQ 288 121 | #define T_NE 289 122 | #define T_EOF 290 123 | #define NO_TOKEN 291 124 | #define T_INT 292 125 | #define T_REAL 293 126 | #define T_STRING 294 127 | #define T_QSTRING 295 128 | #define T_RQSTRING 296 129 | 130 | /* Value type. */ 131 | 132 | 133 | extern YYSTYPE yylval; 134 | 135 | int yyparse (void); 136 | 137 | #endif /* !YY_YY_Y_TAB_H_INCLUDED */ 138 | -------------------------------------------------------------------------------- /src/RecordManager/RecordManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Haotian on 17/5/25. 3 | // 4 | 5 | #include "RecordManager.h" 6 | #include "../../include/DataStructure.h" 7 | 8 | bool RecordManager::createTable(const string &table) { 9 | string tableFileStr = tableFile(table); 10 | bm->createFile(tableFileStr); 11 | return true; 12 | } 13 | 14 | bool RecordManager::dropTable(const string &table) { 15 | string tableFileStr = tableFile(table); 16 | bm->removeFile(tableFileStr); 17 | return true; 18 | } 19 | 20 | bool RecordManager::createIndex(const Table &table, const SqlValueType &index) { 21 | string indexFileStr = indexFile(table.Name, index.attrName); 22 | // Call BM to create index file 23 | bm->createFile(indexFileStr); 24 | // Call IM to create index tree 25 | im->create(indexFileStr, index); 26 | // Add initial values to index tree 27 | int blockID = 0; 28 | char *block = bm->getBlock(tableFile(table.Name), blockID); 29 | int length = table.recordLength + 1; 30 | int recordsPerBlock = BlockSize / length; 31 | int offset = 1; 32 | Tuple tup; 33 | Element attr; 34 | const char *dest; 35 | 36 | for (auto const &attrType : table.attrType) { 37 | if (attrType.attrName == index.attrName) { 38 | attr.type = attrType; 39 | break; 40 | } 41 | offset += attrType.getSize(); 42 | } 43 | 44 | while (block) { 45 | for (int i = 0; i < recordsPerBlock; i++) { 46 | if (block[i * length] != Used) { continue; } 47 | dest = block + i * length + offset; 48 | switch (attr.M()) { 49 | case MINISQL_TYPE_INT: 50 | memcpy(&attr.i, dest, attr.type.getSize()); 51 | break; 52 | case MINISQL_TYPE_FLOAT: 53 | memcpy(&attr.r, dest, attr.type.getSize()); 54 | break; 55 | case MINISQL_TYPE_CHAR: 56 | memcpy(const_cast(attr.str.c_str()), dest, attr.type.getSize()); 57 | break; 58 | default: 59 | cerr << "Undefined type in RM::createIndex." << endl; 60 | } 61 | im->insert(indexFileStr, attr, blockID * recordsPerBlock + i); 62 | } 63 | bm->setFree(tableFile(table.Name), blockID); 64 | blockID++; 65 | block = bm->getBlock(tableFile(table.Name), blockID); 66 | } 67 | return true; 68 | } 69 | 70 | bool RecordManager::dropIndex(const Table &table, const string &index) { 71 | string indexFileStr = indexFile(table.Name, index); 72 | bm->removeFile(indexFileStr); 73 | 74 | bool foundAttr = false; 75 | 76 | for (auto &attr : table.attrType) { 77 | if (attr.attrName == index) { 78 | foundAttr = true; 79 | im->drop(indexFileStr, attr); 80 | break; 81 | } 82 | } 83 | if (!foundAttr) { 84 | cerr << "Drop index on undefined attr!" << endl; 85 | } 86 | 87 | return true; 88 | } 89 | 90 | int RecordManager::insertRecord(const Table &table, const Tuple &record) { 91 | string tableName = tableFile(table.Name); 92 | int blockID = bm->getBlockTail(tableName); 93 | char *block; 94 | if (blockID < 0) { 95 | blockID = 0; 96 | block = bm->getBlock(tableName, blockID, true); 97 | } else { 98 | block = bm->getBlock(tableName, blockID); 99 | } 100 | int length = table.recordLength + 1; 101 | int recordsPerBlock = BlockSize / length; 102 | int offset = 1; 103 | string strFixed; 104 | int lengthOffset; 105 | 106 | bool validBlock = false; 107 | 108 | int recordOffset = 0; 109 | 110 | while (recordOffset < recordsPerBlock) { 111 | if (block[recordOffset * length] != UnUsed) { 112 | recordOffset++; 113 | continue; 114 | } 115 | validBlock = true; 116 | block += recordOffset * length; 117 | break; 118 | } 119 | 120 | if (!validBlock) { 121 | recordOffset = 0; 122 | bm->setFree(tableName, blockID); 123 | block = bm->getBlock(tableName, ++blockID, true); // get next block (should be empty) and get the first unit 124 | } 125 | 126 | for (auto attr = record.element.begin(); attr < record.element.end(); attr++) { 127 | switch (attr->type.M()) { 128 | case MINISQL_TYPE_CHAR: 129 | strFixed = attr->str; 130 | lengthOffset = attr->type.charSize - strFixed.length(); 131 | if (lengthOffset > 0) { 132 | strFixed.insert(strFixed.length(), lengthOffset, 0); 133 | } 134 | memcpy(block + offset, strFixed.c_str(), attr->type.charSize + 1); 135 | offset += attr->type.charSize + 1; 136 | break; 137 | case MINISQL_TYPE_INT: 138 | memcpy(block + offset, &attr->i, sizeof(int)); 139 | offset += sizeof(int); 140 | break; 141 | case MINISQL_TYPE_FLOAT: 142 | memcpy(block + offset, &attr->r, sizeof(float)); 143 | offset += sizeof(float); 144 | break; 145 | default: 146 | cerr << "Undefined type!!" << endl; 147 | break; 148 | } 149 | } 150 | block[0] = Used; 151 | bm->setDirty(tableName, blockID); 152 | bm->setFree(tableName, blockID); 153 | return blockID * recordsPerBlock + recordOffset; 154 | } 155 | 156 | int RecordManager::selectRecord(const Table &table, const vector &attr, const vector &cond) { 157 | int blockID = 0; 158 | char *block = bm->getBlock(tableFile(table.Name), blockID); 159 | int length = table.recordLength + 1; 160 | int blocks = BlockSize / length; 161 | Tuple tup; 162 | Row row; 163 | Result res; 164 | 165 | while (block) { 166 | for (int i = 0; i < blocks; i++) { 167 | if (block[i * length] != Used) { continue; } 168 | convertToTuple(block, i * length, table.attrType, tup); 169 | if (condsTest(cond, tup, table.attrNames)) { 170 | row = tup.fetchRow(table.attrNames, attr); 171 | res.row.push_back(row); 172 | } 173 | } 174 | bm->setFree(tableFile(table.Name), blockID); 175 | blockID++; 176 | block = bm->getBlock(tableFile(table.Name), blockID); 177 | } 178 | 179 | dumpResult(res); 180 | return res.row.size(); 181 | } 182 | 183 | int RecordManager::selectRecord(const Table &table, const vector &attr, const vector &cond, 184 | const IndexHint &indexHint, bool printResult) { 185 | string tableFileName = tableFile(table.Name); 186 | string indexFileName = indexFile(table.Name, indexHint.attrName); 187 | int recordPos; 188 | if (indexHint.cond.cond == MINISQL_COND_LESS || indexHint.cond.cond == MINISQL_COND_LEQUAL) { 189 | recordPos = im->searchHead(indexFileName, indexHint.attrType); 190 | } else { 191 | recordPos = im->search(indexFileName, indexHint.cond.value); 192 | } 193 | 194 | int length = table.recordLength + 1; 195 | int recordsPerBlock = BlockSize / length; 196 | char *block; 197 | int blockID; 198 | Tuple tup; 199 | Row row; 200 | Result res; 201 | Element e; 202 | bool degrade = false; 203 | int threshold = table.recordCnt / recordsPerBlock / 3; 204 | int cnt = 0; 205 | 206 | while (recordPos != -1) { 207 | blockID = recordPos / recordsPerBlock; 208 | block = bm->getBlock(tableFileName, blockID) + recordPos % recordsPerBlock * length; 209 | convertToTuple(block, 0, table.attrType, tup); 210 | if (condsTest(cond, tup, table.attrNames)) { 211 | row = tup.fetchRow(table.attrNames, attr); 212 | res.row.push_back(row); 213 | } else { 214 | e = tup.fetchElement(table.attrNames, indexHint.attrName); 215 | if (indexHint.cond.cond == MINISQL_COND_MORE) { 216 | IndexHint tmp = indexHint; 217 | tmp.cond.cond = MINISQL_COND_GEQUAL; 218 | if (!tmp.cond.test(e)) { 219 | bm->setFree(tableFileName, blockID); 220 | break; 221 | } 222 | } else if (!indexHint.cond.test(e)) { 223 | bm->setFree(tableFileName, blockID); 224 | break; 225 | } 226 | } 227 | recordPos = im->searchNext(indexFileName, indexHint.attrType); 228 | cnt++; 229 | if (cnt > threshold) { 230 | degrade = true; 231 | bm->setFree(tableFileName, blockID); 232 | break; 233 | } 234 | bm->setFree(tableFileName, blockID); 235 | } 236 | 237 | if (!degrade) { 238 | if (printResult) { 239 | dumpResult(res); 240 | } 241 | return cnt; 242 | } else { 243 | return selectRecord(table, attr, cond); 244 | } 245 | } 246 | 247 | bool RecordManager::deleteRecord(const Table &table, const vector &cond) { 248 | int blockOffset = 0; 249 | char *block = bm->getBlock(tableFile(table.Name), blockOffset); 250 | int length = table.recordLength + 1; 251 | int blocks = BlockSize / length; 252 | Tuple tup; 253 | 254 | while (block) { 255 | for (int i = 0; i < blocks; i++) { 256 | if (block[i * length] != Used) { continue; } 257 | convertToTuple(block, i * length, table.attrType, tup); 258 | if (condsTest(cond, tup, table.attrNames)) { 259 | block[i * length] = UnUsed; 260 | for (auto &col: tup.element) { 261 | for (auto &attr : table.index) { 262 | if (col.type.attrName == attr.first) { 263 | im->removeKey(indexFile(table.Name, attr.first), col); 264 | } 265 | } 266 | } 267 | } 268 | } 269 | bm->setDirty(tableFile(table.Name), blockOffset); 270 | bm->setFree(tableFile(table.Name), blockOffset); 271 | blockOffset++; 272 | block = bm->getBlock(tableFile(table.Name), blockOffset); 273 | } 274 | } 275 | 276 | void RecordManager::dumpResult(const Result &res) const { 277 | for (auto const &row : res.row) { 278 | cout << " | "; 279 | for (auto const &col : row.col) { 280 | cout << col << " | "; 281 | } 282 | cout << endl; 283 | } 284 | } 285 | 286 | bool RecordManager::condsTest(const std::vector &conds, const Tuple &tup, const std::vector &attr) { 287 | int condPos; 288 | for (Cond cond : conds) { 289 | condPos = -1; 290 | for (int i = 0; i < attr.size(); i++) { 291 | if (attr[i] == cond.attr) { 292 | condPos = i; 293 | break; 294 | } 295 | } 296 | if (condPos == -1) { 297 | std::cerr << "Attr not found in cond test!" << std::endl; 298 | } 299 | if (!cond.test(tup.element[condPos])) { 300 | return false; 301 | } 302 | } 303 | return true; 304 | } 305 | 306 | void RecordManager::convertToTuple(const char *blockBuffer, int offset, const std::vector &attrType, Tuple &tup) { 307 | const char *block = blockBuffer + offset + 1; // 1 for meta bit 308 | Element e; 309 | tup.element.clear(); 310 | for (int i = 0; i < attrType.size(); i++) { 311 | e.reset(); 312 | e.type = attrType[i]; 313 | switch (attrType[i].M()) { 314 | case MINISQL_TYPE_INT: 315 | memcpy(&e.i, block, sizeof(int)); 316 | block += sizeof(int); 317 | break; 318 | case MINISQL_TYPE_FLOAT: 319 | memcpy(&e.r, block, sizeof(float)); 320 | block += sizeof(float); 321 | break; 322 | case MINISQL_TYPE_CHAR: 323 | e.str = block; 324 | block += attrType[i].charSize + 1; 325 | break; 326 | } 327 | tup.element.push_back(e); 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /src/RecordManager/RecordManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Haotian on 17/5/25. 3 | // 4 | 5 | #ifndef MINISQL_RECORDMANAGER_H 6 | #define MINISQL_RECORDMANAGER_H 7 | 8 | #include 9 | #include 10 | #include "../../include/DataStructure.h" 11 | #include "../BufferManager/BufferManager.h" 12 | #include "../IndexManager/IndexManager.h" 13 | 14 | using namespace std; 15 | using namespace MINISQL_BASE; 16 | 17 | class RecordManager { 18 | public: 19 | RecordManager(BufferManager *bm, IndexManager *im) : bm(bm), im(im) {} 20 | 21 | ~RecordManager() = default; 22 | 23 | bool createTable(const string &table); 24 | 25 | bool dropTable(const string &table); 26 | 27 | bool createIndex(const Table &table, const SqlValueType &index); 28 | 29 | bool dropIndex(const Table &table, const string &index); 30 | 31 | int insertRecord(const Table &table, const Tuple &record); 32 | 33 | int selectRecord(const Table &table, const vector &attr, const vector &cond); 34 | 35 | int 36 | selectRecord(const Table &table, const vector &attr, const vector &cond, const IndexHint &indexHint, 37 | bool printResult = true); 38 | 39 | bool deleteRecord(const Table &table, const vector &cond); 40 | 41 | private: 42 | BufferManager *bm; 43 | IndexManager *im; 44 | 45 | void dumpResult(const Result &res) const; 46 | 47 | bool condsTest(const std::vector &conds, const Tuple &tup, const std::vector &attr); 48 | 49 | void convertToTuple(const char *blockBuffer, int offset, const std::vector &attrType, Tuple &tup); 50 | }; 51 | 52 | 53 | #endif //MINISQL_RECORDMANAGER_H 54 | --------------------------------------------------------------------------------