├── .gitignore ├── CMakeLists.txt ├── README.md ├── cmake └── Findglog.cmake ├── doc └── doc.md ├── src ├── Makefile ├── Parser.HowTo ├── catalog.h ├── dbcreate.cpp ├── interp.cc ├── ix.h ├── ix_error.cc ├── ix_indexhandle.cc ├── ix_indexscan.cc ├── ix_internal.h ├── ix_manager.cc ├── ix_test.cpp ├── linkedlist.h ├── nodes.cc ├── parse.y ├── parser.h ├── parser_internal.h ├── pf.h ├── pf_buffermgr.cc ├── pf_buffermgr.h ├── pf_error.cc ├── pf_filehandle.cc ├── pf_hashtable.cc ├── pf_hashtable.h ├── pf_internal.h ├── pf_manager.cc ├── pf_pagehandle.cc ├── pf_statistics.cc ├── pf_test1.cpp ├── pf_test2.cpp ├── pf_test3.cpp ├── printer.cc ├── printer.h ├── ql.h ├── ql_disjoint.cc ├── ql_disjoint.h ├── ql_error.cc ├── ql_filescaniter.cc ├── ql_indexjoiniter.cc ├── ql_indexsearchiter.cc ├── ql_internal.h ├── ql_iterator.cc ├── ql_iterator.h ├── ql_joiniter.cc ├── ql_manager.cc ├── ql_projectioniter.cc ├── ql_selectioniter.cc ├── redbase.cpp ├── redbase.h ├── rm.h ├── rm_error.cc ├── rm_filehandle.cc ├── rm_filescan.cc ├── rm_internal.h ├── rm_manager.cc ├── rm_record.cc ├── rm_rid.cc ├── rm_rid.h ├── rm_test.cpp ├── scan.l ├── scanhelp.h ├── sm.h ├── sm_error.cc ├── sm_manager.cc ├── statistics.cc └── statistics.h └── testset └── test.sql /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | lib 3 | cmake-build* 4 | test/ 5 | dataset* 6 | 7 | # CLion config 8 | .idea/ 9 | 10 | # Vim tmp files 11 | **/*.swp 12 | **/*.swo 13 | 14 | # YCM config 15 | **/*.ycm_extra_conf.py* 16 | 17 | # generated files 18 | src/parse.c 19 | src/scan.c 20 | src/y.tab.h 21 | src/*_test* 22 | !src/*_test*.cpp 23 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(rebase) 3 | 4 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -O3 -Wall") 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -O3 -std=c++14 -Wall") 6 | 7 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 8 | 9 | find_package(FLEX) 10 | find_package(BISON) 11 | find_package(glog) 12 | find_package(gflags) 13 | 14 | if (GLOG_FOUND) 15 | include_directories(${GLOG_INCLUDE_DIR}) 16 | else() 17 | set(GLOG_LIBRARIES "-lglog -lgflags") 18 | endif() 19 | 20 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 21 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 22 | 23 | BISON_TARGET(Parser src/parse.y ${CMAKE_CURRENT_BINARY_DIR}/y.tab.cc COMPILE_FLAGS --defines=${CMAKE_CURRENT_BINARY_DIR}/y.tab.h) 24 | FLEX_TARGET(Lexer src/scan.l ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.c) 25 | ADD_FLEX_BISON_DEPENDENCY(Lexer Parser) 26 | 27 | file(GLOB C_SOURCE_FILES 28 | "src/*.h" 29 | "src/*.c" 30 | ) 31 | file(GLOB CPP_SOURCE_FILES 32 | "src/*.h" 33 | "src/*.cc" 34 | ) 35 | set(SOURCE_FILES 36 | ${C_SOURCE_FILES} 37 | ${CPP_SOURCE_FILES} 38 | ${BISON_Parser_OUTPUTS} 39 | ${FLEX_Lexer_OUTPUTS} 40 | ) 41 | 42 | add_executable(dbcreate ${SOURCE_FILES} "src/dbcreate.cpp") 43 | add_executable(redbase ${SOURCE_FILES} "src/redbase.cpp") 44 | add_executable(rm_test ${SOURCE_FILES} "src/rm_test.cpp") 45 | add_executable(ix_test ${SOURCE_FILES} "src/ix_test.cpp") 46 | 47 | target_link_libraries(dbcreate ${GLOG_LIBRARIES}) 48 | target_link_libraries(redbase ${GLOG_LIBRARIES}) 49 | target_link_libraries(rm_test ${GLOG_LIBRARIES}) 50 | target_link_libraries(ix_test ${GLOG_LIBRARIES}) 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rebaseDB 2 | 3 | > A rough DBMS based on the RedBase project from CS346 4 | 5 | A simple database management system supporting: 6 | 7 | - Basic SQL commands 8 | - B+ tree-based indexing 9 | - Not-so-slow-but-not-too-fast-either query optimizer 10 | 11 | See our (Chinese) [documentation](https://github.com/huzecong/rebaseDB/blob/master/doc/doc.md) for more details. 12 | 13 | **On the RedBase framework:** For details of the Redbase framework, please refer to [CS346 RedBase Project Overview](https://web.stanford.edu/class/cs346/2015/redbase.html). For those wishing to implement their own DBMS using this framework, check out the `initial` branch. 14 | 15 | ## Usage 16 | 17 | - Install dependencies: `glog` and `gflags` 18 | - Clone this repo 19 | - `mkdir cmake-build && cd cmake-build` (gather all the messy build files in one place) 20 | - `cmake .. && make dbcreate && make redbase` (build the binaries) 21 | - `./redbase` 22 | 23 | For supported SQL commands, also refer to our (Chinese) documentation. 24 | 25 | ## References 26 | 27 | See also: 28 | 29 | - https://github.com/junkumar/redbase 30 | - https://github.com/yifeih/redbase 31 | 32 | 33 | -------------------------------------------------------------------------------- /cmake/Findglog.cmake: -------------------------------------------------------------------------------- 1 | 2 | # - Try to find Glog 3 | # 4 | # The following variables are optionally searched for defaults 5 | # GLOG_ROOT_DIR: Base directory where all GLOG components are found 6 | # 7 | # The following are set after configuration is done: 8 | # GLOG_FOUND 9 | # GLOG_INCLUDE_DIRS 10 | # GLOG_LIBRARIES 11 | 12 | include(FindPackageHandleStandardArgs) 13 | 14 | set(GLOG_ROOT_DIR "" CACHE PATH "Folder contains Google glog") 15 | 16 | if(WIN32) 17 | find_path(GLOG_INCLUDE_DIR glog/logging.h 18 | PATHS ${GLOG_ROOT_DIR}/src/windows) 19 | else() 20 | find_path(GLOG_INCLUDE_DIR glog/logging.h 21 | PATHS ${GLOG_ROOT_DIR}) 22 | endif() 23 | 24 | if(MSVC) 25 | find_library(GLOG_LIBRARY_RELEASE libglog_static 26 | PATHS ${GLOG_ROOT_DIR} 27 | PATH_SUFFIXES Release) 28 | 29 | find_library(GLOG_LIBRARY_DEBUG libglog_static 30 | PATHS ${GLOG_ROOT_DIR} 31 | PATH_SUFFIXES Debug) 32 | 33 | set(GLOG_LIBRARY optimized ${GLOG_LIBRARY_RELEASE} debug ${GLOG_LIBRARY_DEBUG}) 34 | else() 35 | find_library(GLOG_LIBRARY glog 36 | PATHS ${GLOG_ROOT_DIR} 37 | PATH_SUFFIXES 38 | lib 39 | lib64) 40 | endif() 41 | 42 | find_package_handle_standard_args(GLOG DEFAULT_MSG 43 | GLOG_INCLUDE_DIR GLOG_LIBRARY) 44 | 45 | if(GLOG_FOUND) 46 | set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR}) 47 | set(GLOG_LIBRARIES ${GLOG_LIBRARY}) 48 | endif() 49 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for the RedBase project. 3 | # 4 | # Author: Hyunjung Park (hyunjung@cs.stanford.edu) 5 | # 6 | 7 | 8 | # 9 | # Global variables 10 | # 11 | CC = g++ 12 | BUILD_DIR = ../build/ 13 | LIB_DIR = ../lib/ 14 | INC_DIRS = -I. 15 | AR = ar -qc 16 | RANLIB = ranlib 17 | YACC = bison -dy 18 | LEX = flex 19 | 20 | # -m32 - Generates code that runs on any i386 system 21 | # sizeof(int) = sizeof(long) = sizeof(void *) = 4 22 | # -g - Debugging information 23 | # -O1 - Basic optimization 24 | # -Wall - All warnings 25 | # -DDEBUG_PF - This turns on the LOG file for lots of BufferMgr info 26 | CFLAGS := -m32 -std=c++11 -O0 -g -Wall $(STATS_OPTION) $(INC_DIRS) 27 | # CFLAGS += -DPF_LOG 28 | 29 | # The STATS_OPTION can be set to -DPF_STATS or to nothing to turn on and 30 | # off buffer manager statistics. The student should not modify this 31 | # flag at all! 32 | STATS_OPTION = -DPF_STATS 33 | 34 | # 35 | # Students: Please modify SOURCES variables as needed. 36 | # 37 | PF_SOURCES = pf_buffermgr.cc pf_error.cc pf_filehandle.cc \ 38 | pf_pagehandle.cc pf_hashtable.cc pf_manager.cc \ 39 | pf_statistics.cc statistics.cc 40 | RM_SOURCES = rm_error.cc rm_filehandle.cc rm_filescan.cc \ 41 | rm_manager.cc rm_record.cc rm_rid.cc statistics.cc 42 | IX_SOURCES = ix_manager.cc ix_indexhandle.cc ix_indexscan.cc \ 43 | ix_error.cc statistics.cc 44 | SM_SOURCES = statistics.cc #sm_stub.cc printer.cc 45 | QL_SOURCES = statistics.cc #ql_manager_stub.cc 46 | UTILS_SOURCES = #dbcreate.cc dbdestroy.cc redbase.cc 47 | PARSER_SOURCES = statistics.cc #scan.c parse.c nodes.c interp.c 48 | TESTER_SOURCES = pf_test1.cpp pf_test2.cpp pf_test3.cpp rm_test.cpp ix_test.cpp #parser_test.cpp 49 | 50 | PF_OBJECTS = $(addprefix $(BUILD_DIR), $(PF_SOURCES:.cc=.o)) 51 | RM_OBJECTS = $(addprefix $(BUILD_DIR), $(RM_SOURCES:.cc=.o)) 52 | IX_OBJECTS = $(addprefix $(BUILD_DIR), $(IX_SOURCES:.cc=.o)) 53 | SM_OBJECTS = $(addprefix $(BUILD_DIR), $(SM_SOURCES:.cc=.o)) 54 | QL_OBJECTS = $(addprefix $(BUILD_DIR), $(QL_SOURCES:.cc=.o)) 55 | UTILS_OBJECTS = $(addprefix $(BUILD_DIR), $(UTILS_SOURCES:.cc=.o)) 56 | PARSER_OBJECTS = $(addprefix $(BUILD_DIR), $(PARSER_SOURCES:.cc=.o)) 57 | TESTER_OBJECTS = $(addprefix $(BUILD_DIR), $(TESTER_SOURCES:.cpp=.o)) 58 | OBJECTS = $(PF_OBJECTS) $(RM_OBJECTS) $(IX_OBJECTS) \ 59 | $(SM_OBJECTS) $(QL_OBJECTS) $(PARSER_OBJECTS) \ 60 | $(TESTER_OBJECTS) $(UTILS_OBJECTS) 61 | 62 | LIBRARY_PF = $(LIB_DIR)libpf.a 63 | LIBRARY_RM = $(LIB_DIR)librm.a 64 | LIBRARY_IX = $(LIB_DIR)libix.a 65 | LIBRARY_SM = $(LIB_DIR)libsm.a 66 | LIBRARY_QL = $(LIB_DIR)libql.a 67 | LIBRARY_PARSER = $(LIB_DIR)libparser.a 68 | LIBRARIES = $(LIBRARY_PF) $(LIBRARY_RM) $(LIBRARY_IX) \ 69 | $(LIBRARY_SM) $(LIBRARY_QL) $(LIBRARY_PARSER) 70 | 71 | UTILS = $(UTILS_SOURCES:.cc=) 72 | TESTS = $(TESTER_SOURCES:.cpp=) 73 | EXECUTABLES = $(UTILS) $(TESTS) 74 | 75 | LIBS = -lparser -lql -lsm -lix -lrm -lpf -lglog -lgflags 76 | 77 | # 78 | # Build targets 79 | # 80 | all: $(LIBRARIES) $(UTILS) 81 | 82 | clean: 83 | rm -f $(BUILD_DIR)*.o $(BUILD_DIR)*.d y.output y.tab.h parse.c $(LIBRARIES) $(EXECUTABLES) 84 | 85 | testers: all $(TESTS) 86 | 87 | # 88 | # Libraries 89 | # 90 | $(LIBRARY_PF): $(PF_OBJECTS) 91 | $(RM) $@ 92 | $(AR) $(LIBRARY_PF) $(PF_OBJECTS) 93 | $(RANLIB) $(LIBRARY_PF) 94 | 95 | $(LIBRARY_RM): $(RM_OBJECTS) 96 | $(RM) $@ 97 | $(AR) $(LIBRARY_RM) $(RM_OBJECTS) 98 | $(RANLIB) $(LIBRARY_RM) 99 | 100 | $(LIBRARY_IX): $(IX_OBJECTS) 101 | $(RM) $@ 102 | $(AR) $(LIBRARY_IX) $(IX_OBJECTS) 103 | $(RANLIB) $(LIBRARY_IX) 104 | 105 | $(LIBRARY_SM): $(SM_OBJECTS) 106 | $(RM) $@ 107 | $(AR) $(LIBRARY_SM) $(SM_OBJECTS) 108 | $(RANLIB) $(LIBRARY_SM) 109 | 110 | $(LIBRARY_QL): $(QL_OBJECTS) 111 | $(RM) $@ 112 | $(AR) $(LIBRARY_QL) $(QL_OBJECTS) 113 | $(RANLIB) $(LIBRARY_QL) 114 | 115 | $(LIBRARY_PARSER): $(PARSER_OBJECTS) 116 | $(RM) $@ 117 | $(AR) $(LIBRARY_PARSER) $(PARSER_OBJECTS) 118 | $(RANLIB) $(LIBRARY_PARSER) 119 | 120 | # 121 | # Parser 122 | # 123 | y.tab.h: parse.c 124 | 125 | parse.c: parse.y 126 | $(YACC) parse.y; mv y.tab.c parse.c 127 | 128 | scan.c: scan.l scanhelp.c y.tab.h 129 | $(LEX) scan.l; mv lex.yy.c scan.c 130 | 131 | $(BUILD_DIR)parse.o: parse.c 132 | 133 | $(BUILD_DIR)scan.o: scan.c y.tab.h 134 | 135 | $(BUILD_DIR)nodes.o: nodes.c 136 | 137 | $(BUILD_DIR)interp.o: interp.c 138 | 139 | # 140 | # Rules 141 | # 142 | -include $(OBJECTS:.o=.d) 143 | 144 | $(BUILD_DIR)%.d: %.cc 145 | @set -e; \ 146 | rm -f $@; \ 147 | $(CC) $(CFLAGS) -MM -MT $(@:.d=.o) $< > $@.$$$$; \ 148 | sed 's,\($*\)\.o[ :]*,\1.o $@: ,g' $@.$$$$ > $@; \ 149 | rm -f $@.$$$$ 150 | 151 | $(BUILD_DIR)%.d: %.cpp 152 | @set -e; \ 153 | rm -f $@; \ 154 | $(CC) $(CFLAGS) -MM -MT $(@:.d=.o) $< > $@.$$$$; \ 155 | sed 's,\($*\)\.o[ :]*,\1.o $@: ,g' $@.$$$$ > $@; \ 156 | rm -f $@.$$$$ 157 | 158 | $(OBJECTS): %.o: 159 | $(CC) $(CFLAGS) -c $< -o $@ 160 | 161 | $(EXECUTABLES): %: $(BUILD_DIR)%.o $(LIBRARIES) 162 | $(CC) $(CFLAGS) $< -o $@ -L$(LIB_DIR) $(LIBS) 163 | 164 | -------------------------------------------------------------------------------- /src/Parser.HowTo: -------------------------------------------------------------------------------- 1 | Simplified how to manual for REDBASE parser modifications 2 | 3 | This is a bottom up approach to adding functionality to the parser. It 4 | helps, before you start, if you've already decided on the syntax of your 5 | additions. 6 | 7 | In brief, once you have a high level definition of your additions, go 8 | about achieving the functionality starting from the foundations. 9 | 10 | Start by considering which keywords you want to add to to the language. 11 | These are be tokenized by lex. 12 | 13 | 1. scan.l 14 | This is the lexer proper. You probably don't need to change 15 | anything here. 16 | 17 | The idea behind its operation, however, is the following: 18 | the left column corresponds to patterns that the lexer will find 19 | in the strings it processes, and the right column indicates what 20 | actions the lexer takes when the corresponding pattern is found. 21 | 22 | The most important lexer entry in scan.l looks like this: 23 | 24 | {letter}({letter}|{digit}|_)* {return get_id(yylval.sval = yytext):} 25 | 26 | A letter followed by any combination of letters, numbers and underscore 27 | characters is an identifier, and the lexer calls get_id() to figure out 28 | which one it is. 29 | 30 | 2. scanhelp.c 31 | This contains the get_id() function. It checks the identifier 32 | against all of the possible keywords it knows, and if it finds it, 33 | returns the corresponding token, otherwise it returns a stored copy 34 | of the string to be parsed. 35 | 36 | There are also utilities that remove quotes from strings, store copies 37 | of them in a memory pool (the strings that are passed as parameters to 38 | the DM and QL functions are stored here), but they don't need changing. 39 | 40 | 3. parser_internal.h 41 | This begins with a list of the types of commands and their components. 42 | Each component must also be stored, and parse.h defines as nodes these 43 | storage types. If you are adding commands that have a different form 44 | than the already extant ones, you will need to add to this enumeration, 45 | and probably define a new kind of data node, to allow the parser to 46 | build the command correctly. 47 | 48 | 4. nodes.c 49 | If you define new node types in parser_internal.h, then you can write code 50 | to initialize them and add it to nodes.c which is very straightforward 51 | given the multitude of examples of node types already present. 52 | 53 | 5. parse.y 54 | This first defines the types of input it encounters. At the command 55 | start the breakdown of the users commands begins. The most likely spot 56 | to be changing things is in the command section. 57 | 58 | Essentially, all you do is add a line containing | (logical or) 59 | and the particular command you designed. 60 | 61 | Somewhere below you define the structure of the command. The examples 62 | of these commands show pretty clearly how to go about adding a new one. 63 | 64 | The function RBparse() is also in this file. Any changes to the overall 65 | query processing mechanism would be made here. The function PrintError() 66 | is also in this file. 67 | 68 | You don't need to change parse.c, which is automatically generated by 69 | yacc from parse.y and the header files. It exists solely to be compiled 70 | to create the executable code. 71 | 72 | 6. interp.c 73 | Here is where the meat of the interpretation takes place. When 74 | a query has been parsed, it sends the resulting node structure --in 75 | reality, a very simple query tree-- here. This code simply goes 76 | through the nodes setting up parameters for the function calls, and 77 | then calls the user designed and implemented DM and QL functions. 78 | 79 | 80 | An Example: adding the command "pack," which takes a relation name and 81 | a percentage as parameters. 82 | 83 | scan.l: no changes 84 | 85 | scanhelp.c: add lines of the type: 86 | if (!strcmp(string, "pack")) return yylval.ival = RW_PACK 87 | 88 | parser_internal.h: add a type value, node definitions, 89 | and function prototypes: 90 | N_PACK /* added to NODEKIND type */ 91 | 92 | /* pack node */ /* added to NODE type */ 93 | struct{ 94 | char *relname; 95 | int percentage; 96 | } PACK; 97 | 98 | NODE *pack_node(char *relname, int percentage); 99 | 100 | nodes.c: add initialization functions: 101 | NODE *pack_node(char *relname, int percentage) 102 | { 103 | NODE *n = newnode(N_PACK); 104 | 105 | n -> u.PACK.relname = relname; 106 | n -> u.PACK.percentage = percentage; 107 | return n; 108 | } 109 | 110 | parse.y: add parser directions: 111 | 112 | added to token: 113 | RW_PACK 114 | 115 | added to type : 116 | pack 117 | 118 | 119 | added to utility (or wherever you want): 120 | pack 121 | : RW_PACK T_STRING '(' T_INT ')' 122 | { 123 | $$ = pack_node($2, $4); 124 | } 125 | ; 126 | 127 | interp.c: code to unpack the node and call your Pack() function 128 | 129 | case N_PACK: /* for Pack() */ 130 | 131 | errval = pQlm->Pack(n->u.PACK.relname, 132 | n->u.PACK.percentage); 133 | break; 134 | 135 | =========================================================================== 136 | 137 | How to make the Parser read the input from some file and write the 138 | output to a file (or do just one of the two). 139 | The changes required on a file-by-file basis are outlined here: 140 | 141 | 1. redbase.cc 142 | ============== 143 | 144 | (1.1) Define the input and output file pointers globally (so that they 145 | can be made visible to the parser files through extern declarations). 146 | For example: 147 | 148 | // the input to the parser comes from the inputFile 149 | FILE * inputFilePtr; 150 | // the output of the parser goes to the output file 151 | FILE * outputFilePtr; 152 | 153 | (1.2) Open the files before the call to RBparse() and 154 | close them after the call. For example, 155 | 156 | // be careful with the file paths. Remember that OpenDb() will 157 | // change the working directory 158 | inputFilePtr = fopen("input.dat", "r"); 159 | if(!inputFilePtr) { 160 | fprintf(stderr, "unable to open input file\n"); 161 | exit(1); 162 | } 163 | 164 | outputFilePtr = fopen("output.dat", "w"); 165 | if(!outputFilePtr) { 166 | fprintf(stderr, "unable to open output file\n"); 167 | exit(1); 168 | } 169 | 170 | // open the database 171 | rc = smm.OpenDb(dbname); 172 | if(rc) { 173 | SM_PrintError(rc); 174 | exit(1); 175 | } 176 | 177 | // call the command loop 178 | RBparse(pfm, smm, qlm); 179 | 180 | fclose(inputFilePtr); 181 | fclose(outputFilePtr); 182 | 183 | 184 | 2. scan.l : (input redirection) 185 | =========== 186 | 187 | (1). Add the following to the first section of the lex file, ie, within 188 | the first %{ ... %} 189 | 190 | int my_yyinput(char * buf, int max_size); /* defined in scanhelp.c */ 191 | /* Flex uses the macro YY_INPUT to read input data. YY_INPUT needs to be 192 | * redefined for input redirection. 193 | * For more information please see pages 156-157 of: 194 | Lex & Yacc 195 | by John R. Levine, Tony Mason, Doug Brown 196 | Reilly & Associates; ISBN: 1565920007 197 | (Available in the Stanford MATH/CS Library) 198 | */ 199 | #undef YY_INPUT 200 | #define YY_INPUT(buffer, result, max_size) (result = my_yyinput(buffer, max_size)) 201 | 202 | 203 | 3. scanhelp.c: (input redirection continued) 204 | =============== 205 | 206 | 1. Define the my_yyinput() function to read from the required input file: 207 | 208 | extern FILE* inputFilePtr; 209 | int my_yyinput(char * buf, int max_size) { 210 | // read a character from the input file 211 | char c = (char) fgetc(inputFilePtr); 212 | // copy it to the buffer 213 | *buf = c; 214 | // the number of characters read 215 | return 1; 216 | } 217 | 218 | 4. parse.y : (output redirection) 219 | =========== 220 | 221 | 1. In the function RBparse(): 222 | 223 | (1). Disable the printing of the prompt: 224 | 225 | #if 0 226 | /* Print a prompt */ 227 | cout << PROMPT; 228 | 229 | /* Get the prompt to actually show up on the screen */ 230 | cout.flush(); 231 | #endif 232 | 233 | (2). Look at the interp(parse_tree) call in the RBparse() function. It is 234 | this function which actually interprets each redbase command. 235 | The function is defined in interp.c. If you want to do output 236 | redirection, you would want to pass the output file pointer 237 | into this function, which requires changing the function 238 | definition. Changes are required in 2 places: 239 | 240 | (2.1). In interp.c itself: 241 | // RC interp(NODE *n) { 242 | RC interp(FILE* fp, NODE *n) { 243 | 244 | (2.2). In parser_internal.h: 245 | // RC interp(NODE *n); 246 | RC interp(FILE * fp, NODE *n); 247 | 248 | 3. Pass the output file pointer to the interp() function called from 249 | RBparse() 250 | 251 | /* If a query was successfully read, interpret it */ 252 | if(yyparse() == 0 && parse_tree != NULL) 253 | if ((rc = interp(outputFilePtr, parse_tree))) { 254 | 255 | Well, for the above to work (ie, for outputFilePtr to be defined 256 | in parse.y) you have to add an extern declaration of outputFilePtr 257 | in parse.y before its usage: 258 | 259 | // the output of the parser goes to the output file 260 | extern FILE * outputFilePtr; 261 | 262 | 5. interp.c (output redirection continued) 263 | ============ 264 | 265 | At this stage you have got the output file pointer into the 266 | RC interp(FILE* fp, NODE *n) function. Remember, the parameter fp 267 | points to the output file. All that remains to be done is to 268 | make changes within interp(FILE* fp, NODE *n) to 269 | output the required information to the output file. 270 | 271 | A simple change that I made to test out whether all the above stuff 272 | works is to print out all commands to the output file. For example, 273 | I added the following line within the case N_CREATETABLE: { .... } 274 | 275 | // I added this statement to print the command to the 276 | // output file 277 | fprintf(fp, "Create Table Command\n"); 278 | 279 | %end-of-file 280 | -------------------------------------------------------------------------------- /src/catalog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/12/23. 3 | // 4 | 5 | #ifndef REBASE_CATALOG_H 6 | #define REBASE_CATALOG_H 7 | 8 | #include "redbase.h" 9 | 10 | struct RelCatEntry { 11 | char relName[MAXNAME + 1]; // relation name 12 | int tupleLength; // tuple length in bytes 13 | int attrCount; // number of attributes 14 | int indexCount; // number of indexed attributes (not decreased when index is dropped) 15 | int recordCount; // number of records in the relation 16 | }; 17 | 18 | struct AttrCatEntry { 19 | char relName[MAXNAME + 1]; // this attribute's relation 20 | char attrName[MAXNAME + 1]; // attribute name 21 | int offset; // offset in bytes from beginning of tuple 22 | AttrType attrType; // attribute type 23 | int attrSize; // attribute size 24 | int attrDisplayLength; // attribute length 25 | // for INT columns the length is only used to format display 26 | // for STRING columns the length does NOT include '\0' 27 | int attrSpecs; // attribute specification(s) 28 | int indexNo; // index number, or -1 if not indexed 29 | }; 30 | 31 | #endif //REBASE_CATALOG_H 32 | -------------------------------------------------------------------------------- /src/dbcreate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // dbcreate.cc 3 | // 4 | // Author: Jason McHugh (mchughj@cs.stanford.edu) 5 | // 6 | // This shell is provided for the student. 7 | 8 | #include "rm.h" 9 | #include "sm.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | //int isDir(const char *file){ 18 | // struct stat buf; 19 | // stat(file, &buf); 20 | // return S_ISREG(buf.st_mode); 21 | //} 22 | 23 | // 24 | // main 25 | // 26 | int main(int argc, char *argv[]) { 27 | char *dbname; 28 | char command[255] = "mkdir "; 29 | RC rc; 30 | 31 | // Look for 2 arguments. The first is always the name of the program 32 | // that was executed, and the second should be the name of the 33 | // database. 34 | if (argc != 2) { 35 | cerr << "Usage: " << argv[0] << " dbname \n"; 36 | exit(1); 37 | } 38 | 39 | // The database name is the second argument 40 | dbname = argv[1]; 41 | if (strlen(argv[1]) > (sizeof(command) - strlen(command) - 1)) { 42 | cerr << argv[1] << " length exceeds maximum allowed, cannot create database\n"; 43 | exit(1); 44 | } 45 | 46 | // Create a subdirectory for the database 47 | int error = system(strcat(command, dbname)); 48 | 49 | if (error) { 50 | cerr << "system call to create directory exited with error code " << error << endl; 51 | exit(1); 52 | } 53 | 54 | if (chdir(dbname) < 0) { 55 | cerr << argv[0] << " chdir error to " << dbname << "\n"; 56 | exit(1); 57 | } 58 | 59 | // Create the system catalogs... 60 | 61 | PF_Manager pfm; 62 | RM_Manager rmm(pfm); 63 | 64 | // Calculate the size of entries in relcat: 65 | int relCatRecSize = sizeof(RelCatEntry); 66 | int attrCatRecSize = sizeof(AttrCatEntry); 67 | 68 | if ((rc = rmm.CreateFile("relcat", relCatRecSize))) { 69 | cerr << "Trouble creating relcat. Exiting" << endl; 70 | exit(1); 71 | } 72 | 73 | if ((rc = rmm.CreateFile("attrcat", attrCatRecSize))) { 74 | cerr << "Trouble creating attrcat. Exiting" << endl; 75 | exit(1); 76 | } 77 | 78 | // Adding relation metadata of relcat and attrcat 79 | const char *(relName[MAXNAME + 1]) = {"relcat", "attrcat"}; 80 | const char *(attrName[MAXNAME + 1]) = { 81 | "relName", "tupleLength", "attrCount", "indexCount", "recordCount", 82 | "relName", "attrName", "offset", "attrType", "attrSize", "attrDisplayLength", "attrSpecs", "indexNo" 83 | }; 84 | 85 | RM_FileHandle handle; 86 | RID rid; 87 | rmm.OpenFile("relcat", handle); 88 | RelCatEntry relEntry; 89 | 90 | memcpy(relEntry.relName, relName[0], MAXNAME + 1); 91 | relEntry.tupleLength = sizeof(RelCatEntry); 92 | relEntry.attrCount = 5; 93 | relEntry.indexCount = 0; 94 | handle.InsertRec((const char *)&relEntry, rid); 95 | 96 | memcpy(relEntry.relName, relName[1], MAXNAME + 1); 97 | relEntry.tupleLength = sizeof(AttrCatEntry); 98 | relEntry.attrCount = 8; 99 | relEntry.indexCount = 0; 100 | handle.InsertRec((const char *)&relEntry, rid); 101 | 102 | rmm.CloseFile(handle); 103 | 104 | // Adding attribute metadata of relcat and attrcat 105 | rmm.OpenFile("attrcat", handle); 106 | AttrCatEntry attrEntry; 107 | attrEntry.attrSpecs = ATTR_SPEC_NOTNULL; 108 | attrEntry.indexNo = -1; 109 | 110 | memcpy(attrEntry.relName, relName[0], MAXNAME + 1); 111 | memcpy(attrEntry.attrName, attrName[0], MAXNAME + 1); 112 | attrEntry.offset = offsetof(RelCatEntry, relName); 113 | attrEntry.attrType = STRING; 114 | attrEntry.attrDisplayLength = MAXNAME + 1; 115 | handle.InsertRec((const char *)&attrEntry, rid); 116 | memcpy(attrEntry.attrName, attrName[1], MAXNAME + 1); 117 | attrEntry.offset = offsetof(RelCatEntry, tupleLength); 118 | attrEntry.attrType = INT; 119 | attrEntry.attrDisplayLength = sizeof(int); 120 | handle.InsertRec((const char *)&attrEntry, rid); 121 | memcpy(attrEntry.attrName, attrName[2], MAXNAME + 1); 122 | attrEntry.offset = offsetof(RelCatEntry, attrCount); 123 | handle.InsertRec((const char *)&attrEntry, rid); 124 | memcpy(attrEntry.attrName, attrName[3], MAXNAME + 1); 125 | attrEntry.offset = offsetof(RelCatEntry, indexCount); 126 | handle.InsertRec((const char *)&attrEntry, rid); 127 | memcpy(attrEntry.attrName, attrName[4], MAXNAME + 1); 128 | attrEntry.offset = offsetof(RelCatEntry, recordCount); 129 | handle.InsertRec((const char *)&attrEntry, rid); 130 | 131 | memcpy(attrEntry.relName, relName[1], MAXNAME + 1); 132 | memcpy(attrEntry.attrName, attrName[5], MAXNAME + 1); 133 | attrEntry.offset = offsetof(AttrCatEntry, relName); 134 | attrEntry.attrType = STRING; 135 | attrEntry.attrDisplayLength = MAXNAME + 1; 136 | handle.InsertRec((const char *)&attrEntry, rid); 137 | memcpy(attrEntry.attrName, attrName[6], MAXNAME + 1); 138 | attrEntry.offset = offsetof(AttrCatEntry, attrName); 139 | handle.InsertRec((const char *)&attrEntry, rid); 140 | memcpy(attrEntry.attrName, attrName[7], MAXNAME + 1); 141 | attrEntry.offset = offsetof(AttrCatEntry, offset); 142 | attrEntry.attrType = INT; 143 | attrEntry.attrDisplayLength = sizeof(int); 144 | handle.InsertRec((const char *)&attrEntry, rid); 145 | memcpy(attrEntry.attrName, attrName[8], MAXNAME + 1); 146 | attrEntry.offset = offsetof(AttrCatEntry, attrType); 147 | handle.InsertRec((const char *)&attrEntry, rid); 148 | memcpy(attrEntry.attrName, attrName[9], MAXNAME + 1); 149 | attrEntry.offset = offsetof(AttrCatEntry, attrSize); 150 | handle.InsertRec((const char *)&attrEntry, rid); 151 | memcpy(attrEntry.attrName, attrName[10], MAXNAME + 1); 152 | attrEntry.offset = offsetof(AttrCatEntry, attrDisplayLength); 153 | handle.InsertRec((const char *)&attrEntry, rid); 154 | memcpy(attrEntry.attrName, attrName[11], MAXNAME + 1); 155 | attrEntry.offset = offsetof(AttrCatEntry, attrSpecs); 156 | handle.InsertRec((const char *)&attrEntry, rid); 157 | memcpy(attrEntry.attrName, attrName[12], MAXNAME + 1); 158 | attrEntry.offset = offsetof(AttrCatEntry, indexNo); 159 | handle.InsertRec((const char *)&attrEntry, rid); 160 | 161 | rmm.CloseFile(handle); 162 | 163 | return (0); 164 | } 165 | -------------------------------------------------------------------------------- /src/ix.h: -------------------------------------------------------------------------------- 1 | // 2 | // ix.h 3 | // 4 | // Index Manager Component Interface 5 | // 6 | 7 | #ifndef IX_H 8 | #define IX_H 9 | 10 | // Please do not include any other files than the ones below in this file. 11 | 12 | #include "redbase.h" 13 | #include "rm_rid.h" 14 | #include "pf.h" 15 | #include "rm.h" 16 | 17 | #include 18 | #include 19 | 20 | class IX_IndexHandle; 21 | 22 | // 23 | // IX_Manager: provides IX index file management 24 | // 25 | class IX_Manager { 26 | PF_Manager *pfm; 27 | // RM_Manager rmm; 28 | 29 | public: 30 | IX_Manager (PF_Manager &pfm); // Constructor 31 | ~IX_Manager (); // Destructor 32 | RC CreateIndex (const char *fileName, // Create new index 33 | int indexNo, 34 | AttrType attrType, 35 | int attrLength); 36 | RC DestroyIndex (const char *fileName, // Destroy index 37 | int indexNo); 38 | RC OpenIndex (const char *fileName, // Open index 39 | int indexNo, 40 | IX_IndexHandle &indexHandle); 41 | RC CloseIndex (IX_IndexHandle &indexHandle); // Close index 42 | }; 43 | 44 | // 45 | // IX_IndexHandle: IX Index File interface 46 | // 47 | class IX_IndexHandle { 48 | friend class IX_Manager; 49 | friend class IX_IndexScan; 50 | 51 | PF_FileHandle pfHandle; 52 | // RM_FileHandle rmHandle; 53 | 54 | AttrType attrType; 55 | int attrLength; 56 | int root; 57 | int firstFreePage; 58 | 59 | bool isHeaderDirty; 60 | 61 | int ridsPerBucket; 62 | 63 | // use attrType and attrLength to calculate the 64 | // internal parameters 65 | void __initialize(); 66 | 67 | int b; // branch factor 68 | int entrySize; 69 | 70 | int __cmp(void* lhs, void* rhs) const; 71 | inline void* __get_entry(void* base, int n) const { 72 | return (void*)((char*)base + entrySize * n); 73 | } 74 | 75 | RC new_node(int *nodeNum); 76 | RC delete_node(int nodeNum); 77 | 78 | RC new_bucket(int *pageNum); 79 | RC delete_bucket(int pageNum); 80 | RC bucket_insert(int *pageNum, const RID &rid); 81 | RC bucket_delete(int *pageNum, const RID &rid); 82 | 83 | RC insert_internal_entry(void *header, int index, void* key, int node); 84 | RC insert_entry(void *header, void* pData, const RID &rid); 85 | RC insert_internal(int nodeNum, int *splitNode, std::unique_ptr *splitKey, void *pData, const RID &rid); 86 | RC insert_leaf(int nodeNum, int *splitNode, void *pData, const RID &rid); 87 | 88 | public: 89 | IX_IndexHandle (); // Constructor 90 | ~IX_IndexHandle (); // Destructor 91 | RC InsertEntry (void *pData, const RID &rid); // Insert new index entry 92 | RC DeleteEntry (void *pData, const RID &rid); // Delete index entry 93 | RC ForcePages (); // Copy index to disk 94 | 95 | RC Traverse(int nodeNum = 0, int depth = 0); 96 | }; 97 | 98 | // 99 | // IX_IndexScan: condition-based scan of index entries 100 | // 101 | class IX_IndexScan { 102 | const IX_IndexHandle *indexHandle; 103 | CompOp compOp; 104 | void* value; 105 | 106 | bool scanOpened; 107 | int currentNodeNum; 108 | int currentEntryIndex; 109 | int currentBucketIndex; 110 | 111 | bool __check(void* key); 112 | 113 | public: 114 | IX_IndexScan (); // Constructor 115 | ~IX_IndexScan (); // Destructor 116 | RC OpenScan (const IX_IndexHandle &indexHandle, // Initialize index scan 117 | CompOp compOp, 118 | void *value, 119 | ClientHint pinHint = NO_HINT); 120 | RC GetNextEntry (RID &rid); // Get next matching entry 121 | RC CloseScan (); // Terminate index scan 122 | }; 123 | 124 | // 125 | // Print-error function 126 | // 127 | void IX_PrintError(RC rc); 128 | 129 | #define IX_EOF (START_IX_WARN + 0) 130 | #define IX_ENTRY_EXISTS (START_IX_WARN + 1) 131 | #define IX_ENTRY_DOES_NOT_EXIST (START_IX_WARN + 2) 132 | #define IX_SCAN_NOT_OPENED (START_IX_WARN + 3) 133 | #define IX_SCAN_NOT_CLOSED (START_IX_WARN + 4) 134 | #define IX_BUCKET_FULL (START_IX_WARN + 5) 135 | #define IX_LASTWARN IX_SCAN_NOT_CLOSED 136 | 137 | 138 | #define IX_ATTR_TOO_LARGE (START_IX_ERR - 0) 139 | #define IX_LASTERROR IX_ATTR_TOO_LARGE 140 | 141 | #endif // IX_H 142 | -------------------------------------------------------------------------------- /src/ix_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: ix_error.cc 3 | // Description: IX_PrintError function 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ix.h" 10 | 11 | using namespace std; 12 | 13 | // 14 | // Error table 15 | // 16 | const char *IX_WarnMsg[] = { 17 | 18 | }; 19 | 20 | const char *IX_ErrorMsg[] = { 21 | 22 | }; 23 | 24 | // 25 | // IX_PrintError 26 | // 27 | // Desc: Send a message corresponding to a IX return code to cerr 28 | // In: rc - return code for which a message is desired 29 | // 30 | void IX_PrintError(RC rc) { 31 | // Check the return code is within proper limits 32 | if (rc >= START_IX_WARN && rc <= IX_LASTWARN) 33 | // Print warning 34 | cerr << "IX warning: " << IX_WarnMsg[rc - START_IX_WARN] << "\n"; 35 | // Error codes are negative, so invert everything 36 | else if ((-rc >= -START_IX_ERR) && -rc <= -IX_LASTERROR) { 37 | // Print error 38 | cerr << "IX error: " << IX_ErrorMsg[-rc + START_IX_ERR] << "\n"; 39 | } 40 | else if (rc == 0) 41 | cerr << "IX_PrintError called with return code of 0\n"; 42 | else { 43 | // Print error 44 | cerr << "rc was " << rc << endl; 45 | cerr << "START_IX_ERR was " << START_IX_ERR << endl; 46 | cerr << "IX_LASTERROR was " << IX_LASTERROR << endl; 47 | cerr << "IX error: " << rc << " is out of bounds\n"; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ix_indexscan.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/12/24. 3 | // 4 | 5 | #include "ix.h" 6 | #include "ix_internal.h" 7 | 8 | IX_IndexScan::IX_IndexScan() { 9 | scanOpened = false; 10 | } 11 | 12 | IX_IndexScan::~IX_IndexScan() { } 13 | 14 | bool IX_IndexScan::__check(void* key) { 15 | #define CMP_RESULT (indexHandle->__cmp(key, this->value)) 16 | switch (compOp) { 17 | case GT_OP: 18 | case GE_OP: 19 | // initial search has done all the work 20 | return true; 21 | case EQ_OP: 22 | return CMP_RESULT == 0; 23 | case NE_OP: 24 | return CMP_RESULT != 0; 25 | case LT_OP: 26 | return CMP_RESULT < 0; 27 | case LE_OP: 28 | return CMP_RESULT <= 0; 29 | case NO_OP: 30 | return true; 31 | case ISNULL_OP: 32 | case NOTNULL_OP: 33 | LOG(FATAL) << "null is not supported in IX"; 34 | } 35 | } 36 | 37 | RC IX_IndexScan::OpenScan(const IX_IndexHandle &indexHandle, CompOp compOp, void *value, ClientHint pinHint) { 38 | if (scanOpened) { 39 | return IX_SCAN_NOT_CLOSED; 40 | } 41 | this->indexHandle = &indexHandle; 42 | this->compOp = compOp; 43 | this->value = value; 44 | bool initial_search_needed = false; 45 | switch (compOp) { 46 | case GT_OP: 47 | case GE_OP: 48 | initial_search_needed = true; 49 | break; 50 | case NO_OP: 51 | case EQ_OP: 52 | case NE_OP: 53 | case LT_OP: 54 | case LE_OP: 55 | initial_search_needed = false; 56 | break; 57 | case ISNULL_OP: 58 | case NOTNULL_OP: 59 | LOG(FATAL) << "null is not supported in IX"; 60 | } 61 | currentNodeNum = indexHandle.root; 62 | currentEntryIndex = 0; 63 | currentBucketIndex = 0; 64 | const PF_FileHandle &file = indexHandle.pfHandle; 65 | bool should_stop = false; 66 | while (!should_stop) { 67 | PF_PageHandle page; 68 | IX_PageHeader *header; 69 | TRY(file.GetThisPage(currentNodeNum, page)); 70 | int openedPageNum = currentNodeNum; 71 | TRY(page.GetData(CVOID(header))); 72 | if (header->type == kLeafNode) { 73 | if (compOp == GT_OP || compOp == GE_OP) { 74 | for (currentEntryIndex = 0; currentEntryIndex < header->childrenNum; 75 | ++currentEntryIndex) { 76 | Entry *entry = (Entry*)indexHandle.__get_entry( 77 | header->entries, currentEntryIndex); 78 | int c = indexHandle.__cmp(entry->key, value); 79 | if ((compOp == GT_OP && c > 0) || 80 | (compOp == GE_OP && c >= 0)) { 81 | break; 82 | } 83 | } 84 | } 85 | should_stop = true; 86 | } else { 87 | int index = 0; 88 | if (compOp == GT_OP || compOp == GE_OP) { 89 | index = header->childrenNum - 1; 90 | for (int i = 0; i < header->childrenNum - 1; ++i) { 91 | if (indexHandle.__cmp(((Entry*)indexHandle.__get_entry( 92 | header->entries, i))->key, value) > 0) { 93 | index = i; 94 | break; 95 | } 96 | } 97 | } 98 | currentNodeNum = ((Entry*)indexHandle.__get_entry( 99 | header->entries, index))->pageNum; 100 | } 101 | TRY(file.UnpinPage(openedPageNum)); 102 | } 103 | scanOpened = true; 104 | return 0; 105 | } 106 | 107 | RC IX_IndexScan::GetNextEntry(RID &rid) { 108 | if (!scanOpened) { 109 | return IX_SCAN_NOT_OPENED; 110 | } 111 | const PF_FileHandle &file = indexHandle->pfHandle; 112 | PF_PageHandle page; 113 | int ret = 0; 114 | bool should_exit = false; 115 | while (!should_exit) { 116 | IX_PageHeader* header; 117 | int openedPageNum = currentNodeNum; 118 | TRY(file.GetThisPage(currentNodeNum, page)); 119 | TRY(page.GetData(CVOID(header))); 120 | Entry* entry = (Entry*)indexHandle->__get_entry(header->entries, currentEntryIndex); 121 | if (currentEntryIndex == header->childrenNum) { 122 | if (entry->pageNum == kNullNode) { 123 | ret = IX_EOF; 124 | should_exit = true; 125 | } else { 126 | currentNodeNum = entry->pageNum; 127 | currentEntryIndex = 0; 128 | currentBucketIndex = 0; 129 | } 130 | } else { 131 | if (__check(entry->key)) { 132 | if (entry->pageNum == kInvalidBucket) { 133 | ++currentEntryIndex; 134 | currentBucketIndex = 0; 135 | } else { 136 | PF_PageHandle bucket; 137 | IX_BucketHeader *bucket_header; 138 | TRY(file.GetThisPage(entry->pageNum, bucket)); 139 | TRY(bucket.GetData(CVOID(bucket_header))); 140 | // NOTE: currentBucketIndex may be greater than bucket_header->ridNum 141 | // as a result of deletion during the scan 142 | if (currentBucketIndex >= bucket_header->ridNum) { 143 | ++currentEntryIndex; 144 | currentBucketIndex = 0; 145 | } else { 146 | rid = bucket_header->rids[currentBucketIndex]; 147 | ++currentBucketIndex; 148 | should_exit = true; 149 | } 150 | TRY(file.UnpinPage(entry->pageNum)); 151 | } 152 | } else { 153 | if (compOp == LT_OP || compOp == LE_OP) { 154 | ret = IX_EOF; 155 | should_exit = true; 156 | } else { 157 | ++currentEntryIndex; 158 | currentBucketIndex = 0; 159 | } 160 | } 161 | } 162 | TRY(file.UnpinPage(openedPageNum)); 163 | } 164 | return ret; 165 | } 166 | 167 | RC IX_IndexScan::CloseScan() { 168 | scanOpened = false; 169 | return 0; 170 | } 171 | -------------------------------------------------------------------------------- /src/ix_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "redbase.h" 4 | #include "rm_rid.h" 5 | 6 | static const int kLastFreePage = -1; 7 | static const int kNullNode = -1; 8 | static const int kInvalidBucket = -1; 9 | 10 | struct IX_FileHeader { 11 | AttrType attrType; 12 | int attrLength; 13 | int root; 14 | int firstFreePage; 15 | }; 16 | 17 | enum IX_NodeType { 18 | kInternalNode, 19 | kLeafNode, 20 | }; 21 | 22 | struct IX_PageHeader { 23 | short type; 24 | short childrenNum; 25 | char entries[4]; 26 | }; 27 | 28 | struct Entry { 29 | // internal nodes: pageNum = the pageNum of child 30 | // leaf nodes: pageNum is the page no. of corresponding bucket, with the exception that 31 | // the last entry (entries[header->childrenNum]) 32 | // contains the pageNum to next leaf node 33 | int pageNum; 34 | char key[4]; 35 | }; 36 | 37 | struct IX_BucketHeader { 38 | int ridNum; 39 | RID rids[1]; 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /src/ix_manager.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/12/24. 3 | // 4 | 5 | #include "ix.h" 6 | #include "ix_internal.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | static std::string filename_gen(const char* fileName, int indexNo) { 13 | std::ostringstream oss; 14 | oss << fileName << "." << indexNo; 15 | return oss.str(); 16 | } 17 | 18 | IX_Manager::IX_Manager(PF_Manager &pfm) /* : rmm(pfm) */ { 19 | this->pfm = &pfm; 20 | } 21 | 22 | IX_Manager::~IX_Manager() { } 23 | 24 | RC IX_Manager::CreateIndex(const char *fileName, int indexNo, AttrType attrType, int attrLength) { 25 | int attrSize = upper_align<4>(attrLength); 26 | if (offsetof(IX_PageHeader, entries) + attrSize + 2 * offsetof(Entry, key) > PF_PAGE_SIZE) { 27 | return IX_ATTR_TOO_LARGE; 28 | } 29 | std::string indexFileName = filename_gen(fileName, indexNo); 30 | TRY(pfm->CreateFile(indexFileName.c_str())); 31 | PF_FileHandle fileHandle; 32 | PF_PageHandle pageHandle; 33 | TRY(pfm->OpenFile(indexFileName.c_str(), fileHandle)); 34 | 35 | // create file header 36 | IX_FileHeader *fileHeader; 37 | TRY(fileHandle.AllocatePage(pageHandle)); 38 | TRY(pageHandle.GetData(CVOID(fileHeader))); 39 | fileHeader->attrType = attrType; 40 | fileHeader->attrLength = attrLength; 41 | fileHeader->root = 1; 42 | fileHeader->firstFreePage = kLastFreePage; 43 | TRY(fileHandle.MarkDirty(0)); 44 | TRY(fileHandle.UnpinPage(0)); 45 | 46 | // create root node 47 | IX_PageHeader *root; 48 | TRY(fileHandle.AllocatePage(pageHandle)); 49 | TRY(pageHandle.GetData(CVOID(root))); 50 | root->type = kLeafNode; 51 | root->childrenNum = 0; 52 | Entry* root_first_entry = (Entry*)(root->entries); 53 | root_first_entry->pageNum = kNullNode; 54 | TRY(fileHandle.MarkDirty(1)); 55 | TRY(fileHandle.UnpinPage(1)); 56 | 57 | TRY(pfm->CloseFile(fileHandle)); 58 | 59 | // RM_Manager rmm(*pfm); 60 | // TRY(rmm.CreateFile((indexFileName + "r").c_str(), sizeof(RID))); 61 | return 0; 62 | } 63 | 64 | RC IX_Manager::DestroyIndex(const char *fileName, int indexNo) { 65 | std::string indexFileName = filename_gen(fileName, indexNo); 66 | TRY(pfm->DestroyFile(indexFileName.c_str())); 67 | 68 | // TRY(rmm.DestroyFile((indexFileName + "r").c_str())); 69 | return 0; 70 | } 71 | 72 | RC IX_Manager::OpenIndex(const char *fileName, int indexNo, IX_IndexHandle &indexHandle) { 73 | std::string indexFileName = filename_gen(fileName, indexNo); 74 | PF_FileHandle &fileHandle = indexHandle.pfHandle; 75 | PF_PageHandle pageHandle; 76 | IX_FileHeader *fileHeader; 77 | 78 | TRY(pfm->OpenFile(indexFileName.c_str(), fileHandle)); 79 | TRY(fileHandle.GetFirstPage(pageHandle)); 80 | TRY(pageHandle.GetData(CVOID(fileHeader))); 81 | indexHandle.attrType = fileHeader->attrType; 82 | indexHandle.attrLength = fileHeader->attrLength; 83 | indexHandle.root = fileHeader->root; 84 | indexHandle.firstFreePage = fileHeader->firstFreePage; 85 | indexHandle.isHeaderDirty = false; 86 | TRY(fileHandle.UnpinPage(0)); 87 | // the initialization MUST come after information in the 88 | // header copied into the handle 89 | indexHandle.__initialize(); 90 | 91 | // RM_FileHandle &rmHandle = indexHandle.rmHandle; 92 | // TRY(rmm.OpenFile((indexFileName + "r").c_str(), rmHandle)); 93 | 94 | return 0; 95 | } 96 | 97 | RC IX_Manager::CloseIndex(IX_IndexHandle &indexHandle) { 98 | PF_FileHandle &fileHandle = indexHandle.pfHandle; 99 | 100 | if (indexHandle.isHeaderDirty) { 101 | PF_PageHandle pageHandle; 102 | IX_FileHeader *fileHeader; 103 | 104 | TRY(fileHandle.GetFirstPage(pageHandle)); 105 | TRY(pageHandle.GetData(CVOID(fileHeader))); 106 | fileHeader->root = indexHandle.root; 107 | fileHeader->firstFreePage = indexHandle.firstFreePage; 108 | 109 | TRY(fileHandle.MarkDirty(0)); 110 | TRY(fileHandle.UnpinPage(0)); 111 | } 112 | 113 | TRY(pfm->CloseFile(fileHandle)); 114 | 115 | // RM_FileHandle &rmHandle = indexHandle.rmHandle; 116 | // TRY(rmm.CloseFile(rmHandle)); 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /src/ix_test.cpp: -------------------------------------------------------------------------------- 1 | #include "ix.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | const char* kFileName = "ixt"; 13 | 14 | // 15 | // Function declarations 16 | // 17 | RC Test1(void); 18 | RC Test2(void); 19 | RC Test3(void); 20 | RC Test4(void); 21 | RC Test5(void); 22 | 23 | 24 | int (*tests[])() = // RC doesn't work on some compilers 25 | { 26 | Test1, 27 | Test2, 28 | Test3, 29 | Test4, 30 | Test5, 31 | }; 32 | #define NUM_TESTS ((int)((sizeof(tests)) / sizeof(tests[0]))) // number of tests 33 | 34 | PF_Manager pfm; 35 | IX_Manager ixm(pfm); 36 | 37 | static void Test_PrintError(RC rc) 38 | { 39 | if (abs(rc) <= END_PF_WARN) 40 | PF_PrintError(rc); 41 | else if (abs(rc) <= END_RM_WARN) 42 | IX_PrintError(rc); 43 | else 44 | LOG(INFO) << "Error code out of range: " << rc << "\n"; 45 | } 46 | 47 | int main(int argc, char *argv[]) { 48 | FLAGS_logtostderr = true; 49 | gflags::ParseCommandLineFlags(&argc, &argv, true); 50 | google::InitGoogleLogging(argv[0]); 51 | 52 | RC rc; 53 | char *progName = argv[0]; // since we will be changing argv 54 | int testNum; 55 | 56 | // Delete files from last time 57 | system((std::string("rm ") + kFileName + ".*").c_str()); 58 | 59 | // If no argument given, do all tests 60 | if (argc == 1) { 61 | for (testNum = 0; testNum < NUM_TESTS; testNum++) 62 | if ((rc = (tests[testNum])())) { 63 | 64 | // Print the error and exit 65 | Test_PrintError(rc); 66 | return (1); 67 | } 68 | } 69 | else { 70 | 71 | // Otherwise, perform specific tests 72 | while (*++argv != NULL) { 73 | 74 | // Make sure it's a number 75 | if (sscanf(*argv, "%d", &testNum) != 1) { 76 | LOG(INFO) << progName << ": " << *argv << " is not a number\n"; 77 | continue; 78 | } 79 | 80 | // Make sure it's in range 81 | if (testNum < 1 || testNum > NUM_TESTS) { 82 | LOG(INFO) << "Valid test numbers are between 1 and " << NUM_TESTS << "\n"; 83 | continue; 84 | } 85 | 86 | // Perform the test 87 | if ((rc = (tests[testNum - 1])())) { 88 | 89 | // Print the error and exit 90 | Test_PrintError(rc); 91 | return (1); 92 | } 93 | } 94 | } 95 | 96 | // Write ending message and exit 97 | LOG(INFO) << "Ending IX component test.\n\n"; 98 | 99 | return 0; 100 | } 101 | 102 | inline RC check_rid_eq(const RID &lhs, const RID &rhs) { 103 | PageNum a, b; 104 | SlotNum u, v; 105 | TRY(lhs.GetPageNum(a)); 106 | TRY(rhs.GetPageNum(b)); 107 | TRY(lhs.GetSlotNum(u)); 108 | TRY(rhs.GetSlotNum(v)); 109 | CHECK(a == b); 110 | CHECK(u == v); 111 | return 0; 112 | } 113 | 114 | RC Test1() { 115 | LOG(INFO) << "test1"; 116 | IX_IndexHandle ih; 117 | // CHECK(ixm.CloseIndex(ih) != 0); 118 | TRY(ixm.CreateIndex(kFileName, 0, INT, 4)); 119 | TRY(ixm.OpenIndex(kFileName, 0, ih)); 120 | // CHECK(ixm.DestroyIndex(kFileName, 1) != 0); 121 | TRY(ixm.CloseIndex(ih)); 122 | TRY(ixm.DestroyIndex(kFileName, 0)); 123 | return 0; 124 | } 125 | 126 | RC Test2() { 127 | LOG(INFO) << "test2"; 128 | IX_IndexHandle ih; 129 | TRY(ixm.CreateIndex(kFileName, 1, INT, 4)); 130 | TRY(ixm.OpenIndex(kFileName, 1, ih)); 131 | RID rid0(0, 0), rid1(1, 1); 132 | int n0 = 4, n1 = 1; 133 | TRY(ih.InsertEntry(&n0, rid0)); 134 | TRY(ih.InsertEntry(&n1, rid1)); 135 | // now the index contains (1, (1, 1)), (4, (0, 0)) 136 | 137 | IX_IndexScan sc; 138 | TRY(sc.OpenScan(ih, NO_OP, NULL)); 139 | RID rid; 140 | 141 | TRY(sc.GetNextEntry(rid)); 142 | TRY(check_rid_eq(rid, rid1)); 143 | TRY(sc.GetNextEntry(rid)); 144 | TRY(check_rid_eq(rid, rid0)); 145 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 146 | TRY(sc.CloseScan()); 147 | 148 | int eqcmpi = 1; 149 | TRY(sc.OpenScan(ih, EQ_OP, &eqcmpi)); 150 | TRY(sc.GetNextEntry(rid)); 151 | TRY(check_rid_eq(rid, rid1)); 152 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 153 | TRY(sc.CloseScan()); 154 | 155 | int necmpi = 1; 156 | TRY(sc.OpenScan(ih, NE_OP, &necmpi)); 157 | TRY(sc.GetNextEntry(rid)); 158 | TRY(check_rid_eq(rid, rid0)); 159 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 160 | TRY(sc.CloseScan()); 161 | 162 | int gecmpi = 4; 163 | TRY(sc.OpenScan(ih, GE_OP, &gecmpi)); 164 | TRY(sc.GetNextEntry(rid)); 165 | TRY(check_rid_eq(rid, rid0)); 166 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 167 | TRY(sc.CloseScan()); 168 | 169 | int gtcmpi = 1; 170 | TRY(sc.OpenScan(ih, GT_OP, >cmpi)); 171 | TRY(sc.GetNextEntry(rid)); 172 | TRY(check_rid_eq(rid, rid0)); 173 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 174 | TRY(sc.CloseScan()); 175 | 176 | int ltcmpi = 4; 177 | TRY(sc.OpenScan(ih, LT_OP, <cmpi)); 178 | TRY(sc.GetNextEntry(rid)); 179 | TRY(check_rid_eq(rid, rid1)); 180 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 181 | TRY(sc.CloseScan()); 182 | 183 | int lecmpi = 1; 184 | TRY(sc.OpenScan(ih, LE_OP, &lecmpi)); 185 | TRY(sc.GetNextEntry(rid)); 186 | TRY(check_rid_eq(rid, rid1)); 187 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 188 | TRY(sc.CloseScan()); 189 | 190 | int lecmpi2 = 4; 191 | TRY(sc.OpenScan(ih, LE_OP, &lecmpi2)); 192 | TRY(sc.GetNextEntry(rid)); 193 | TRY(check_rid_eq(rid, rid1)); 194 | TRY(sc.GetNextEntry(rid)); 195 | TRY(check_rid_eq(rid, rid0)); 196 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 197 | TRY(sc.CloseScan()); 198 | 199 | TRY(ixm.CloseIndex(ih)); 200 | TRY(ixm.DestroyIndex(kFileName, 1)); 201 | return 0; 202 | } 203 | 204 | auto default_rid_gen = [](int n) { 205 | return RID(n, n); 206 | }; 207 | 208 | RC Test3() { 209 | LOG(INFO) << "test3"; 210 | IX_IndexHandle ih; 211 | TRY(ixm.CreateIndex(kFileName, 1, INT, 4)); 212 | TRY(ixm.OpenIndex(kFileName, 1, ih)); 213 | 214 | auto rid_gen = [](int n) { 215 | return RID(n + 1, n * 8); 216 | }; 217 | 218 | const int n = 1; 219 | 220 | for (int i = 0; i < n; ++i) { 221 | TRY(ih.InsertEntry(&i, rid_gen(i))); 222 | } 223 | 224 | IX_IndexScan sc; 225 | TRY(sc.OpenScan(ih, NO_OP, NULL)); 226 | RID rid; 227 | 228 | for (int i = 0; i < n; ++i) { 229 | TRY(sc.GetNextEntry(rid)); 230 | TRY(check_rid_eq(rid, rid_gen(i))); 231 | } 232 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 233 | TRY(sc.CloseScan()); 234 | 235 | TRY(ixm.CloseIndex(ih)); 236 | TRY(ixm.DestroyIndex(kFileName, 1)); 237 | return 0; 238 | } 239 | 240 | RC Test4() { 241 | LOG(INFO) << "test4"; 242 | IX_IndexHandle ih; 243 | TRY(ixm.CreateIndex(kFileName, 1, INT, 4)); 244 | TRY(ixm.OpenIndex(kFileName, 1, ih)); 245 | 246 | auto rid_gen = default_rid_gen; 247 | 248 | const int n = 1000; 249 | 250 | for (int i = n - 1; i >= 0; --i) { 251 | TRY(ih.InsertEntry(&i, rid_gen(i))); 252 | } 253 | 254 | int gtcmpi = 500; 255 | IX_IndexScan sc; 256 | TRY(sc.OpenScan(ih, GT_OP, >cmpi)); 257 | RID rid; 258 | 259 | for (int i = gtcmpi + 1; i < n; ++i) { 260 | int pageNum, slotNum; 261 | TRY(sc.GetNextEntry(rid)); 262 | TRY(rid.GetPageNum(pageNum)); 263 | TRY(rid.GetSlotNum(slotNum)); 264 | // LOG(INFO) << "[" << pageNum << ", " << slotNum << "]"; 265 | TRY(check_rid_eq(rid, rid_gen(i))); 266 | } 267 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 268 | TRY(sc.CloseScan()); 269 | 270 | TRY(ixm.CloseIndex(ih)); 271 | TRY(ixm.DestroyIndex(kFileName, 1)); 272 | return 0; 273 | } 274 | 275 | extern void WriteLog(const char* psMessage); 276 | 277 | RC Test5() { 278 | LOG(INFO) << "test5"; 279 | IX_IndexHandle ih; 280 | TRY(ixm.CreateIndex(kFileName, 1, INT, 4)); 281 | TRY(ixm.OpenIndex(kFileName, 1, ih)); 282 | 283 | auto rid_gen = default_rid_gen; 284 | 285 | const int n = 1000; 286 | 287 | for (int i = 0; i < n; ++i) { 288 | // LOG(INFO) << "Insert i = " << i; 289 | TRY(ih.InsertEntry(&i, rid_gen(i))); 290 | } 291 | 292 | // ih.Traverse(); 293 | 294 | int ltcmpi = 800; 295 | IX_IndexScan sc; 296 | TRY(sc.OpenScan(ih, LT_OP, <cmpi)); 297 | RID rid; 298 | int pageNum, slotNum; 299 | 300 | for (int i = 0; i < ltcmpi; ++i) { 301 | TRY(sc.GetNextEntry(rid)); 302 | TRY(rid.GetPageNum(pageNum)); 303 | TRY(rid.GetSlotNum(slotNum)); 304 | // LOG(INFO) << "to delete: [" << pageNum << ", " << slotNum << "]"; 305 | TRY(check_rid_eq(rid, rid_gen(i))); 306 | TRY(ih.DeleteEntry(&i, rid_gen(i))); 307 | } 308 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 309 | TRY(sc.CloseScan()); 310 | 311 | // ih.Traverse(); 312 | 313 | TRY(sc.OpenScan(ih, NO_OP, NULL)); 314 | for (int i = ltcmpi; i < n; ++i) { 315 | TRY(sc.GetNextEntry(rid)); 316 | TRY(rid.GetPageNum(pageNum)); 317 | TRY(rid.GetSlotNum(slotNum)); 318 | // LOG(INFO) << "get [" << pageNum << ", " << slotNum << "]"; 319 | TRY(check_rid_eq(rid, rid_gen(i))); 320 | } 321 | CHECK(sc.GetNextEntry(rid) == IX_EOF); 322 | TRY(sc.CloseScan()); 323 | 324 | TRY(ixm.CloseIndex(ih)); 325 | TRY(ixm.DestroyIndex(kFileName, 1)); 326 | return 0; 327 | } 328 | -------------------------------------------------------------------------------- /src/nodes.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * nodes.c: functions to allocate an initialize parse-tree nodes 3 | * 4 | * Authors; Dallan Quass 5 | * Jan Jannink 6 | * 7 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "redbase.h" 16 | #include "parser_internal.h" 17 | #include "y.tab.h" 18 | 19 | /* 20 | * total number of nodes available for a given parse-tree 21 | */ 22 | #define MAXNODE 8192 23 | 24 | static NODE nodepool[MAXNODE]; 25 | static int nodeptr = 0; 26 | 27 | /* 28 | * reset_parser: resets the scanner and parser when a syntax error occurs 29 | * 30 | * No return value 31 | */ 32 | void reset_parser(void) { 33 | reset_scanner(); 34 | nodeptr = 0; 35 | } 36 | 37 | static void (*cleanup_func)() = NULL; 38 | 39 | /* 40 | * new_query: prepares for a new query by releasing all resources used 41 | * by previous queries. 42 | * 43 | * No return value. 44 | */ 45 | void new_query(void) { 46 | nodeptr = 0; 47 | reset_charptr(); 48 | if (cleanup_func != NULL) 49 | (*cleanup_func)(); 50 | } 51 | 52 | void register_cleanup_function(void (*func)()) { 53 | cleanup_func = func; 54 | } 55 | 56 | /* 57 | * newnode: allocates a new node of the specified kind and returns a pointer 58 | * to it on success. Returns NULL on error. 59 | */ 60 | NODE *newnode(NODEKIND kind) { 61 | NODE *n; 62 | 63 | /* if we've used up all of the nodes then error */ 64 | if (nodeptr == MAXNODE) { 65 | fprintf(stderr, "%s:%s out of memory\n", __FILE__, __FUNCTION__); 66 | exit(1); 67 | } 68 | 69 | /* get the next node */ 70 | n = nodepool + nodeptr; 71 | ++nodeptr; 72 | 73 | /* initialize the `kind' field */ 74 | n -> kind = kind; 75 | return n; 76 | } 77 | 78 | NODE *show_dbs_node() { 79 | return newnode(N_SHOWDBS); 80 | } 81 | 82 | NODE *create_db_node(char *relname) { 83 | NODE *n = newnode(N_CREATEDB); 84 | n->u.DB_OP.relname = relname; 85 | return n; 86 | } 87 | 88 | NODE *drop_db_node(char *relname) { 89 | NODE *n = newnode(N_DROPDB); 90 | n->u.DB_OP.relname = relname; 91 | return n; 92 | } 93 | 94 | NODE *use_db_node(char *relname) { 95 | NODE *n = newnode(N_USEDB); 96 | n->u.DB_OP.relname = relname; 97 | return n; 98 | } 99 | 100 | NODE *show_tables_node() { 101 | return newnode(N_SHOWTABLES); 102 | } 103 | 104 | /* 105 | * create_table_node: allocates, initializes, and returns a pointer to a new 106 | * create table node having the indicated values. 107 | */ 108 | NODE *create_table_node(char *relname, NODE *attrlist) { 109 | NODE *n = newnode(N_CREATETABLE); 110 | 111 | n -> u.CREATETABLE.relname = relname; 112 | n -> u.CREATETABLE.attrlist = attrlist; 113 | return n; 114 | } 115 | 116 | /* 117 | * create_index_node: allocates, initializes, and returns a pointer to a new 118 | * create index node having the indicated values. 119 | */ 120 | NODE *create_index_node(char *relname, char *attrname) { 121 | NODE *n = newnode(N_CREATEINDEX); 122 | 123 | n -> u.CREATEINDEX.relname = relname; 124 | n -> u.CREATEINDEX.attrname = attrname; 125 | return n; 126 | } 127 | 128 | /* 129 | * drop_index_node: allocates, initializes, and returns a pointer to a new 130 | * drop index node having the indicated values. 131 | */ 132 | NODE *drop_index_node(char *relname, char *attrname) { 133 | NODE *n = newnode(N_DROPINDEX); 134 | 135 | n -> u.DROPINDEX.relname = relname; 136 | n -> u.DROPINDEX.attrname = attrname; 137 | return n; 138 | } 139 | 140 | /* 141 | * drop_table_node: allocates, initializes, and returns a pointer to a new 142 | * drop table node having the indicated values. 143 | */ 144 | NODE *drop_table_node(char *relname) { 145 | NODE *n = newnode(N_DROPTABLE); 146 | 147 | n -> u.DROPTABLE.relname = relname; 148 | return n; 149 | } 150 | 151 | /* 152 | * load_node: allocates, initializes, and returns a pointer to a new 153 | * load node having the indicated values. 154 | */ 155 | NODE *load_node(char *relname, char *filename) { 156 | NODE *n = newnode(N_LOAD); 157 | 158 | n -> u.LOAD.relname = relname; 159 | n -> u.LOAD.filename = filename; 160 | return n; 161 | } 162 | 163 | /* 164 | * set_node: allocates, initializes, and returns a pointer to a new 165 | * set node having the indicated values. 166 | */ 167 | NODE *set_node(char *paramName, char *string) { 168 | NODE *n = newnode(N_SET); 169 | 170 | n -> u.SET.paramName = paramName; 171 | n -> u.SET.string = string; 172 | return n; 173 | } 174 | 175 | /* 176 | * help_node: allocates, initializes, and returns a pointer to a new 177 | * help node having the indicated values. 178 | */ 179 | NODE *help_node(char *relname) { 180 | NODE *n = newnode(N_HELP); 181 | 182 | n -> u.HELP.relname = relname; 183 | return n; 184 | } 185 | 186 | /* 187 | * print_node: allocates, initializes, and returns a pointer to a new 188 | * print node having the indicated values. 189 | */ 190 | NODE *print_node(char *relname) { 191 | NODE *n = newnode(N_PRINT); 192 | 193 | n -> u.PRINT.relname = relname; 194 | return n; 195 | } 196 | 197 | /* 198 | * query_node: allocates, initializes, and returns a pointer to a new 199 | * query node having the indicated values. 200 | */ 201 | NODE *query_node(NODE *relattrlist, NODE *rellist, NODE *conditionlist) { 202 | NODE *n = newnode(N_QUERY); 203 | 204 | n->u.QUERY.relattrlist = relattrlist; 205 | n->u.QUERY.rellist = rellist; 206 | n->u.QUERY.conditionlist = conditionlist; 207 | return n; 208 | } 209 | 210 | /* 211 | * insert_node: allocates, initializes, and returns a pointer to a new 212 | * insert node having the indicated values. 213 | */ 214 | NODE *insert_node(char *relname, NODE *valuelist) { 215 | NODE *n = newnode(N_INSERT); 216 | 217 | n->u.INSERT.relname = relname; 218 | n->u.INSERT.valuelist = valuelist; 219 | return n; 220 | } 221 | 222 | /* 223 | * delete_node: allocates, initializes, and returns a pointer to a new 224 | * delete node having the indicated values. 225 | */ 226 | NODE *delete_node(char *relname, NODE *conditionlist) { 227 | NODE *n = newnode(N_DELETE); 228 | 229 | n->u.DELETE.relname = relname; 230 | n->u.DELETE.conditionlist = conditionlist; 231 | return n; 232 | } 233 | 234 | /* 235 | * update_node: allocates, initializes, and returns a pointer to a new 236 | * update node having the indicated values. 237 | */ 238 | NODE *update_node(char *relname, NODE *relattr, NODE *relorvalue, 239 | NODE *conditionlist) { 240 | NODE *n = newnode(N_UPDATE); 241 | 242 | n->u.UPDATE.relname = relname; 243 | n->u.UPDATE.relattr = relattr; 244 | n->u.UPDATE.relorvalue = relorvalue; 245 | n->u.UPDATE.conditionlist = conditionlist; 246 | return n; 247 | } 248 | 249 | 250 | /* 251 | * relattr_node: allocates, initializes, and returns a pointer to a new 252 | * relattr node having the indicated values. 253 | */ 254 | NODE *relattr_node(char *relname, char *attrname) { 255 | NODE *n = newnode(N_RELATTR); 256 | 257 | n -> u.RELATTR.relname = relname; 258 | n -> u.RELATTR.attrname = attrname; 259 | return n; 260 | } 261 | 262 | /* 263 | * condition_node: allocates, initializes, and returns a pointer to a new 264 | * condition node having the indicated values. 265 | */ 266 | NODE *condition_node(NODE *lhsRelattr, CompOp op, NODE *rhsRelattrOrValue) { 267 | NODE *n = newnode(N_CONDITION); 268 | 269 | n->u.CONDITION.lhsRelattr = lhsRelattr; 270 | n->u.CONDITION.op = op; 271 | if (rhsRelattrOrValue) { 272 | // binary operator 273 | n->u.CONDITION.rhsRelattr = 274 | rhsRelattrOrValue->u.RELATTR_OR_VALUE.relattr; 275 | n->u.CONDITION.rhsValue = 276 | rhsRelattrOrValue->u.RELATTR_OR_VALUE.value; 277 | } 278 | return n; 279 | } 280 | 281 | /* 282 | * value_node: allocates, initializes, and returns a pointer to a new 283 | * value node having the indicated values. 284 | */ 285 | NODE *value_node(AttrType type, void *value) { 286 | NODE *n = newnode(N_VALUE); 287 | 288 | n->u.VALUE.type = type; 289 | switch (type) { 290 | case INT: 291 | n->u.VALUE.ival = *(int *)value; 292 | break; 293 | case FLOAT: 294 | n->u.VALUE.rval = *(float *)value; 295 | break; 296 | case STRING: 297 | n->u.VALUE.sval = (char *)value; 298 | break; 299 | } 300 | return n; 301 | } 302 | 303 | /* 304 | * relattr_or_valuenode: allocates, initializes, and returns a pointer to 305 | * a new relattr_or_value node having the indicated values. 306 | */ 307 | NODE *relattr_or_value_node(NODE *relattr, NODE *value) { 308 | NODE *n = newnode(N_RELATTR_OR_VALUE); 309 | 310 | n->u.RELATTR_OR_VALUE.relattr = relattr; 311 | n->u.RELATTR_OR_VALUE.value = value; 312 | return n; 313 | } 314 | 315 | /* 316 | * attrtype_node: allocates, initializes, and returns a pointer to a new 317 | * attrtype node having the indicated values. 318 | */ 319 | NODE *attrtype_node(char *attrname, char *type, int size, enum AttrSpec spec) { 320 | NODE *n = newnode(N_ATTRTYPE); 321 | 322 | n -> u.ATTRTYPE.attrname = attrname; 323 | n -> u.ATTRTYPE.type = type; 324 | n -> u.ATTRTYPE.size = size; 325 | n -> u.ATTRTYPE.spec = spec; 326 | return n; 327 | } 328 | 329 | /* 330 | * relation_node: allocates, initializes, and returns a pointer to a new 331 | * relation node having the indicated values. 332 | */ 333 | NODE *relation_node(char *relname) { 334 | NODE *n = newnode(N_RELATION); 335 | 336 | n->u.RELATION.relname = relname; 337 | return n; 338 | } 339 | 340 | /* 341 | * list_node: allocates, initializes, and returns a pointer to a new 342 | * list node having the indicated values. 343 | */ 344 | NODE *list_node(NODE *n) { 345 | NODE *list = newnode(N_LIST); 346 | 347 | list -> u.LIST.curr = n; 348 | list -> u.LIST.next = NULL; 349 | return list; 350 | } 351 | 352 | /* 353 | * prepends node n onto the front of list. 354 | * 355 | * Returns the resulting list. 356 | */ 357 | NODE *prepend(NODE *n, NODE *list) { 358 | NODE *newlist = newnode(N_LIST); 359 | 360 | newlist -> u.LIST.curr = n; 361 | newlist -> u.LIST.next = list; 362 | return newlist; 363 | } 364 | 365 | NODE *prepend_list(NODE *l, NODE *list) { 366 | NODE* r = l; 367 | assert(l->kind == N_LIST); 368 | while (l->u.LIST.next != NULL) { 369 | l = l->u.LIST.next; 370 | assert(l->kind == N_LIST); 371 | } 372 | l->u.LIST.next = list; 373 | return r; 374 | } 375 | -------------------------------------------------------------------------------- /src/parser.h: -------------------------------------------------------------------------------- 1 | // 2 | // parser.h 3 | // Parser Component Interface 4 | // 5 | 6 | #ifndef PARSER_H 7 | #define PARSER_H 8 | 9 | #ifdef __cplusplus 10 | # include 11 | #endif 12 | #include "redbase.h" 13 | // 14 | // Structure declarations and output functions 15 | // 16 | struct AttrInfo { 17 | char *attrName; /* attribute name */ 18 | enum AttrType attrType; /* type of attribute */ 19 | int attrLength; /* length of attribute */ 20 | int attrSpecs; /* additional specification(s) */ 21 | }; 22 | 23 | struct RelAttr { 24 | char *relName; // Relation name (may be NULL) 25 | char *attrName; // Attribute name 26 | #ifdef __cplusplus 27 | // Print function 28 | friend std::ostream &operator<<(std::ostream &s, const RelAttr &ra); 29 | #endif 30 | }; 31 | 32 | struct Value { 33 | enum ValueType type; /* type of value */ 34 | void *data; /* value */ 35 | #ifdef __cplusplus 36 | /* print function */ 37 | friend std::ostream &operator<<(std::ostream &s, const Value &v); 38 | #endif 39 | }; 40 | 41 | struct Condition { 42 | struct RelAttr lhsAttr; /* left-hand side attribute */ 43 | enum CompOp op; /* comparison operator */ 44 | int bRhsIsAttr; /* TRUE if the rhs is an attribute, */ 45 | /* in which case rhsAttr below is valid;*/ 46 | /* otherwise, rhsValue below is valid. */ 47 | struct RelAttr rhsAttr; /* right-hand side attribute */ 48 | struct Value rhsValue; /* right-hand side value */ 49 | #ifdef __cplusplus 50 | /* print function */ 51 | friend std::ostream &operator<<(std::ostream &s, const Condition &c); 52 | #endif 53 | 54 | }; 55 | 56 | // static struct Condition NULLCONDITION; 57 | 58 | #ifdef __cplusplus 59 | std::ostream &operator<<(std::ostream &s, const CompOp &op); 60 | std::ostream &operator<<(std::ostream &s, const AttrType &at); 61 | #endif 62 | 63 | #ifdef __cplusplus 64 | // 65 | // Parse function 66 | // 67 | class PF_Manager; 68 | class QL_Manager; 69 | class SM_Manager; 70 | 71 | void RBparse(PF_Manager &pfm, SM_Manager &smm, QL_Manager &qlm); 72 | #endif 73 | 74 | // 75 | // Error printing function; calls component-specific functions 76 | // 77 | void PrintError(RC rc); 78 | 79 | // bQueryPlans is allocated by parse.y. When bQueryPlans is 1 then the 80 | // query plan chosen for the SFW query will be displayed. When 81 | // bQueryPlans is 0 then no query plan is shown. 82 | extern int bQueryPlans; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/parser_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * parser_internal.h: internal declarations for the REDBASE parser 3 | * 4 | * Authors: Dallan Quass 5 | * Jan Jannink 6 | * 7 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 8 | */ 9 | 10 | #ifndef PARSER_INTERNAL_H 11 | #define PARSER_INTERNAL_H 12 | 13 | #include "parser.h" 14 | 15 | /* 16 | * use double for real 17 | */ 18 | typedef float real; 19 | 20 | /* 21 | * the prompt 22 | */ 23 | #define PROMPT "\nREDBASE >> " 24 | 25 | /* 26 | * REL_ATTR: describes a qualified attribute (relName.attrName) 27 | */ 28 | typedef struct { 29 | char *relName; /* relation name */ 30 | char *attrName; /* attribute name */ 31 | } REL_ATTR; 32 | 33 | /* 34 | * ATTR_VAL: pair 35 | */ 36 | typedef struct { 37 | char *attrName; /* attribute name */ 38 | enum AttrType valType; /* type of value */ 39 | int valLength; /* length if type = STRING */ 40 | void *value; /* value for attribute */ 41 | } ATTR_VAL; 42 | 43 | /* 44 | * all the available kinds of nodes 45 | */ 46 | typedef enum { 47 | N_SHOWDBS, 48 | N_CREATEDB, 49 | N_DROPDB, 50 | N_USEDB, 51 | N_SHOWTABLES, 52 | N_CREATETABLE, 53 | N_CREATEINDEX, 54 | N_DROPTABLE, 55 | N_DROPINDEX, 56 | N_LOAD, 57 | N_SET, 58 | N_HELP, 59 | N_PRINT, 60 | N_QUERY, 61 | N_INSERT, 62 | N_DELETE, 63 | N_UPDATE, 64 | N_RELATTR, 65 | N_CONDITION, 66 | N_RELATTR_OR_VALUE, 67 | N_ATTRTYPE, 68 | N_VALUE, 69 | N_RELATION, 70 | N_STATISTICS, 71 | N_LIST 72 | } NODEKIND; 73 | 74 | /* 75 | * structure of parse tree nodes 76 | */ 77 | typedef struct node { 78 | NODEKIND kind; 79 | 80 | union { 81 | /* SM component nodes */ 82 | struct { 83 | char *relname; 84 | } DB_OP; 85 | 86 | /* create table node */ 87 | struct { 88 | char *relname; 89 | struct node *attrlist; 90 | } CREATETABLE; 91 | 92 | /* create index node */ 93 | struct { 94 | char *relname; 95 | char *attrname; 96 | } CREATEINDEX; 97 | 98 | /* drop index node */ 99 | struct { 100 | char *relname; 101 | char *attrname; 102 | } DROPINDEX; 103 | 104 | /* drop table node */ 105 | struct { 106 | char *relname; 107 | } DROPTABLE; 108 | 109 | /* load node */ 110 | struct { 111 | char *relname; 112 | char *filename; 113 | } LOAD; 114 | 115 | /* set node */ 116 | struct { 117 | char *paramName; 118 | char *string; 119 | } SET; 120 | 121 | /* help node */ 122 | struct { 123 | char *relname; 124 | } HELP; 125 | 126 | /* print node */ 127 | struct { 128 | char *relname; 129 | } PRINT; 130 | 131 | /* QL component nodes */ 132 | /* query node */ 133 | struct { 134 | struct node *relattrlist; 135 | struct node *rellist; 136 | struct node *conditionlist; 137 | } QUERY; 138 | 139 | /* insert node */ 140 | struct { 141 | char *relname; 142 | struct node *valuelist; 143 | } INSERT; 144 | 145 | /* delete node */ 146 | struct { 147 | char *relname; 148 | struct node *conditionlist; 149 | } DELETE; 150 | 151 | /* update node */ 152 | struct { 153 | char *relname; 154 | struct node *relattr; 155 | struct node *relorvalue; 156 | struct node *conditionlist; 157 | } UPDATE; 158 | 159 | /* command support nodes */ 160 | /* relation attribute node */ 161 | struct { 162 | char *relname; 163 | char *attrname; 164 | } RELATTR; 165 | 166 | /* condition node */ 167 | struct { 168 | struct node *lhsRelattr; 169 | enum CompOp op; 170 | struct node *rhsRelattr; 171 | struct node *rhsValue; 172 | } CONDITION; 173 | 174 | /* relation-attribute or value */ 175 | struct { 176 | struct node *relattr; 177 | struct node *value; 178 | } RELATTR_OR_VALUE; 179 | 180 | /* pair */ 181 | struct { 182 | char *attrname; 183 | char *type; 184 | int size; 185 | enum AttrSpec spec; 186 | } ATTRTYPE; 187 | 188 | /* pair */ 189 | struct { 190 | enum AttrType type; 191 | int ival; 192 | real rval; 193 | char *sval; 194 | } VALUE; 195 | 196 | /* relation node */ 197 | struct { 198 | char *relname; 199 | } RELATION; 200 | 201 | /* list node */ 202 | struct { 203 | struct node *curr; 204 | struct node *next; 205 | } LIST; 206 | } u; 207 | } NODE; 208 | 209 | 210 | /* 211 | * function prototypes 212 | */ 213 | NODE *newnode(NODEKIND kind); 214 | NODE *show_dbs_node(); 215 | NODE *create_db_node(char *relname); 216 | NODE *drop_db_node(char *relname); 217 | NODE *use_db_node(char *relname); 218 | NODE *show_tables_node(); 219 | NODE *create_table_node(char *relname, NODE *attrlist); 220 | NODE *create_index_node(char *relname, char *attrname); 221 | NODE *drop_index_node(char *relname, char *attrname); 222 | NODE *drop_table_node(char *relname); 223 | NODE *load_node(char *relname, char *filename); 224 | NODE *set_node(char *paramName, char *string); 225 | NODE *help_node(char *relname); 226 | NODE *print_node(char *relname); 227 | NODE *query_node(NODE *relattrlist, NODE *rellist, NODE *conditionlist); 228 | NODE *insert_node(char *relname, NODE *valuelist); 229 | NODE *delete_node(char *relname, NODE *conditionlist); 230 | NODE *update_node(char *relname, NODE *relattr, NODE *value, 231 | NODE *conditionlist); 232 | NODE *relattr_node(char *relname, char *attrname); 233 | NODE *condition_node(NODE *lhsRelattr, enum CompOp op, NODE *rhsRelattrOrValue); 234 | NODE *value_node(enum AttrType type, void *value); 235 | NODE *relattr_or_value_node(NODE *relattr, NODE *value); 236 | NODE *attrtype_node(char *attrname, char *type, int size, enum AttrSpec spec); 237 | NODE *relation_node(char *relname); 238 | NODE *list_node(NODE *n); 239 | NODE *prepend(NODE *n, NODE *list); 240 | NODE *prepend_list(NODE *l, NODE* list); 241 | 242 | #ifdef __cplusplus 243 | extern "C" void reset_scanner(void); 244 | extern "C" void reset_charptr(void); 245 | extern "C" void new_query(void); 246 | extern "C" RC interp(NODE *n); 247 | extern "C" int yylex(void); 248 | extern "C" int yyparse(void); 249 | #endif 250 | 251 | #endif 252 | 253 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /src/pf.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf.h 3 | // Description: Paged File component interface 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // Jason McHugh (mchughj@cs.stanford.edu) 7 | // 8 | // 1997: Default page size is now 4k. 9 | // Some additional constants used for the statistics. 10 | // 1998: PageNum is now an int instead of short int. 11 | // Allow chunks from the buffer manager to not be associated with 12 | // a particular file. Allows students to use main memory chunks 13 | // that are associated with (and limited by) the buffer. 14 | // 2005: Added GetLastPage and GetPrevPage for rocking 15 | 16 | #ifndef PF_H 17 | #define PF_H 18 | 19 | #include "redbase.h" 20 | 21 | // 22 | // PageNum: uniquely identifies a page in a file 23 | // 24 | typedef int PageNum; 25 | 26 | // Page Size 27 | // 28 | // Each page stores some header information. The PF_PageHdr is defined 29 | // in pf_internal.h and contains the information that we would store. 30 | // Unfortunately, we cannot use sizeof(PF_PageHdr) here, but it is an 31 | // int and we simply use that. 32 | // 33 | const int PF_PAGE_SIZE = 4096 - sizeof(int); 34 | 35 | // 36 | // PF_PageHandle: PF page interface 37 | // 38 | class PF_PageHandle { 39 | friend class PF_FileHandle; 40 | public: 41 | PF_PageHandle (); // Default constructor 42 | ~PF_PageHandle (); // Destructor 43 | 44 | // Copy constructor 45 | PF_PageHandle (const PF_PageHandle &pageHandle); 46 | // Overloaded = 47 | PF_PageHandle& operator=(const PF_PageHandle &pageHandle); 48 | 49 | RC GetData (char *&pData) const; // Set pData to point to 50 | // the page contents 51 | RC GetPageNum (PageNum &pageNum) const; // Return the page number 52 | private: 53 | int pageNum; // page number 54 | char *pPageData; // pointer to page data 55 | }; 56 | 57 | // 58 | // PF_FileHdr: Header structure for files 59 | // 60 | struct PF_FileHdr { 61 | int firstFree; // first free page in the linked list 62 | int numPages; // # of pages in the file 63 | }; 64 | 65 | // 66 | // PF_FileHandle: PF File interface 67 | // 68 | class PF_BufferMgr; 69 | 70 | class PF_FileHandle { 71 | friend class PF_Manager; 72 | public: 73 | PF_FileHandle (); // Default constructor 74 | ~PF_FileHandle (); // Destructor 75 | 76 | // Copy constructor 77 | PF_FileHandle (const PF_FileHandle &fileHandle); 78 | 79 | // Overload = 80 | PF_FileHandle& operator=(const PF_FileHandle &fileHandle); 81 | 82 | // Get the first page 83 | RC GetFirstPage(PF_PageHandle &pageHandle) const; 84 | // Get the next page after current 85 | RC GetNextPage (PageNum current, PF_PageHandle &pageHandle) const; 86 | // Get a specific page 87 | RC GetThisPage (PageNum pageNum, PF_PageHandle &pageHandle) const; 88 | // Get the last page 89 | RC GetLastPage(PF_PageHandle &pageHandle) const; 90 | // Get the prev page after current 91 | RC GetPrevPage (PageNum current, PF_PageHandle &pageHandle) const; 92 | 93 | RC AllocatePage(PF_PageHandle &pageHandle); // Allocate a new page 94 | RC DisposePage (PageNum pageNum); // Dispose of a page 95 | RC MarkDirty (PageNum pageNum) const; // Mark page as dirty 96 | RC UnpinPage (PageNum pageNum) const; // Unpin the page 97 | 98 | // Flush pages from buffer pool. Will write dirty pages to disk. 99 | RC FlushPages () const; 100 | 101 | // Force a page or pages to disk (but do not remove from the buffer pool) 102 | RC ForcePages (PageNum pageNum=ALL_PAGES) const; 103 | 104 | private: 105 | 106 | // IsValidPageNum will return TRUE if page number is valid and FALSE 107 | // otherwise 108 | int IsValidPageNum (PageNum pageNum) const; 109 | 110 | PF_BufferMgr *pBufferMgr; // pointer to buffer manager 111 | PF_FileHdr hdr; // file header 112 | int bFileOpen; // file open flag 113 | int bHdrChanged; // dirty flag for file hdr 114 | int unixfd; // OS file descriptor 115 | }; 116 | 117 | // 118 | // PF_Manager: provides PF file management 119 | // 120 | class PF_Manager { 121 | public: 122 | PF_Manager (); // Constructor 123 | ~PF_Manager (); // Destructor 124 | RC CreateFile (const char *fileName); // Create a new file 125 | RC DestroyFile (const char *fileName); // Delete a file 126 | 127 | // Open and close file methods 128 | RC OpenFile (const char *fileName, PF_FileHandle &fileHandle); 129 | RC CloseFile (PF_FileHandle &fileHandle); 130 | 131 | // Three methods that manipulate the buffer manager. The calls are 132 | // forwarded to the PF_BufferMgr instance and are called by parse.y 133 | // when the user types in a system command. 134 | RC ClearBuffer (); 135 | RC PrintBuffer (); 136 | RC ResizeBuffer (int iNewSize); 137 | 138 | // Three Methods for manipulating raw memory buffers. These memory 139 | // locations are handled by the buffer manager, but are not 140 | // associated with a particular file. These should be used if you 141 | // want to create structures in memory that are bounded by the size 142 | // of the buffer pool. 143 | // 144 | // Note: If you lose the pointer to the buffer that is passed back 145 | // from AllocateBlock or do not call DisposeBlock with that pointer 146 | // then the internal memory will always remain "pinned" in the buffer 147 | // pool and you will have lost a slot in the buffer pool. 148 | 149 | // Return the size of the block that can be allocated. 150 | RC GetBlockSize (int &length) const; 151 | 152 | // Allocate a memory chunk that lives in buffer manager 153 | RC AllocateBlock (char *&buffer); 154 | // Dispose of a memory chunk managed by the buffer manager. 155 | RC DisposeBlock (char *buffer); 156 | 157 | private: 158 | PF_BufferMgr *pBufferMgr; // page-buffer manager 159 | }; 160 | 161 | // 162 | // Print-error function and PF return code defines 163 | // 164 | void PF_PrintError(RC rc); 165 | 166 | #define PF_PAGEPINNED (START_PF_WARN + 0) // page pinned in buffer 167 | #define PF_PAGENOTINBUF (START_PF_WARN + 1) // page isn't pinned in buffer 168 | #define PF_INVALIDPAGE (START_PF_WARN + 2) // invalid page number 169 | #define PF_FILEOPEN (START_PF_WARN + 3) // file is open 170 | #define PF_CLOSEDFILE (START_PF_WARN + 4) // file is closed 171 | #define PF_PAGEFREE (START_PF_WARN + 5) // page already free 172 | #define PF_PAGEUNPINNED (START_PF_WARN + 6) // page already unpinned 173 | #define PF_EOF (START_PF_WARN + 7) // end of file 174 | #define PF_TOOSMALL (START_PF_WARN + 8) // Resize buffer too small 175 | #define PF_LASTWARN PF_TOOSMALL 176 | 177 | #define PF_NOMEM (START_PF_ERR - 0) // no memory 178 | #define PF_NOBUF (START_PF_ERR - 1) // no buffer space 179 | #define PF_INCOMPLETEREAD (START_PF_ERR - 2) // incomplete read from file 180 | #define PF_INCOMPLETEWRITE (START_PF_ERR - 3) // incomplete write to file 181 | #define PF_HDRREAD (START_PF_ERR - 4) // incomplete read of header 182 | #define PF_HDRWRITE (START_PF_ERR - 5) // incomplete write to header 183 | 184 | // Internal errors 185 | #define PF_PAGEINBUF (START_PF_ERR - 6) // new page already in buffer 186 | #define PF_HASHNOTFOUND (START_PF_ERR - 7) // hash table entry not found 187 | #define PF_HASHPAGEEXIST (START_PF_ERR - 8) // page already in hash table 188 | #define PF_INVALIDNAME (START_PF_ERR - 9) // invalid PC file name 189 | 190 | // Error in UNIX system call or library routine 191 | #define PF_UNIX (START_PF_ERR - 10) // Unix error 192 | #define PF_LASTERROR PF_UNIX 193 | 194 | #endif 195 | -------------------------------------------------------------------------------- /src/pf_buffermgr.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_buffermgr.h 3 | // Description: PF_BufferMgr class interface 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // Jason McHugh (mchughj@cs.stanford.edu) 7 | // 8 | // 1997: When requesting a page from the buffer manager the page requested 9 | // is now promoted to the MRU slot. 10 | // 1998: Allow chunks from the buffer manager to not be associated with 11 | // a particular file. Allows students to use main memory chunks that 12 | // are associated with (and limited by) the buffer. 13 | // 14 | 15 | #ifndef PF_BUFFERMGR_H 16 | #define PF_BUFFERMGR_H 17 | 18 | #include "pf_internal.h" 19 | #include "pf_hashtable.h" 20 | 21 | // 22 | // Defines 23 | // 24 | 25 | // INVALID_SLOT is used within the PF_BufferMgr class which tracks a list 26 | // of PF_BufPageDesc. Inside the PF_BufPageDesc are integer "pointers" to 27 | // next and prev items. INVALID_SLOT is used to indicate no previous or 28 | // next. 29 | #define INVALID_SLOT (-1) 30 | 31 | // 32 | // PF_BufPageDesc - struct containing data about a page in the buffer 33 | // 34 | struct PF_BufPageDesc { 35 | char *pData; // page contents 36 | int next; // next in the linked list of buffer pages 37 | int prev; // prev in the linked list of buffer pages 38 | int bDirty; // TRUE if page is dirty 39 | short int pinCount; // pin count 40 | PageNum pageNum; // page number for this page 41 | int fd; // OS file descriptor of this page 42 | }; 43 | 44 | // 45 | // PF_BufferMgr - manage the page buffer 46 | // 47 | class PF_BufferMgr { 48 | public: 49 | 50 | PF_BufferMgr (int numPages); // Constructor - allocate 51 | // numPages buffer pages 52 | ~PF_BufferMgr (); // Destructor 53 | 54 | // Read pageNum into buffer, point *ppBuffer to location 55 | RC GetPage (int fd, PageNum pageNum, char **ppBuffer, 56 | int bMultiplePins = TRUE); 57 | // Allocate a new page in the buffer, point *ppBuffer to its location 58 | RC AllocatePage (int fd, PageNum pageNum, char **ppBuffer); 59 | 60 | RC MarkDirty (int fd, PageNum pageNum); // Mark page dirty 61 | RC UnpinPage (int fd, PageNum pageNum); // Unpin page from the buffer 62 | RC FlushPages (int fd); // Flush pages for file 63 | 64 | // Force a page to the disk, but do not remove from the buffer pool 65 | RC ForcePages (int fd, PageNum pageNum); 66 | 67 | 68 | // Remove all entries from the Buffer Manager. 69 | RC ClearBuffer (); 70 | // Display all entries in the buffer 71 | RC PrintBuffer (); 72 | 73 | // Attempts to resize the buffer to the new size 74 | RC ResizeBuffer (int iNewSize); 75 | 76 | // Three Methods for manipulating raw memory buffers. These memory 77 | // locations are handled by the buffer manager, but are not 78 | // associated with a particular file. These should be used if you 79 | // want memory that is bounded by the size of the buffer pool. 80 | 81 | // Return the size of the block that can be allocated. 82 | RC GetBlockSize (int &length) const; 83 | 84 | // Allocate a memory chunk that lives in buffer manager 85 | RC AllocateBlock (char *&buffer); 86 | // Dispose of a memory chunk managed by the buffer manager. 87 | RC DisposeBlock (char *buffer); 88 | 89 | private: 90 | RC InsertFree (int slot); // Insert slot at head of free 91 | RC LinkHead (int slot); // Insert slot at head of used 92 | RC Unlink (int slot); // Unlink slot 93 | RC InternalAlloc(int &slot); // Get a slot to use 94 | 95 | // Read a page 96 | RC ReadPage (int fd, PageNum pageNum, char *dest); 97 | 98 | // Write a page 99 | RC WritePage (int fd, PageNum pageNum, char *source); 100 | 101 | // Init the page desc entry 102 | RC InitPageDesc (int fd, PageNum pageNum, int slot); 103 | 104 | PF_BufPageDesc *bufTable; // info on buffer pages 105 | PF_HashTable hashTable; // Hash table object 106 | int numPages; // # of pages in the buffer 107 | int pageSize; // Size of pages in the buffer 108 | int first; // MRU page slot 109 | int last; // LRU page slot 110 | int free; // head of free list 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/pf_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_error.cc 3 | // Description: PF_PrintError function 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include "pf_internal.h" 12 | 13 | using namespace std; 14 | 15 | // 16 | // Error table 17 | // 18 | static char *PF_WarnMsg[] = { 19 | (char*)"page pinned in buffer", 20 | (char*)"page is not in the buffer", 21 | (char*)"invalid page number", 22 | (char*)"file open", 23 | (char*)"invalid file descriptor (file closed)", 24 | (char*)"page already free", 25 | (char*)"page already unpinned", 26 | (char*)"end of file", 27 | (char*)"attempting to resize the buffer too small", 28 | (char*)"invalid filename" 29 | }; 30 | 31 | static char *PF_ErrorMsg[] = { 32 | (char*)"no memory", 33 | (char*)"no buffer space", 34 | (char*)"incomplete read of page from file", 35 | (char*)"incomplete write of page to file", 36 | (char*)"incomplete read of header from file", 37 | (char*)"incomplete write of header from file", 38 | (char*)"new page to be allocated already in buffer", 39 | (char*)"hash table entry not found", 40 | (char*)"page already in hash table", 41 | (char*)"invalid file name" 42 | }; 43 | 44 | // 45 | // PF_PrintError 46 | // 47 | // Desc: Send a message corresponding to a PF return code to cerr 48 | // Assumes PF_UNIX is last valid PF return code 49 | // In: rc - return code for which a message is desired 50 | // 51 | void PF_PrintError(RC rc) 52 | { 53 | // Check the return code is within proper limits 54 | if (rc >= START_PF_WARN && rc <= PF_LASTWARN) 55 | // Print warning 56 | cerr << "PF warning: " << PF_WarnMsg[rc - START_PF_WARN] << "\n"; 57 | // Error codes are negative, so invert everything 58 | else if (-rc >= -START_PF_ERR && -rc < -PF_LASTERROR) 59 | // Print error 60 | cerr << "PF error: " << PF_ErrorMsg[-rc + START_PF_ERR] << "\n"; 61 | else if (rc == PF_UNIX) 62 | #ifdef PC 63 | cerr << "OS error\n"; 64 | #else 65 | cerr << strerror(errno) << "\n"; 66 | #endif 67 | else if (rc == 0) 68 | cerr << "PF_PrintError called with return code of 0\n"; 69 | else 70 | cerr << "PF error: " << rc << " is out of bounds\n"; 71 | } 72 | -------------------------------------------------------------------------------- /src/pf_hashtable.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_hashtable.cc 3 | // Description: PF_HashTable class implementation 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include "pf_internal.h" 9 | #include "pf_hashtable.h" 10 | 11 | // 12 | // PF_HashTable 13 | // 14 | // Desc: Constructor for PF_HashTable object, which allows search, insert, 15 | // and delete of hash table entries. 16 | // In: numBuckets - number of hash table buckets 17 | // 18 | PF_HashTable::PF_HashTable(int _numBuckets) 19 | { 20 | // Initialize numBuckets local variable from parameter 21 | this->numBuckets = _numBuckets; 22 | 23 | // Allocate memory for hash table 24 | hashTable = new PF_HashEntry* [numBuckets]; 25 | 26 | // Initialize all buckets to empty 27 | for (int i = 0; i < numBuckets; i++) 28 | hashTable[i] = NULL; 29 | } 30 | 31 | // 32 | // ~PF_HashTable 33 | // 34 | // Desc: Destructor 35 | // 36 | PF_HashTable::~PF_HashTable() 37 | { 38 | // Clear out all buckets 39 | for (int i = 0; i < numBuckets; i++) { 40 | 41 | // Delete all entries in the bucket 42 | PF_HashEntry *entry = hashTable[i]; 43 | while (entry != NULL) { 44 | PF_HashEntry *next = entry->next; 45 | delete entry; 46 | entry = next; 47 | } 48 | } 49 | 50 | // Finally delete the hash table 51 | delete[] hashTable; 52 | } 53 | 54 | // 55 | // Find 56 | // 57 | // Desc: Find a hash table entry. 58 | // In: fd - file descriptor 59 | // pageNum - page number 60 | // Out: slot - set to slot associated with fd and pageNum 61 | // Ret: PF return code 62 | // 63 | RC PF_HashTable::Find(int fd, PageNum pageNum, int &slot) 64 | { 65 | // Get which bucket it should be in 66 | int bucket = Hash(fd, pageNum); 67 | 68 | if (bucket<0) 69 | return (PF_HASHNOTFOUND); 70 | 71 | // Go through the linked list of this bucket 72 | for (PF_HashEntry *entry = hashTable[bucket]; 73 | entry != NULL; 74 | entry = entry->next) { 75 | if (entry->fd == fd && entry->pageNum == pageNum) { 76 | 77 | // Found it 78 | slot = entry->slot; 79 | return (0); 80 | } 81 | } 82 | 83 | // Didn't find it 84 | return (PF_HASHNOTFOUND); 85 | } 86 | 87 | // 88 | // Insert 89 | // 90 | // Desc: Insert a hash table entry 91 | // In: fd - file descriptor 92 | // pagenum - page number 93 | // slot - slot associated with fd and pageNum 94 | // Ret: PF return code 95 | // 96 | RC PF_HashTable::Insert(int fd, PageNum pageNum, int slot) 97 | { 98 | // Get which bucket it should be in 99 | int bucket = Hash(fd, pageNum); 100 | 101 | // Check entry doesn't already exist in the bucket 102 | PF_HashEntry *entry; 103 | for (entry = hashTable[bucket]; 104 | entry != NULL; 105 | entry = entry->next) { 106 | if (entry->fd == fd && entry->pageNum == pageNum) 107 | return (PF_HASHPAGEEXIST); 108 | } 109 | 110 | // Allocate memory for new hash entry 111 | if ((entry = new PF_HashEntry) == NULL) 112 | return (PF_NOMEM); 113 | 114 | // Insert entry at head of list for this bucket 115 | entry->fd = fd; 116 | entry->pageNum = pageNum; 117 | entry->slot = slot; 118 | entry->next = hashTable[bucket]; 119 | entry->prev = NULL; 120 | if (hashTable[bucket] != NULL) 121 | hashTable[bucket]->prev = entry; 122 | hashTable[bucket] = entry; 123 | 124 | // Return ok 125 | return (0); 126 | } 127 | 128 | // 129 | // Delete 130 | // 131 | // Desc: Delete a hash table entry 132 | // In: fd - file descriptor 133 | // pagenum - page number 134 | // Ret: PF return code 135 | // 136 | RC PF_HashTable::Delete(int fd, PageNum pageNum) 137 | { 138 | // Get which bucket it should be in 139 | int bucket = Hash(fd, pageNum); 140 | 141 | // Find the entry is in this bucket 142 | PF_HashEntry *entry; 143 | for (entry = hashTable[bucket]; 144 | entry != NULL; 145 | entry = entry->next) { 146 | if (entry->fd == fd && entry->pageNum == pageNum) 147 | break; 148 | } 149 | 150 | // Did we find hash entry? 151 | if (entry == NULL) 152 | return (PF_HASHNOTFOUND); 153 | 154 | // Remove this entry 155 | if (entry == hashTable[bucket]) 156 | hashTable[bucket] = entry->next; 157 | if (entry->prev != NULL) 158 | entry->prev->next = entry->next; 159 | if (entry->next != NULL) 160 | entry->next->prev = entry->prev; 161 | delete entry; 162 | 163 | // Return ook 164 | return (0); 165 | } 166 | 167 | 168 | -------------------------------------------------------------------------------- /src/pf_hashtable.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_hashtable.h 3 | // Description: PF_HashTable class interface 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #ifndef PF_HASHTABLE_H 9 | #define PF_HASHTABLE_H 10 | 11 | #include "pf_internal.h" 12 | 13 | // 14 | // HashEntry - Hash table bucket entries 15 | // 16 | struct PF_HashEntry { 17 | PF_HashEntry *next; // next hash table element or NULL 18 | PF_HashEntry *prev; // prev hash table element or NULL 19 | int fd; // file descriptor 20 | PageNum pageNum; // page number 21 | int slot; // slot of this page in the buffer 22 | }; 23 | 24 | // 25 | // PF_HashTable - allow search, insertion, and deletion of hash table entries 26 | // 27 | class PF_HashTable { 28 | public: 29 | PF_HashTable (int numBuckets); // Constructor 30 | ~PF_HashTable(); // Destructor 31 | RC Find (int fd, PageNum pageNum, int &slot); 32 | // Set slot to the hash table 33 | // entry for fd and pageNum 34 | RC Insert (int fd, PageNum pageNum, int slot); 35 | // Insert a hash table entry 36 | RC Delete (int fd, PageNum pageNum); // Delete a hash table entry 37 | 38 | private: 39 | int Hash (int fd, PageNum pageNum) const 40 | { return ((fd + pageNum) % numBuckets); } // Hash function 41 | int numBuckets; // Number of hash table buckets 42 | PF_HashEntry **hashTable; // Hash table 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/pf_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_internal.h 3 | // Description: Declarations internal to the paged file component 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // Jason McHugh (mchughj@cs.stanford.edu) 7 | // 8 | 9 | #ifndef PF_INTERNAL_H 10 | #define PF_INTERNAL_H 11 | 12 | #include 13 | #include 14 | #include "pf.h" 15 | 16 | // 17 | // Constants and defines 18 | // 19 | const int PF_BUFFER_SIZE = 40; // Number of pages in the buffer 20 | const int PF_HASH_TBL_SIZE = 20; // Size of hash table 21 | 22 | #define CREATION_MASK 0600 // r/w privileges to owner only 23 | #define PF_PAGE_LIST_END -1 // end of list of free pages 24 | #define PF_PAGE_USED -2 // page is being used 25 | 26 | // L_SET is used to indicate the "whence" argument of the lseek call 27 | // defined in "/usr/include/unistd.h". A value of 0 indicates to 28 | // move to the absolute location specified. 29 | #ifndef L_SET 30 | #define L_SET 0 31 | #endif 32 | 33 | // 34 | // PF_PageHdr: Header structure for pages 35 | // 36 | struct PF_PageHdr { 37 | int nextFree; // nextFree can be any of these values: 38 | // - the number of the next free page 39 | // - PF_PAGE_LIST_END if this is last free page 40 | // - PF_PAGE_USED if the page is not free 41 | }; 42 | 43 | // Justify the file header to the length of one page 44 | const int PF_FILE_HDR_SIZE = PF_PAGE_SIZE + sizeof(PF_PageHdr); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/pf_manager.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_manager.cc 3 | // Description: PF_Manager class implementation 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "pf_internal.h" 14 | #include "pf_buffermgr.h" 15 | 16 | // 17 | // PF_Manager 18 | // 19 | // Desc: Constructor - intended to be called once at begin of program 20 | // Handles creation, deletion, opening and closing of files. 21 | // It is associated with a PF_BufferMgr that manages the page 22 | // buffer and executes the page replacement policies. 23 | // 24 | PF_Manager::PF_Manager() 25 | { 26 | // Create Buffer Manager 27 | pBufferMgr = new PF_BufferMgr(PF_BUFFER_SIZE); 28 | } 29 | 30 | // 31 | // ~PF_Manager 32 | // 33 | // Desc: Destructor - intended to be called once at end of program 34 | // Destroys the buffer manager. 35 | // All files are expected to be closed when this method is called. 36 | // 37 | PF_Manager::~PF_Manager() 38 | { 39 | // Destroy the buffer manager objects 40 | delete pBufferMgr; 41 | } 42 | 43 | // 44 | // CreateFile 45 | // 46 | // Desc: Create a new PF file named fileName 47 | // In: fileName - name of file to create 48 | // Ret: PF return code 49 | // 50 | RC PF_Manager::CreateFile (const char *fileName) 51 | { 52 | int fd; // unix file descriptor 53 | int numBytes; // return code form write syscall 54 | 55 | // Create file for exclusive use 56 | if ((fd = open(fileName, 57 | #ifdef PC 58 | O_BINARY | 59 | #endif 60 | O_CREAT | O_EXCL | O_WRONLY, 61 | CREATION_MASK)) < 0) 62 | return (PF_UNIX); 63 | 64 | // Initialize the file header: must reserve FileHdrSize bytes in memory 65 | // though the actual size of FileHdr is smaller 66 | char hdrBuf[PF_FILE_HDR_SIZE]; 67 | 68 | // So that Purify doesn't complain 69 | memset(hdrBuf, 0, PF_FILE_HDR_SIZE); 70 | 71 | PF_FileHdr *hdr = (PF_FileHdr*)hdrBuf; 72 | hdr->firstFree = PF_PAGE_LIST_END; 73 | hdr->numPages = 0; 74 | 75 | // Write header to file 76 | if((numBytes = write(fd, hdrBuf, PF_FILE_HDR_SIZE)) 77 | != PF_FILE_HDR_SIZE) { 78 | 79 | // Error while writing: close and remove file 80 | close(fd); 81 | unlink(fileName); 82 | 83 | // Return an error 84 | if(numBytes < 0) 85 | return (PF_UNIX); 86 | else 87 | return (PF_HDRWRITE); 88 | } 89 | 90 | // Close file 91 | if(close(fd) < 0) 92 | return (PF_UNIX); 93 | 94 | // Return ok 95 | return (0); 96 | } 97 | 98 | // 99 | // DestroyFile 100 | // 101 | // Desc: Delete a PF file named fileName (fileName must exist and not be open) 102 | // In: fileName - name of file to delete 103 | // Ret: PF return code 104 | // 105 | RC PF_Manager::DestroyFile (const char *fileName) 106 | { 107 | // Remove the file 108 | if (unlink(fileName) < 0) 109 | return (PF_UNIX); 110 | 111 | // Return ok 112 | return (0); 113 | } 114 | 115 | // 116 | // OpenFile 117 | // 118 | // Desc: Open the paged file whose name is "fileName". It is possible to open 119 | // a file more than once, however, it will be treated as 2 separate files 120 | // (different file descriptors; different buffers). Thus, opening a file 121 | // more than once for writing may corrupt the file, and can, in certain 122 | // circumstances, crash the PF layer. Note that even if only one instance 123 | // of a file is for writing, problems may occur because some writes may 124 | // not be seen by a reader of another instance of the file. 125 | // In: fileName - name of file to open 126 | // Out: fileHandle - refer to the open file 127 | // this function modifies local var's in fileHandle 128 | // to point to the file data in the file table, and to point to the 129 | // buffer manager object 130 | // Ret: PF_FILEOPEN or other PF return code 131 | // 132 | RC PF_Manager::OpenFile (const char *fileName, PF_FileHandle &fileHandle) 133 | { 134 | int rc; // return code 135 | 136 | // Ensure file is not already open 137 | if (fileHandle.bFileOpen) 138 | return (PF_FILEOPEN); 139 | 140 | // Open the file 141 | if ((fileHandle.unixfd = open(fileName, 142 | #ifdef PC 143 | O_BINARY | 144 | #endif 145 | O_RDWR)) < 0) 146 | return (PF_UNIX); 147 | 148 | // Read the file header 149 | { 150 | int numBytes = read(fileHandle.unixfd, (char *)&fileHandle.hdr, 151 | sizeof(PF_FileHdr)); 152 | if (numBytes != sizeof(PF_FileHdr)) { 153 | rc = (numBytes < 0) ? PF_UNIX : PF_HDRREAD; 154 | goto err; 155 | } 156 | } 157 | 158 | // Set file header to be not changed 159 | fileHandle.bHdrChanged = FALSE; 160 | 161 | // Set local variables in file handle object to refer to open file 162 | fileHandle.pBufferMgr = pBufferMgr; 163 | fileHandle.bFileOpen = TRUE; 164 | 165 | // Return ok 166 | return 0; 167 | 168 | err: 169 | // Close file 170 | close(fileHandle.unixfd); 171 | fileHandle.bFileOpen = FALSE; 172 | 173 | // Return error 174 | return (rc); 175 | } 176 | 177 | // 178 | // CloseFile 179 | // 180 | // Desc: Close file associated with fileHandle 181 | // The file should have been opened with OpenFile(). 182 | // Also, flush all pages for the file from the page buffer 183 | // It is an error to close a file with pages still fixed in the buffer. 184 | // In: fileHandle - handle of file to close 185 | // Out: fileHandle - no longer refers to an open file 186 | // this function modifies local var's in fileHandle 187 | // Ret: PF return code 188 | // 189 | RC PF_Manager::CloseFile(PF_FileHandle &fileHandle) 190 | { 191 | RC rc; 192 | 193 | // Ensure fileHandle refers to open file 194 | if (!fileHandle.bFileOpen) 195 | return (PF_CLOSEDFILE); 196 | 197 | // Flush all buffers for this file and write out the header 198 | if ((rc = fileHandle.FlushPages())) 199 | return (rc); 200 | 201 | // Close the file 202 | if (close(fileHandle.unixfd) < 0) 203 | return (PF_UNIX); 204 | fileHandle.bFileOpen = FALSE; 205 | 206 | // Reset the buffer manager pointer in the file handle 207 | fileHandle.pBufferMgr = NULL; 208 | 209 | // Return ok 210 | return 0; 211 | } 212 | 213 | // 214 | // ClearBuffer 215 | // 216 | // Desc: Remove all entries from the buffer manager. 217 | // This routine will be called via the system command and is only 218 | // really useful if the user wants to run some performance 219 | // comparison starting with an clean buffer. 220 | // In: Nothing 221 | // Out: Nothing 222 | // Ret: Returns the result of PF_BufferMgr::ClearBuffer 223 | // It is a code: 0 for success, something else for a PF error. 224 | // 225 | RC PF_Manager::ClearBuffer() 226 | { 227 | return pBufferMgr->ClearBuffer(); 228 | } 229 | 230 | // 231 | // PrintBuffer 232 | // 233 | // Desc: Display all of the pages within the buffer. 234 | // This routine will be called via the system command. 235 | // In: Nothing 236 | // Out: Nothing 237 | // Ret: Returns the result of PF_BufferMgr::PrintBuffer 238 | // It is a code: 0 for success, something else for a PF error. 239 | // 240 | RC PF_Manager::PrintBuffer() 241 | { 242 | return pBufferMgr->PrintBuffer(); 243 | } 244 | 245 | // 246 | // ResizeBuffer 247 | // 248 | // Desc: Resizes the buffer manager to the size passed in. 249 | // This routine will be called via the system command. 250 | // In: The new buffer size 251 | // Out: Nothing 252 | // Ret: Returns the result of PF_BufferMgr::ResizeBuffer 253 | // It is a code: 0 for success, PF_TOOSMALL when iNewSize 254 | // would be too small. 255 | // 256 | RC PF_Manager::ResizeBuffer(int iNewSize) 257 | { 258 | return pBufferMgr->ResizeBuffer(iNewSize); 259 | } 260 | 261 | //------------------------------------------------------------------------------ 262 | // Three Methods for manipulating raw memory buffers. These memory 263 | // locations are handled by the buffer manager, but are not 264 | // associated with a particular file. These should be used if you 265 | // want memory that is bounded by the size of the buffer pool. 266 | // 267 | // The PF_Manager just passes the calls down to the Buffer manager. 268 | //------------------------------------------------------------------------------ 269 | 270 | RC PF_Manager::GetBlockSize(int &length) const 271 | { 272 | return pBufferMgr->GetBlockSize(length); 273 | } 274 | 275 | RC PF_Manager::AllocateBlock(char *&buffer) 276 | { 277 | return pBufferMgr->AllocateBlock(buffer); 278 | } 279 | 280 | RC PF_Manager::DisposeBlock(char *buffer) 281 | { 282 | return pBufferMgr->DisposeBlock(buffer); 283 | } 284 | -------------------------------------------------------------------------------- /src/pf_pagehandle.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_pagehandle.cc 3 | // Description: PF_PageHandle class implementation 4 | // Authors: Hugo Rivero (rivero@cs.stanford.edu) 5 | // Dallan Quass (quass@cs.stanford.edu) 6 | // 7 | 8 | #include "pf_internal.h" 9 | 10 | // 11 | // Defines 12 | // 13 | #define INVALID_PAGE (-1) 14 | 15 | // 16 | // PF_PageHandle 17 | // 18 | // Desc: Default constructor for a page handle object 19 | // A page handle object provides access to the contents of a page 20 | // and the page's page number. The page handle object is constructed 21 | // here but it must be passed to one of the PF_FileHandle methods to 22 | // have it refer to a pinned page before it can be used to access the 23 | // contents of a page. Remember to call PF_FileHandle::UnpinPage() 24 | // to unpin the page when you are finished accessing it. 25 | // 26 | PF_PageHandle::PF_PageHandle() 27 | { 28 | pageNum = INVALID_PAGE; 29 | pPageData = NULL; 30 | } 31 | 32 | // 33 | // ~PF_PageHandle 34 | // 35 | // Desc: Destroy the page handle object. 36 | // If the page handle object refers to a pinned page, the page will 37 | // NOT be unpinned. 38 | // 39 | PF_PageHandle::~PF_PageHandle() 40 | { 41 | // Don't need to do anything 42 | } 43 | 44 | // 45 | // PF_PageHandle 46 | // 47 | // Desc: Copy constructor 48 | // If the incoming page handle object refers to a pinned page, 49 | // the page will NOT be pinned again. 50 | // In: pageHandle - page handle object from which to construct this object 51 | // 52 | PF_PageHandle::PF_PageHandle(const PF_PageHandle &pageHandle) 53 | { 54 | // Just copy the local variables since there is no local memory 55 | // allocation involved 56 | this->pageNum = pageHandle.pageNum; 57 | this->pPageData = pageHandle.pPageData; 58 | } 59 | 60 | // 61 | // operator= 62 | // 63 | // Desc: overload = operator 64 | // If the page handle object on the rhs refers to a pinned page, 65 | // the page will NOT be pinned again. 66 | // In: pageHandle - page handle object to set this object equal to 67 | // Ret: reference to *this 68 | // 69 | PF_PageHandle& PF_PageHandle::operator= (const PF_PageHandle &pageHandle) 70 | { 71 | // Check for self-assignment 72 | if (this != &pageHandle) { 73 | 74 | // Just copy the pointers since there is no local memory 75 | // allocation involved 76 | this->pageNum = pageHandle.pageNum; 77 | this->pPageData = pageHandle.pPageData; 78 | } 79 | 80 | // Return a reference to this 81 | return (*this); 82 | } 83 | 84 | // 85 | // GetData 86 | // 87 | // Desc: Access the contents of a page. The page handle object must refer 88 | // to a pinned page. 89 | // Out: pData - Set pData to point to the page contents 90 | // Ret: PF return code 91 | // 92 | RC PF_PageHandle::GetData(char *&pData) const 93 | { 94 | // Page must refer to a pinned page 95 | if (pPageData == NULL) 96 | return (PF_PAGEUNPINNED); 97 | 98 | // Set pData to point to page contents (after PF header) 99 | pData = pPageData; 100 | 101 | // Return ok 102 | return (0); 103 | } 104 | 105 | // 106 | // GetPageNum 107 | // 108 | // Desc: Access the page number. The page handle object must refer to 109 | // a pinned page. 110 | // Out: pageNum - contains the page number 111 | // Ret: PF return code 112 | // 113 | RC PF_PageHandle::GetPageNum(PageNum &_pageNum) const 114 | { 115 | 116 | // Page must refer to a pinned page 117 | if (pPageData == NULL) 118 | return (PF_PAGEUNPINNED); 119 | 120 | // Set page number 121 | _pageNum = this->pageNum; 122 | 123 | // Return ok 124 | return (0); 125 | } 126 | -------------------------------------------------------------------------------- /src/pf_statistics.cc: -------------------------------------------------------------------------------- 1 | // 2 | // pf_statistics.cc 3 | // 4 | 5 | // This file contains the procedure to display all the statistics for the 6 | // PF layer. 7 | 8 | // Code written by Andre Bergholz, who was the TA for 2000 9 | 10 | // 11 | // This file only makes sense when the PF Statistics layer is defined 12 | // 13 | #ifdef PF_STATS 14 | 15 | #include 16 | #include "pf.h" 17 | #include "statistics.h" 18 | 19 | using namespace std; 20 | 21 | // This is defined within pf_buffermgr.cc 22 | extern StatisticsMgr *pStatisticsMgr; 23 | 24 | void PF_Statistics() 25 | { 26 | // First get all the statistics, must remember to delete memory returned 27 | int *piGP = pStatisticsMgr->Get(PF_GETPAGE); 28 | int *piPF = pStatisticsMgr->Get(PF_PAGEFOUND); 29 | int *piPNF = pStatisticsMgr->Get(PF_PAGENOTFOUND); 30 | int *piRP = pStatisticsMgr->Get(PF_READPAGE); 31 | int *piWP = pStatisticsMgr->Get(PF_WRITEPAGE); 32 | int *piFP = pStatisticsMgr->Get(PF_FLUSHPAGES); 33 | 34 | cout << "PF Layer Statistics\n"; 35 | cout << "-------------------\n"; 36 | 37 | cout << "Total number of calls to GetPage Routine: "; 38 | if (piGP) cout << *piGP; else cout << "None"; 39 | cout << "\n Number found: "; 40 | if (piPF) cout << *piPF; else cout << "None"; 41 | cout << "\n Number not found: "; 42 | if (piPNF) cout << *piPNF; else cout << "None"; 43 | cout << "\n-------------------\n"; 44 | 45 | cout << "Number of read requests: "; 46 | if (piRP) cout << *piRP; else cout << "None"; 47 | cout << "\nNumber of write requests: "; 48 | if (piWP) cout << *piWP; else cout << "None"; 49 | cout << "\n-------------------\n"; 50 | cout << "Number of flushes: "; 51 | if (piFP) cout << *piFP; else cout << "None"; 52 | cout << "\n-------------------\n"; 53 | 54 | // Must delete the memory returned from StatisticsMgr::Get 55 | delete piGP; 56 | delete piPF; 57 | delete piPNF; 58 | delete piRP; 59 | delete piWP; 60 | delete piFP; 61 | } 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/pf_test2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_test2.cc 3 | // Description: Test PF component 4 | // Authors: Jason McHugh (mchughj@cs.stanford.edu) 5 | // 6 | // 1997: This file was created to utilize the statistics manager to ensure 7 | // that the buffer manager was performing correctly. Note that you must 8 | // compile the pf layer with the -DPF_STATS flag. If you don't then this 9 | // test won't really test anything over and above the pf_test1.cc. 10 | // 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "pf.h" 17 | #include "pf_internal.h" 18 | #include "pf_hashtable.h" 19 | 20 | using namespace std; 21 | 22 | // The PF_STATS indicates that we will be tracking statistics for the PF 23 | // Layer. The Manager is defined within pf_buffermgr.cc. Here we must 24 | // place the initializer and then the final call to printout the statistics 25 | // once main has finished 26 | #ifdef PF_STATS 27 | #include "statistics.h" 28 | 29 | // This is defined within pf_buffermgr.cc 30 | extern StatisticsMgr *pStatisticsMgr; 31 | 32 | // This method is defined within pf_statistics.cc. It is called at the end 33 | // to display the final statistics, or by the debugger to monitor progress. 34 | extern void PF_Statistics(); 35 | #endif 36 | 37 | // 38 | // Defines 39 | // 40 | #define FILE1 "file1" 41 | 42 | RC TestPF() 43 | { 44 | PF_Manager pfm; 45 | PF_FileHandle fh; 46 | PF_PageHandle ph; 47 | RC rc; 48 | char *pData; 49 | PageNum pageNum; 50 | int i; 51 | 52 | cout << "Creating file: " << FILE1 << "\n"; 53 | if ((rc = pfm.CreateFile(FILE1))) 54 | return (rc); 55 | 56 | cout << "Opening file: " << FILE1 << "\n"; 57 | 58 | if ((rc = pfm.OpenFile(FILE1, fh))) 59 | return(rc); 60 | 61 | cout << "Allocating " << PF_BUFFER_SIZE << " pages.\n"; 62 | 63 | for (i = 0; i < PF_BUFFER_SIZE; i++) { 64 | if ((rc = fh.AllocatePage(ph)) || 65 | (rc = ph.GetData(pData)) || 66 | (rc = ph.GetPageNum(pageNum))) 67 | return(rc); 68 | 69 | if (i != pageNum) { 70 | cout << "Page number incorrect: " << (int)pageNum << " " << i << "\n"; 71 | exit(1); 72 | } 73 | // Put only the page number into the page 74 | memcpy(pData, (char *)&pageNum, sizeof(PageNum)); 75 | } 76 | 77 | // Now ask for the same pages again 78 | cout << "Asking for the same pages again.\n"; 79 | 80 | for (i = 0; i < PF_BUFFER_SIZE; i++) { 81 | if ((rc = fh.GetThisPage(i, ph)) || 82 | (rc = ph.GetData(pData)) || 83 | (rc = ph.GetPageNum(pageNum))) 84 | return(rc); 85 | if (i != pageNum) { 86 | cout << "Page number incorrect: " << (int)pageNum 87 | << " " << i << "\n"; 88 | exit(1); 89 | } 90 | } 91 | 92 | // Now, if we have compiled with PF_STATS then we need to ensure that 93 | // PF_GETPAGE = PF_BUFFER_SIZE and PF_PAGEFOUND = PF_BUFFER_SIZE. 94 | // Also that PF_PAGENOTFOUND = PF_BUFFER_SIZE. 95 | #ifdef PF_STATS 96 | cout << "Verifying the statistics for buffer manager: "; 97 | int *piGP = pStatisticsMgr->Get(PF_GETPAGE); 98 | int *piPF = pStatisticsMgr->Get(PF_PAGEFOUND); 99 | int *piPNF = pStatisticsMgr->Get(PF_PAGENOTFOUND); 100 | 101 | if (piGP && (*piGP != PF_BUFFER_SIZE)) { 102 | cout << "Number of GetPages is incorrect! (" << *piGP << ")\n"; 103 | // No built in error code for this 104 | exit(1); 105 | } 106 | if (piPF && (*piPF != PF_BUFFER_SIZE)) { 107 | cout << "Number of pages found in the buffer is incorrect! (" << 108 | *piPF << ")\n"; 109 | // No built in error code for this 110 | exit(1); 111 | } 112 | if (piPNF!=NULL) { 113 | cout << "Number of pages not found in the buffer is incorrect! (" << 114 | *piPNF << ")\n"; 115 | // No built in error code for this 116 | exit(1); 117 | } 118 | cout << " Correct!\n"; 119 | 120 | delete piGP; 121 | delete piPF; 122 | delete piPNF; 123 | 124 | #endif // PF_STATS 125 | 126 | cout << "Unpinning pages.\n"; 127 | for (i = 0; i < PF_BUFFER_SIZE; i++) 128 | // Must unpine the pages twice 129 | if ((rc = fh.UnpinPage(i)) || 130 | (rc = fh.UnpinPage(i))) 131 | return(rc); 132 | 133 | // Confirm that the buffer manager has written the correct number of 134 | // pages. 135 | #ifdef PF_STATS 136 | cout << "Verifying the write statistics for buffer manager: "; 137 | int *piWP = pStatisticsMgr->Get(PF_WRITEPAGE); 138 | int *piRP = pStatisticsMgr->Get(PF_READPAGE); 139 | 140 | if (piWP && (*piWP != PF_BUFFER_SIZE)) { 141 | cout << "Number of write pages is incorrect! (" << *piGP << ")\n"; 142 | // No built in error code for this 143 | exit(1); 144 | } 145 | if (piRP!=NULL) { 146 | cout << "Number of pages read in is incorrect! (" << 147 | *piPNF << ")\n"; 148 | // No built in error code for this 149 | exit(1); 150 | } 151 | cout << " Correct!\n"; 152 | 153 | delete piWP; 154 | delete piRP; 155 | 156 | #endif // PF_STATS 157 | 158 | // Goal here is to push out of the buffer manager the old pages by 159 | // asking for new ones. At the end the LRU algorithm should ensure that 160 | // none of the original pages lie in memory 161 | cout << "Allocating an additional " << PF_BUFFER_SIZE << " pages to clear"; 162 | cout << "out the buffer pool.\n"; 163 | for (i = 0; i < PF_BUFFER_SIZE; i++) { 164 | if ((rc = fh.AllocatePage(ph)) || 165 | (rc = ph.GetData(pData)) || 166 | (rc = ph.GetPageNum(pageNum))) 167 | return(rc); 168 | if (i+PF_BUFFER_SIZE != pageNum) { 169 | cout << "Page number incorrect: " << (int)pageNum 170 | << " " << i << "\n"; 171 | exit(1); 172 | } 173 | if ((rc = fh.UnpinPage(i+PF_BUFFER_SIZE))) 174 | return(rc); 175 | } 176 | 177 | // Now refetch the original pages 178 | cout << "Now asking for the original pages again.\n"; 179 | for (i = 0; i < PF_BUFFER_SIZE; i++) { 180 | if ((rc = fh.GetThisPage(i, ph)) || 181 | (rc = ph.GetData(pData)) || 182 | (rc = ph.GetPageNum(pageNum))) 183 | return(rc); 184 | if (i!= pageNum) { 185 | cout << "Page number incorrect: " << (int)pageNum 186 | << " " << i << "\n"; 187 | exit(1); 188 | } 189 | if ((rc = fh.UnpinPage(i))) 190 | return(rc); 191 | } 192 | 193 | // The previous refetch should have resulted in the buffer manager 194 | // going to disk for each of the pages, since the buffer should 195 | // not have had any of the pages. 196 | #ifdef PF_STATS 197 | cout << "Verifying that pages were not found in buffer pool: "; 198 | piPNF = pStatisticsMgr->Get(PF_PAGENOTFOUND); 199 | 200 | if (piPNF && (*piPNF != PF_BUFFER_SIZE)) { 201 | cout << "Number of pages not found in the buffer is incorrect! (" << 202 | *piPF << ")\n"; 203 | // No built in error code for this 204 | exit(1); 205 | } 206 | cout << " Correct!\n"; 207 | 208 | delete piPNF; 209 | #endif // PF_STATS 210 | 211 | // Now we will Flush the buffer manager to disk and count the number of 212 | // flushes and the total number of writes. 213 | cout << "Flushing the File handle to disk.\n"; 214 | if ((rc = fh.FlushPages())) 215 | return (rc); 216 | 217 | #ifdef PF_ 218 | cout << "Testing flush to disk: "; 219 | int *piFP = pStatisticsMgr->Get(PF_FLUSHPAGES); 220 | piWP = pStatisticsMgr->Get(PF_WRITEPAGES); 221 | 222 | if (piFP && (*piFP != 1)) { 223 | cout << "Number of times Flush pages routine has been called " << 224 | "is incorrect! (" << *piFP << ")\n"; 225 | // No built in error code for this 226 | exit(1); 227 | } 228 | if (piWP && (*piWP != 2*PF_BUFFER_SIZE)) { 229 | cout << "Number of written pages is incorrect! (" << *piWP << ")\n"; 230 | // No built in error code for this 231 | exit(1); 232 | } 233 | cout << " Correct!\n"; 234 | 235 | delete piFP; 236 | delete piWP; 237 | #endif 238 | 239 | 240 | cout << "Flushing the File handle to disk. (Again)\n"; 241 | if ((rc = fh.FlushPages())) 242 | return (rc); 243 | 244 | // Here the idea is to ensure that the number of pages written has not 245 | // increased! Since everything was already flushed. 246 | #ifdef PF_STATS 247 | cout << "Testing number of pages written to disk: "; 248 | piWP = pStatisticsMgr->Get(PF_WRITEPAGE); 249 | 250 | // This number should not have increased since last time! 251 | if (piWP && (*piWP != 2*PF_BUFFER_SIZE)) { 252 | cout << "Number of written pages is incorrect! (" << *piWP << ")\n"; 253 | // No built in error code for this 254 | exit(1); 255 | } 256 | cout << " Correct!\n"; 257 | 258 | delete piWP; 259 | #endif 260 | 261 | // Close the file 262 | if ((rc = pfm.CloseFile(fh))) 263 | return(rc); 264 | 265 | // If we are dealing with statistics then we might as well output the 266 | // final numbers 267 | #ifdef PF_STATS 268 | PF_Statistics(); 269 | #endif 270 | 271 | // Return ok 272 | return (0); 273 | } 274 | 275 | int main() 276 | { 277 | RC rc; 278 | 279 | // Write out initial starting message 280 | cerr.flush(); 281 | cout.flush(); 282 | cout << "********************\n"; 283 | cout << "Starting PF layer test.\n"; 284 | cout.flush(); 285 | 286 | // If we are tracking the PF Layer statistics 287 | #ifndef PF_STATS 288 | cout << " ** The PF layer was not compiled with the -DPF_STATS flag **\n"; 289 | cout << " ** This test file is not very effective without it! **\n"; 290 | #endif 291 | 292 | cout << "----------------------\n"; 293 | 294 | // Delete files from last time 295 | unlink(FILE1); 296 | 297 | if ((rc = TestPF())) { 298 | PF_PrintError(rc); 299 | return (1); 300 | } 301 | 302 | // Write ending message and exit 303 | cout << "Ending PF layer test.\n"; 304 | cout << "********************\n\n"; 305 | 306 | return (0); 307 | } 308 | 309 | -------------------------------------------------------------------------------- /src/pf_test3.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // File: pf_test3.cc 3 | // Description: Tests Main Memory Allocation component of PF 4 | // Authors: Jason McHugh (mchughj@cs.stanford.edu) 5 | // 6 | // 1998: This tester will test the handling of main memory allocation 7 | // that the PF_Manager class now allows. Students may now use the 8 | // PF manager as both the interface to the disk and also as a heap for 9 | // internal structures. 10 | // 11 | // Students can use the methods: GetBlockSize, AllocateBlock and 12 | // DisposeBlock to build in memory structures that are limited by the 13 | // size of the buffer pool. For example if a student wanted to do 14 | // sort-merge-join (for QL or EX) then they can use the PF Manager to 15 | // give them a heap of memory where they can do the sorting. 16 | // 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "pf.h" 23 | #include "pf_internal.h" 24 | #include "pf_hashtable.h" 25 | 26 | using namespace std; 27 | 28 | // 29 | // Defines 30 | // 31 | #define FILE1 "file1" 32 | #define FILE2 "file2" 33 | 34 | // Allocate a group of main memory pages from the buffer 35 | RC AllocateChunk(PF_Manager &pfm, int iBlocks, char *ptr[]); 36 | 37 | // Verify chunks that were allocated in the buffer 38 | RC VerifyChunks(int iBlocks, char *ptr[]); 39 | 40 | // Dispose of the chunks from the buffer pool 41 | RC DisposeChunk(PF_Manager &pfm, int iBlocks, char *ptr[]); 42 | 43 | RC TestChunk(); 44 | 45 | // 46 | // AllocateChunk 47 | // 48 | RC AllocateChunk(PF_Manager &pfm, int iBlocks, char *ptr[]) 49 | { 50 | RC rc = OK_RC; 51 | int i; 52 | 53 | cout << "Asking for " << iBlocks << " chunks from the buffer manager: "; 54 | 55 | // for (i = 0; i < PF_BUFFER_SIZE; i++) { 56 | for (i = 0; i < iBlocks; i++) { 57 | if ((rc = pfm.AllocateBlock(ptr[i]))) 58 | break; 59 | // Put some simple data inside 60 | memcpy (ptr[i], (void *) &i, sizeof(int)); 61 | } 62 | 63 | return rc; 64 | } 65 | 66 | // 67 | // VerifyChunks 68 | // 69 | RC VerifyChunks(int iBlocks, char *ptr[]) 70 | { 71 | int i; 72 | 73 | cout << "Verifying the contents of " << iBlocks << " chunks from the buffer manager: "; 74 | 75 | for (i = 0; i < iBlocks; i++) { 76 | int k; 77 | memcpy ((void *)&k, ptr[i], sizeof(int)); 78 | 79 | if (k!=i) 80 | return 1; 81 | } 82 | return 0; 83 | } 84 | 85 | // 86 | // DisposeChunk 87 | // 88 | RC DisposeChunk(PF_Manager &pfm, int iBlocks, char *ptr[]) 89 | { 90 | RC rc = OK_RC; 91 | int i; 92 | 93 | cout << "Disposing of " << iBlocks << " chunks from the buffer manager: "; 94 | 95 | // for (i = 0; i < PF_BUFFER_SIZE; i++) { 96 | for (i = 0; i < iBlocks; i++) { 97 | if ((rc = pfm.DisposeBlock(ptr[i]))) 98 | break; 99 | } 100 | return rc; 101 | } 102 | 103 | 104 | RC TestChunk() 105 | { 106 | PF_Manager pfm; 107 | RC rc; 108 | char *ptr[10]; 109 | 110 | if ((rc = AllocateChunk(pfm, 10, ptr))) { 111 | cout << "FAILED!\a\a\n"; 112 | return rc; 113 | } 114 | cout << "Pass\n"; 115 | 116 | if ((rc = VerifyChunks(10, ptr))) { 117 | cout << "FAILED!\a\a\n"; 118 | return rc; 119 | } 120 | cout << "Pass\n"; 121 | 122 | if ((rc = DisposeChunk(pfm, 5, ptr))) { 123 | cout << "FAILED!\a\a\n"; 124 | return rc; 125 | } 126 | cout << "Pass\n"; 127 | 128 | // Verify the first 5 chunks, notice that this passes okay! This is 129 | // a bit odd since we have Disposed of the first 5 chunks. 130 | // However, things have not changed since then and the memory is 131 | // sitting there for awhile before someone else comes along and asks 132 | // to change it. 133 | if ((rc = VerifyChunks(10, ptr))) { 134 | cout << "FAILED!\a\a\n"; 135 | return rc; 136 | } 137 | cout << "Pass\n"; 138 | 139 | // Now five chunks are left in the buffer 140 | // Ask for 35 chunks 141 | char *ptr2[35]; 142 | if ((rc = AllocateChunk(pfm, 35, ptr2))) { 143 | cout << "FAILED!\a\a\n"; 144 | return rc; 145 | } 146 | cout << "Pass\n"; 147 | 148 | char *ptr3[1]; 149 | ptr3[0] = NULL; 150 | // Now ask for one more chunk -- shouldn't be able to do it with 151 | // buffer pool size of 40. 152 | if ((rc = AllocateChunk(pfm, 1, ptr3))==0) { 153 | cout << "FAILED!\a\a\n"; 154 | return rc; 155 | } 156 | cout << "Pass\n"; 157 | 158 | // Now ask to remove a chunk which doesn't exist 159 | if ((rc = DisposeChunk(pfm, 1, ptr3))==0) { 160 | cout << "FAILED!\a\a\n"; 161 | return rc; 162 | } 163 | cout << "Pass\n"; 164 | 165 | // Now remove the block of 35 chunks from before 166 | if ((rc = DisposeChunk(pfm, 35, ptr2))) { 167 | cout << "FAILED!\a\a\n"; 168 | return rc; 169 | } 170 | cout << "Pass\n"; 171 | 172 | // And ask for 25 more chunks to ensure that I haven't pinned in some 173 | // way the previous ones. 174 | if ((rc = AllocateChunk(pfm, 25, (ptr2 + 10)))) { 175 | cout << "FAILED!\a\a\n"; 176 | return rc; 177 | } 178 | cout << "Pass\n"; 179 | 180 | 181 | // And make sure that all is well... 182 | if ((rc = VerifyChunks(25, (ptr2+10)))) { 183 | cout << "FAILED!\a\a\n"; 184 | return rc; 185 | } 186 | cout << "Pass\n"; 187 | 188 | // Finally, leave the chunks that are there lying around. They will 189 | // be cleaned up by the PF Manager instance and no purify warnings 190 | // should result. 191 | return 0; 192 | } 193 | 194 | int main() 195 | { 196 | RC rc; 197 | 198 | // Write out initial starting message 199 | cerr.flush(); 200 | cout.flush(); 201 | cout << "Starting PF Chunk layer test.\n"; 202 | cout.flush(); 203 | 204 | // If we are tracking the PF Layer statistics 205 | #ifdef PF_STATS 206 | cout << "Note: Statistics are turned on.\n"; 207 | #endif 208 | 209 | // Do tests 210 | if ((rc = TestChunk())) { 211 | PF_PrintError(rc); 212 | return (1); 213 | } 214 | 215 | // Write ending message and exit 216 | cout << "Ending PF Chunk layer test.\n\n"; 217 | 218 | return (0); 219 | } 220 | 221 | -------------------------------------------------------------------------------- /src/printer.cc: -------------------------------------------------------------------------------- 1 | // 2 | // printer.cc 3 | // 4 | 5 | // This file contains the interface for the Printer class and some 6 | // functions that will be used by both the SM and QL components. 7 | 8 | #include "printer.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | // 19 | // void Spaces(int maxLength, int printedSoFar) 20 | // 21 | // This method will output some spaces so that print entry will align everythin 22 | // nice and neat. 23 | // 24 | void Spaces(int maxLength, size_t printedSoFar) { 25 | for (int i = printedSoFar; i < maxLength; i++) 26 | cout << " "; 27 | } 28 | 29 | // 30 | // ------------------------------------------------------------------------------ 31 | // 32 | 33 | // 34 | // Printer 35 | // 36 | // This class handles the printing of tuples. 37 | // 38 | // DataAttrInfo - describes all of the attributes. Defined 39 | // within sm.h 40 | // attrCount - the number of attributes 41 | // 42 | Printer::Printer(const std::vector &attributes_) { 43 | attrCount = attributes_.size(); 44 | attributes = new DataAttrInfo[attrCount]; 45 | 46 | for (int i = 0; i < attrCount; i++) 47 | attributes[i] = attributes_[i]; 48 | 49 | // Number of tuples printed 50 | iCount = 0; 51 | 52 | // Figure out what the header information will look like. Normally, 53 | // we can just use the attribute name, but if that appears more than 54 | // once, then we should use "relation.attribute". 55 | 56 | // this line broke when using CC 57 | // changing to use malloc and free instead of new and delete 58 | // psHeader = (char **) new (char *)[attrCount]; 59 | psHeader = (char**)malloc(attrCount * sizeof(char*)); 60 | 61 | // Also figure out the number of spaces between each attribute 62 | spaces = new int[attrCount]; 63 | 64 | for (int i = 0; i < attrCount; i++ ) { 65 | // Try to find the attribute in another column 66 | int bFound = 0; 67 | psHeader[i] = new char[MAXPRINTSTRING]; 68 | memset(psHeader[i], 0, MAXPRINTSTRING); 69 | 70 | for (int j = 0; j < attrCount; j++) 71 | if (j != i && 72 | strcmp(attributes[i].attrName, 73 | attributes[j].attrName) == 0) { 74 | bFound = 1; 75 | break; 76 | } 77 | 78 | if (bFound) 79 | sprintf(psHeader[i], "%s.%s", 80 | attributes[i].relName, attributes[i].attrName); 81 | else 82 | strcpy(psHeader[i], attributes[i].attrName); 83 | 84 | if (attributes[i].attrType == STRING) 85 | spaces[i] = min(attributes[i].attrDisplayLength, MAXPRINTSTRING); 86 | else 87 | spaces[i] = max(12, (int)strlen(psHeader[i])); 88 | 89 | // We must subtract out those characters that will be for the 90 | // header. 91 | spaces[i] -= strlen(psHeader[i]); 92 | 93 | // If there are negative (or zero) spaces, then insert a single 94 | // space. 95 | if (spaces[i] < 1) { 96 | // The psHeader will give us the space we need 97 | spaces[i] = 0; 98 | strcat(psHeader[i], " "); 99 | } 100 | } 101 | } 102 | 103 | 104 | // 105 | // Destructor 106 | // 107 | Printer::~Printer() { 108 | for (int i = 0; i < attrCount; i++) 109 | delete [] psHeader[i]; 110 | 111 | delete [] spaces; 112 | //delete [] psHeader; 113 | free (psHeader); 114 | delete [] attributes; 115 | } 116 | 117 | // 118 | // PrintHeader 119 | // 120 | void Printer::PrintHeader( ostream &c ) const { 121 | int dashes = 0; 122 | int iLen; 123 | int i, j; 124 | 125 | for (i = 0; i < attrCount; i++) { 126 | // Print out the header information name 127 | c << psHeader[i]; 128 | iLen = (int)strlen(psHeader[i]); 129 | dashes += iLen; 130 | 131 | for (j = 0; j < spaces[i]; j++) 132 | c << " "; 133 | 134 | dashes += spaces[i]; 135 | } 136 | 137 | c << "\n"; 138 | for (i = 0; i < dashes; i++) c << "-"; 139 | c << "\n"; 140 | } 141 | 142 | // 143 | // PrintFooter 144 | // 145 | void Printer::PrintFooter(ostream &c) const { 146 | c << "\n"; 147 | c << iCount << " tuple(s).\n"; 148 | } 149 | 150 | // 151 | // Print 152 | // 153 | // data - the actual data for the tuple to be printed 154 | // 155 | // The routine tries to make things line up nice, however no 156 | // attempt is made to keep the tuple constrained to some number of 157 | // characters. 158 | // 159 | void Printer::Print(ostream &c, const char * const data, bool isnull[]) { 160 | char str[MAXPRINTSTRING], strSpace[50]; 161 | int i, a; 162 | float b; 163 | 164 | if (data == NULL) 165 | return; 166 | 167 | // Increment the number of tuples printed 168 | iCount++; 169 | 170 | int nullableIndex = 0; 171 | 172 | for (i = 0; i < attrCount; i++) { 173 | bool this_isnull = false; 174 | if (!(attributes[i].attrSpecs & ATTR_SPEC_NOTNULL)) { 175 | this_isnull = isnull[nullableIndex++]; 176 | } 177 | if (attributes[i].attrType == STRING || this_isnull) { 178 | // We will only print out the first MAXNAME+10 characters of 179 | // the string value. 180 | memset(str, 0, MAXPRINTSTRING); 181 | 182 | const char* str_to_print = this_isnull ? "NULL" : data + attributes[i].offset; 183 | 184 | if (attributes[i].attrDisplayLength > MAXPRINTSTRING) { 185 | strncpy(str, str_to_print, MAXPRINTSTRING - 1); 186 | str[MAXPRINTSTRING - 3] = '.'; 187 | str[MAXPRINTSTRING - 2] = '.'; 188 | c << str; 189 | Spaces(MAXPRINTSTRING, strlen(str)); 190 | } else { 191 | strncpy(str, str_to_print, (size_t)(this_isnull ? 4 : attributes[i].attrDisplayLength)); 192 | c << str; 193 | if (attributes[i].attrDisplayLength < (int) strlen(psHeader[i])) 194 | Spaces((int)strlen(psHeader[i]), strlen(str)); 195 | else 196 | Spaces(attributes[i].attrDisplayLength, strlen(str)); 197 | } 198 | } else if (attributes[i].attrType == INT) { 199 | memcpy (&a, (data + attributes[i].offset), sizeof(int)); 200 | sprintf(strSpace, "%d", a); 201 | c << a; 202 | if (strlen(psHeader[i]) < 12) 203 | Spaces(12, strlen(strSpace)); 204 | else 205 | Spaces((int)strlen(psHeader[i]), strlen(strSpace)); 206 | } else if (attributes[i].attrType == FLOAT) { 207 | memcpy (&b, (data + attributes[i].offset), sizeof(float)); 208 | sprintf(strSpace, "%f", b); 209 | c << strSpace; 210 | if (strlen(psHeader[i]) < 12) 211 | Spaces(12, strlen(strSpace)); 212 | else 213 | Spaces((int)strlen(psHeader[i]), strlen(strSpace)); 214 | } 215 | } 216 | c << "\n"; 217 | } 218 | 219 | -------------------------------------------------------------------------------- /src/printer.h: -------------------------------------------------------------------------------- 1 | // 2 | // printer.h 3 | // 4 | 5 | // This file contains the interface for the Printer class and some 6 | // functions that will be used by both the SM and QL components. 7 | 8 | #ifndef _HELPER 9 | #define _HELPER 10 | 11 | #include 12 | #include 13 | #include 14 | #include "redbase.h" // For definition of MAXNAME 15 | 16 | #define MAXPRINTSTRING ((2*MAXNAME) + 5) 17 | 18 | // 19 | // DataAttrInfo 20 | // 21 | // This struct stores the information that is kept within in 22 | // attribute catalog. It identifies a relation name, attribute name 23 | // and the location type and length of the attribute. 24 | // 25 | struct DataAttrInfo { 26 | // Default constructor 27 | DataAttrInfo() { 28 | memset(relName, 0, MAXNAME + 1); 29 | memset(attrName, 0, MAXNAME + 1); 30 | }; 31 | 32 | // Copy constructor 33 | DataAttrInfo( const DataAttrInfo &d ) { 34 | strcpy (relName, d.relName); 35 | strcpy (attrName, d.attrName); 36 | offset = d.offset; 37 | attrType = d.attrType; 38 | attrSize = d.attrSize; 39 | attrDisplayLength = d.attrDisplayLength; 40 | attrSpecs = d.attrSpecs; 41 | indexNo = d.indexNo; 42 | nullableIndex = d.nullableIndex; 43 | }; 44 | 45 | DataAttrInfo& operator=(const DataAttrInfo &d) { 46 | if (this != &d) { 47 | strcpy (relName, d.relName); 48 | strcpy (attrName, d.attrName); 49 | offset = d.offset; 50 | attrType = d.attrType; 51 | attrSize = d.attrSize; 52 | attrDisplayLength = d.attrDisplayLength; 53 | attrSpecs = d.attrSpecs; 54 | indexNo = d.indexNo; 55 | nullableIndex = d.nullableIndex; 56 | } 57 | return (*this); 58 | }; 59 | 60 | char relName[MAXNAME + 1]; // Relation name 61 | char attrName[MAXNAME + 1]; // Attribute name 62 | int offset; // Offset of attribute 63 | AttrType attrType; // Type of attribute 64 | int attrSize; // Size of attribute 65 | int attrDisplayLength; // Length of attribute 66 | int attrSpecs; // Attribute specifications 67 | int indexNo; // Index number of attribute 68 | int nullableIndex; // Index on the nullable bitmap 69 | }; 70 | 71 | // Print some number of spaces 72 | void Spaces(int maxLength, int printedSoFar); 73 | 74 | class Printer { 75 | public: 76 | // Constructor. Takes as arguments an array of attributes along with 77 | // the length of the array. 78 | Printer(const std::vector &attributes); 79 | ~Printer(); 80 | 81 | void PrintHeader(std::ostream &c) const; 82 | 83 | // Two flavors for the Print routine. The first takes a char* to the 84 | // data and is useful when the data corresponds to a single record in 85 | // a table -- since in this situation you can just send in the 86 | // RecData. The second will be useful in the QL layer. 87 | void Print(std::ostream &c, const char * const data, bool isnull[]); 88 | 89 | void PrintFooter(std::ostream &c) const; 90 | 91 | private: 92 | DataAttrInfo *attributes; 93 | int attrCount; 94 | 95 | // An array of strings for the header information 96 | char **psHeader; 97 | // Number of spaces between each attribute 98 | int *spaces; 99 | 100 | // The number of tuples printed 101 | int iCount; 102 | }; 103 | 104 | 105 | // #ifndef min 106 | // #define min(a,b) (((a) < (b)) ? (a) : (b)) 107 | // #endif 108 | // 109 | // #ifndef max 110 | // #define max(a,b) (((a) > (b)) ? (a) : (b)) 111 | // #endif 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/ql.h: -------------------------------------------------------------------------------- 1 | // 2 | // ql.h 3 | // Query Language Component Interface 4 | // 5 | 6 | // This file only gives the stub for the QL component 7 | 8 | #ifndef QL_H 9 | #define QL_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include "redbase.h" 15 | #include "parser.h" 16 | #include "rm.h" 17 | #include "ix.h" 18 | #include "sm.h" 19 | #include "ql_internal.h" 20 | 21 | // 22 | // QL_Manager: query language (DML) 23 | // 24 | class QL_Manager { 25 | public: 26 | QL_Manager (SM_Manager &smm, IX_Manager &ixm, RM_Manager &rmm); 27 | ~QL_Manager(); // Destructor 28 | 29 | RC Select (int nSelAttrs, // # attrs in select clause 30 | const RelAttr selAttrs[], // attrs in select clause 31 | int nRelations, // # relations in from clause 32 | const char * const relations[], // relations in from clause 33 | int nConditions, // # conditions in where clause 34 | const Condition conditions[]); // conditions in where clause 35 | 36 | RC Insert (const char *relName, // relation to insert into 37 | int nValues, // # values 38 | const Value values[]); // values to insert 39 | 40 | RC Delete (const char *relName, // relation to delete from 41 | int nConditions, // # conditions in where clause 42 | const Condition conditions[]); // conditions in where clause 43 | 44 | RC Update (const char *relName, // relation to update 45 | const RelAttr &updAttr, // attribute to update 46 | const int bIsValue, // 1 if RHS is a value, 0 if attr 47 | const RelAttr &rhsRelAttr, // attr on RHS to set LHS eq to 48 | const Value &rhsValue, // or value to set attr eq to 49 | int nConditions, // # conditions in where clause 50 | const Condition conditions[]); // conditions in where clause 51 | 52 | private: 53 | SM_Manager *pSmm; 54 | IX_Manager *pIxm; 55 | RM_Manager *pRmm; 56 | 57 | RC CheckConditionsValid(const char *relName, int nConditions, const Condition *conditions, 58 | const std::map &attrMap, 59 | std::vector &retConditions); 60 | }; 61 | 62 | // 63 | // Print-error function 64 | // 65 | void QL_PrintError(RC rc); 66 | 67 | bool checkSatisfy(char *lhsData, bool lhsIsnull, char *rhsData, bool rhsIsnull, const QL_Condition &condition); 68 | bool checkSatisfy(char *data, bool *isnull, const QL_Condition &condition); 69 | 70 | #define QL_ATTR_COUNT_MISMATCH (START_QL_WARN + 0) 71 | #define QL_VALUE_TYPES_MISMATCH (START_QL_WARN + 1) 72 | #define QL_STRING_VAL_TOO_LONG (START_QL_WARN + 2) 73 | #define QL_ATTR_NOTEXIST (START_QL_WARN + 3) 74 | #define QL_ATTR_TYPES_MISMATCH (START_QL_WARN + 4) 75 | #define QL_AMBIGUOUS_ATTR_NAME (START_QL_WARN + 5) 76 | #define QL_FORBIDDEN (START_QL_WARN + 6) 77 | #define QL_ATTR_IS_NOTNULL (START_QL_WARN + 7) 78 | #define QL_DUPLICATE_PRIMARY_KEY (START_QL_WARN + 8) 79 | #define QL_LASTWARN QL_DUPLICATE_PRIMARY_KEY 80 | 81 | #define QL_SOMEERROR (START_QL_ERR - 0) 82 | #define QL_LASTERROR QL_SOMEERROR 83 | 84 | #endif // QL_H 85 | -------------------------------------------------------------------------------- /src/ql_disjoint.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/3. 3 | // 4 | 5 | #include "ql_disjoint.h" 6 | 7 | QL_DisjointSet::QL_DisjointSet(int n) { 8 | this->n = n; 9 | this->f = new int[n]; 10 | for (int i = 0; i < n; ++i) 11 | f[i] = i; 12 | } 13 | 14 | QL_DisjointSet::~QL_DisjointSet() { 15 | delete[] this->f; 16 | } 17 | 18 | int QL_DisjointSet::find(int x) { 19 | return x == f[x] ? x : f[x] = find(f[x]); 20 | } 21 | 22 | int QL_DisjointSet::join(int a, int b) { 23 | return f[find(a)] = find(b); 24 | } 25 | 26 | bool QL_DisjointSet::connected(int a, int b) { 27 | return find(a) == find(b); 28 | } 29 | -------------------------------------------------------------------------------- /src/ql_disjoint.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/3. 3 | // 4 | 5 | #ifndef REBASE_QL_DISJOINT_H 6 | #define REBASE_QL_DISJOINT_H 7 | 8 | 9 | class QL_DisjointSet { 10 | int *f, n; 11 | 12 | public: 13 | QL_DisjointSet(int n); 14 | ~QL_DisjointSet(); 15 | int find(int x); 16 | int join(int a, int b); 17 | bool connected(int a, int b); 18 | }; 19 | 20 | 21 | #endif //REBASE_QL_DISJOINT_H 22 | -------------------------------------------------------------------------------- /src/ql_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: ql_error.cc 3 | // Description: QL_PrintError function 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ql.h" 10 | 11 | using namespace std; 12 | 13 | // 14 | // Error table 15 | // 16 | const char *QL_WarnMsg[] = { 17 | "number of values to insert does not match", 18 | "value type does not match", 19 | "string value too long", 20 | "attribute does not exist", 21 | "attribute types does not match", 22 | "ambiguous attribute name", 23 | "operation forbidden", 24 | "attribute should not be null", 25 | "a record with the same primary key already exits", 26 | }; 27 | 28 | const char *QL_ErrorMsg[] = { 29 | 30 | }; 31 | 32 | // 33 | // QL_PrintError 34 | // 35 | // Desc: Send a message corresponding to a QL return code to cerr 36 | // In: rc - return code for which a message is desired 37 | // 38 | void QL_PrintError(RC rc) { 39 | // Check the return code is within proper limits 40 | if (rc >= START_QL_WARN && rc <= QL_LASTWARN) 41 | // Print warning 42 | cerr << "QL warning: " << QL_WarnMsg[rc - START_QL_WARN] << "\n"; 43 | // Error codes are negative, so invert everything 44 | else if ((-rc >= -START_QL_ERR) && -rc <= -QL_LASTERROR) { 45 | // Print error 46 | cerr << "QL error: " << QL_ErrorMsg[-rc + START_QL_ERR] << "\n"; 47 | } else if (rc == 0) 48 | cerr << "QL_PrintError called with return code of 0\n"; 49 | else { 50 | // Print error 51 | cerr << "rc was " << rc << endl; 52 | cerr << "START_QL_ERR was " << START_QL_ERR << endl; 53 | cerr << "QL_LASTERROR was " << QL_LASTERROR << endl; 54 | cerr << "QL error: " << rc << " is out of bounds\n"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/ql_filescaniter.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/4. 3 | // 4 | 5 | #include "ql_iterator.h" 6 | 7 | QL_FileScanIterator::QL_FileScanIterator(std::string relName) 8 | : QL_Iterator(), relName(relName) { 9 | QL_Iterator::rmm->OpenFile(relName.c_str(), fileHandle); 10 | scan.OpenScan(fileHandle, INT, 4, 0, NO_OP, NULL); 11 | } 12 | 13 | RC QL_FileScanIterator::GetNextRec(RM_Record &rec) { 14 | return scan.GetNextRec(rec); 15 | } 16 | 17 | RC QL_FileScanIterator::Reset() { 18 | TRY(scan.CloseScan()); 19 | TRY(scan.OpenScan(fileHandle, INT, 4, 0, NO_OP, NULL)); 20 | return 0; 21 | } 22 | 23 | void QL_FileScanIterator::Print(std::string prefix) { 24 | std::cout << prefix; 25 | std::cout << id << ": "; 26 | std::cout << "SCAN " << relName << std::endl; 27 | } 28 | -------------------------------------------------------------------------------- /src/ql_indexjoiniter.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/4. 3 | // 4 | 5 | #include "ql_iterator.h" 6 | 7 | QL_IndexedJoinIterator::QL_IndexedJoinIterator(QL_Iterator *scanIter, const std::vector &scanRel, 8 | QL_IndexSearchIterator *indexIter, int searchAttrOffset, 9 | QL_Iterator *searchIter, const std::vector &searchRel) 10 | : QL_Iterator(), scanIter(scanIter), scanRel(scanRel), 11 | indexIter(indexIter), searchAttrOffset(searchAttrOffset), 12 | searchIter(searchIter), searchRel(searchRel) { 13 | joinedSize = 0; 14 | nullableNum = 0; 15 | for (auto info : scanRel) { 16 | joinedSize += upper_align<4>(info.attrSize); 17 | if (!(info.attrSpecs & ATTR_SPEC_NOTNULL)) ++nullableNum; 18 | } 19 | scanSize = joinedSize; 20 | nullableNum1 = nullableNum; 21 | for (auto info : searchRel) { 22 | joinedSize += upper_align<4>(info.attrSize); 23 | if (!(info.attrSpecs & ATTR_SPEC_NOTNULL)) ++nullableNum; 24 | } 25 | data = new char[joinedSize]; 26 | isnull = new bool[nullableNum]; 27 | 28 | scanIter->GetNextRec(rec1); 29 | rec1.GetData(data1); 30 | rec1.GetIsnull(isnull1); 31 | indexIter->ChangeValue(data1 + searchAttrOffset); 32 | searchIter->Reset(); 33 | } 34 | 35 | QL_IndexedJoinIterator::~QL_IndexedJoinIterator() { 36 | delete[] data; 37 | delete[] isnull; 38 | } 39 | 40 | RC QL_IndexedJoinIterator::GetNextRec(RM_Record &rec) { 41 | int retcode = searchIter->GetNextRec(rec2); 42 | while (retcode == RM_EOF) { 43 | // prevent RM_EOF from triggering error reporting mechanism in TRY 44 | if (int rc = scanIter->GetNextRec(rec1)) { 45 | if (rc != RM_EOF) { 46 | TRY(rc); 47 | } 48 | return rc; 49 | } 50 | TRY(rec1.GetData(data1)); 51 | TRY(rec1.GetIsnull(isnull1)); 52 | indexIter->ChangeValue(data1 + searchAttrOffset); 53 | TRY(searchIter->Reset()); 54 | retcode = searchIter->GetNextRec(rec2); 55 | } 56 | if (retcode) return retcode; 57 | TRY(rec2.GetData(data2)); 58 | TRY(rec2.GetIsnull(isnull2)); 59 | memcpy(data, data1, scanSize); 60 | memcpy(data + scanSize, data2, joinedSize - scanSize); 61 | memcpy(isnull, isnull1, sizeof(bool) * nullableNum1); 62 | memcpy(isnull + nullableNum1, isnull2, sizeof(bool) * (nullableNum - nullableNum1)); 63 | rec.SetData(data, joinedSize); 64 | rec.SetIsnull(isnull, nullableNum); 65 | return 0; 66 | } 67 | 68 | RC QL_IndexedJoinIterator::Reset() { 69 | TRY(scanIter->Reset()); 70 | TRY(searchIter->Reset()); 71 | TRY(scanIter->GetNextRec(rec1)); 72 | TRY(rec1.GetData(data1)); 73 | TRY(rec1.GetIsnull(isnull1)); 74 | indexIter->ChangeValue(data1 + searchAttrOffset); 75 | return 0; 76 | } 77 | 78 | void QL_IndexedJoinIterator::Print(std::string prefix) { 79 | std::cout << prefix; 80 | std::cout << id << ": "; 81 | std::cout << "INDEXED JOIN " << scanIter->getID() << " and " << searchIter->getID() << std::endl; 82 | editPrefix(prefix); 83 | scanIter->Print(prefix + "├──"); 84 | searchIter->Print(prefix + "└──"); 85 | } 86 | -------------------------------------------------------------------------------- /src/ql_indexsearchiter.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/4. 3 | // 4 | 5 | #include "ql_iterator.h" 6 | 7 | QL_IndexSearchIterator::QL_IndexSearchIterator(QL_Condition condition) 8 | : QL_Iterator(), condition(condition) { 9 | QL_Iterator::rmm->OpenFile(condition.lhsAttr.relName, fileHandle); 10 | QL_Iterator::ixm->OpenIndex(condition.lhsAttr.relName, condition.lhsAttr.indexNo, indexHandle); 11 | scan.OpenScan(indexHandle, condition.op, condition.rhsValue.data); 12 | } 13 | 14 | void QL_IndexSearchIterator::ChangeValue(char *value) { 15 | condition.rhsValue.data = value; 16 | } 17 | 18 | RC QL_IndexSearchIterator::GetNextRec(RM_Record &rec) { 19 | RID rid; 20 | int retcode = scan.GetNextEntry(rid); 21 | if (retcode == IX_EOF) return RM_EOF; 22 | TRY(retcode); 23 | TRY(fileHandle.GetRec(rid, rec)); 24 | return 0; 25 | } 26 | 27 | RC QL_IndexSearchIterator::Reset() { 28 | TRY(scan.CloseScan()); 29 | TRY(scan.OpenScan(indexHandle, condition.op, condition.rhsValue.data)); 30 | return 0; 31 | } 32 | 33 | void QL_IndexSearchIterator::Print(std::string prefix) { 34 | std::cout << prefix; 35 | std::cout << id << ": "; 36 | std::cout << "SEARCH" << " " << condition << std::endl; 37 | } 38 | -------------------------------------------------------------------------------- /src/ql_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/12/28. 3 | // 4 | 5 | #ifndef REBASE_QL_INTERNAL_H 6 | #define REBASE_QL_INTERNAL_H 7 | 8 | #include "parser.h" 9 | #include "printer.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | typedef std::vector AttrList; 16 | 17 | typedef std::pair AttrTag; 18 | 19 | template 20 | using AttrMap = std::map; 21 | 22 | inline bool operator ==(const DataAttrInfo &lhs, const DataAttrInfo &rhs) { 23 | return !strcmp(lhs.relName, rhs.relName) && !strcmp(lhs.attrName, rhs.attrName); 24 | } 25 | 26 | inline bool operator ==(const Value &lhs, const Value &rhs) { 27 | return lhs.type == rhs.type && lhs.data == rhs.data; 28 | } 29 | 30 | struct QL_Condition { 31 | DataAttrInfo lhsAttr; 32 | CompOp op; 33 | bool bRhsIsAttr; 34 | DataAttrInfo rhsAttr; 35 | Value rhsValue; 36 | 37 | inline bool operator ==(const QL_Condition &rhs) const { 38 | return lhsAttr == rhs.lhsAttr && 39 | op == rhs.op && 40 | bRhsIsAttr == rhs.bRhsIsAttr && 41 | ((bRhsIsAttr && rhsAttr == rhs.rhsAttr) || 42 | (!bRhsIsAttr && rhsValue == rhs.rhsValue)); 43 | } 44 | 45 | friend std::ostream &operator <<(std::ostream &os, const QL_Condition &condition); 46 | }; 47 | 48 | #endif //REBASE_QL_INTERNAL_H 49 | -------------------------------------------------------------------------------- /src/ql_iterator.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/4. 3 | // 4 | 5 | #include "ql_iterator.h" 6 | 7 | int QL_Iterator::totalIters = 0; 8 | 9 | void QL_Iterator::editPrefix(std::string &prefix) { 10 | if (prefix.length() > 0) { 11 | if (prefix.substr(prefix.length() - 9, 3) == "├") { 12 | prefix = prefix.substr(0, prefix.length() - 9) + "│ "; 13 | } else { 14 | prefix = prefix.substr(0, prefix.length() - 9) + " "; 15 | } 16 | } 17 | } 18 | 19 | RM_Manager *QL_Iterator::rmm; 20 | IX_Manager *QL_Iterator::ixm; 21 | -------------------------------------------------------------------------------- /src/ql_iterator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/3. 3 | // 4 | 5 | #ifndef REBASE_QL_ITERATOR_H 6 | #define REBASE_QL_ITERATOR_H 7 | 8 | 9 | #include "redbase.h" 10 | #include "rm.h" 11 | #include "ix.h" 12 | #include "ql.h" 13 | #include "ql_internal.h" 14 | 15 | class QL_Iterator { 16 | static int totalIters; 17 | protected: 18 | int id; 19 | static RM_Manager *rmm; 20 | static IX_Manager *ixm; 21 | void editPrefix(std::string &prefix); 22 | public: 23 | QL_Iterator() { 24 | id = ++totalIters; 25 | } 26 | 27 | static void setRM(RM_Manager *rmm) { 28 | QL_Iterator::rmm = rmm; 29 | } 30 | 31 | static void setIX(IX_Manager *ixm) { 32 | QL_Iterator::ixm = ixm; 33 | } 34 | 35 | int getID() const { return id; } 36 | 37 | virtual RC GetNextRec(RM_Record &rec) = 0; 38 | 39 | virtual RC Reset() = 0; 40 | virtual void Print(std::string prefix = "") = 0; 41 | }; 42 | 43 | class QL_FileScanIterator : public QL_Iterator { 44 | std::string relName; 45 | RM_FileHandle fileHandle; 46 | RM_FileScan scan; 47 | public: 48 | QL_FileScanIterator(std::string relName); 49 | 50 | RC GetNextRec(RM_Record &rec) override; 51 | RC Reset() override; 52 | void Print(std::string prefix = "") override; 53 | }; 54 | 55 | class QL_SelectionIterator : public QL_Iterator { 56 | QL_Iterator *inputIter; 57 | std::vector conditions; 58 | public: 59 | QL_SelectionIterator(QL_Iterator *iter, const std::vector &conditions); 60 | 61 | RC GetNextRec(RM_Record &rec) override; 62 | RC Reset() override; 63 | void Print(std::string prefix = "") override; 64 | }; 65 | 66 | class QL_ProjectionIterator : public QL_Iterator { 67 | QL_Iterator *inputIter; 68 | AttrList projectFrom; 69 | AttrList projectTo; 70 | 71 | size_t projectedSize; 72 | short nullableNum; 73 | char *data; 74 | bool *isnull; 75 | public: 76 | QL_ProjectionIterator(QL_Iterator *iter, 77 | const AttrList &projectFrom, const AttrList &projectTo); 78 | virtual ~QL_ProjectionIterator(); 79 | 80 | RC GetNextRec(RM_Record &rec) override; 81 | RC Reset() override; 82 | void Print(std::string prefix = "") override; 83 | }; 84 | 85 | class QL_IndexSearchIterator : public QL_Iterator { 86 | QL_Condition condition; 87 | RM_FileHandle fileHandle; 88 | IX_IndexHandle indexHandle; 89 | IX_IndexScan scan; 90 | public: 91 | QL_IndexSearchIterator(QL_Condition condition); 92 | void ChangeValue(char *value); 93 | 94 | RC GetNextRec(RM_Record &rec) override; 95 | RC Reset() override; 96 | void Print(std::string prefix = "") override; 97 | }; 98 | 99 | class QL_NestedLoopJoinIterator : public QL_Iterator { 100 | QL_Iterator *inputIter1; 101 | AttrList rel1; 102 | QL_Iterator *inputIter2; 103 | AttrList rel2; 104 | 105 | RM_Record rec1; 106 | RM_Record rec2; 107 | size_t joinedSize, rec1Size; 108 | short nullableNum, nullableNum1; 109 | char *data, *data1, *data2; 110 | bool *isnull, *isnull1, *isnull2; 111 | public: 112 | QL_NestedLoopJoinIterator(QL_Iterator *iter1, const AttrList &rel1, 113 | QL_Iterator *iter2, const AttrList &rel2); 114 | virtual ~QL_NestedLoopJoinIterator(); 115 | 116 | RC GetNextRec(RM_Record &rec) override; 117 | RC Reset() override; 118 | void Print(std::string prefix = "") override; 119 | }; 120 | 121 | class QL_IndexedJoinIterator : public QL_Iterator { 122 | QL_Iterator *scanIter; 123 | AttrList scanRel; 124 | QL_IndexSearchIterator *indexIter; 125 | int searchAttrOffset; 126 | QL_Iterator *searchIter; 127 | AttrList searchRel; 128 | 129 | RM_Record rec1; 130 | RM_Record rec2; 131 | size_t joinedSize, scanSize; 132 | short nullableNum, nullableNum1; 133 | char *data, *data1, *data2; 134 | bool *isnull, *isnull1, *isnull2; 135 | public: 136 | QL_IndexedJoinIterator(QL_Iterator *scanIter, const AttrList &scanRel, 137 | QL_IndexSearchIterator *indexIter, int searchAttrOffset, 138 | QL_Iterator *searchIter, const AttrList &searchRel); 139 | virtual ~QL_IndexedJoinIterator(); 140 | 141 | RC GetNextRec(RM_Record &rec) override; 142 | RC Reset() override; 143 | void Print(std::string prefix = "") override; 144 | }; 145 | 146 | 147 | #endif //REBASE_QL_ITERATOR_H 148 | -------------------------------------------------------------------------------- /src/ql_joiniter.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/4. 3 | // 4 | 5 | #include "ql_iterator.h" 6 | 7 | QL_NestedLoopJoinIterator::QL_NestedLoopJoinIterator(QL_Iterator *iter1, const AttrList &rel1, QL_Iterator *iter2, const AttrList &rel2) 8 | : QL_Iterator(), inputIter1(iter1), rel1(rel1), inputIter2(iter2), rel2(rel2) { 9 | joinedSize = 0; 10 | nullableNum = 0; 11 | for (auto info : rel1) { 12 | joinedSize += upper_align<4>(info.attrSize); 13 | if (!(info.attrSpecs & ATTR_SPEC_NOTNULL)) ++nullableNum; 14 | } 15 | rec1Size = joinedSize; 16 | nullableNum1 = nullableNum; 17 | for (auto info : rel2) { 18 | joinedSize += upper_align<4>(info.attrSize); 19 | if (!(info.attrSpecs & ATTR_SPEC_NOTNULL)) ++nullableNum; 20 | } 21 | data = new char[joinedSize]; 22 | isnull = new bool[nullableNum]; 23 | 24 | inputIter1->GetNextRec(rec1); 25 | rec1.GetData(data1); 26 | rec1.GetIsnull(isnull1); 27 | } 28 | 29 | QL_NestedLoopJoinIterator::~QL_NestedLoopJoinIterator() { 30 | delete[] data; 31 | delete[] isnull; 32 | } 33 | 34 | RC QL_NestedLoopJoinIterator::GetNextRec(RM_Record &rec) { 35 | int retcode = inputIter2->GetNextRec(rec2); 36 | while (retcode == RM_EOF) { 37 | if (int rc = inputIter1->GetNextRec(rec1)) { 38 | if (rc == RM_EOF) return RM_EOF; 39 | TRY(rc); 40 | } 41 | TRY(rec1.GetData(data1)); 42 | TRY(rec1.GetIsnull(isnull1)); 43 | TRY(inputIter2->Reset()); 44 | retcode = inputIter2->GetNextRec(rec2); 45 | } 46 | TRY(retcode); 47 | TRY(rec2.GetData(data2)); 48 | TRY(rec2.GetIsnull(isnull2)); 49 | memcpy(data, data1, rec1Size); 50 | memcpy(data + rec1Size, data2, joinedSize - rec1Size); 51 | memcpy(isnull, isnull1, sizeof(bool) * nullableNum1); 52 | memcpy(isnull + nullableNum1, isnull2, sizeof(bool) * (nullableNum - nullableNum1)); 53 | rec.SetData(data, joinedSize); 54 | rec.SetIsnull(isnull, nullableNum); 55 | return 0; 56 | } 57 | 58 | RC QL_NestedLoopJoinIterator::Reset() { 59 | TRY(inputIter1->Reset()); 60 | TRY(inputIter2->Reset()); 61 | TRY(inputIter1->GetNextRec(rec1)); 62 | TRY(rec1.GetData(data1)); 63 | TRY(rec1.GetIsnull(isnull1)); 64 | return 0; 65 | } 66 | 67 | void QL_NestedLoopJoinIterator::Print(std::string prefix) { 68 | std::cout << prefix; 69 | std::cout << id << ": "; 70 | std::cout << "NESTED-LOOP JOIN " << inputIter1->getID() << " and " << inputIter2->getID() << std::endl; 71 | editPrefix(prefix); 72 | inputIter1->Print(prefix + "├──"); 73 | inputIter2->Print(prefix + "└──"); 74 | } 75 | -------------------------------------------------------------------------------- /src/ql_projectioniter.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/4. 3 | // 4 | 5 | #include "ql_iterator.h" 6 | 7 | QL_ProjectionIterator::QL_ProjectionIterator(QL_Iterator *iter, const AttrList &projectFrom, const AttrList &projectTo) 8 | : QL_Iterator(), inputIter(iter), projectFrom(projectFrom), projectTo(projectTo) { 9 | projectedSize = 0; 10 | nullableNum = 0; 11 | for (auto info : projectTo) { 12 | projectedSize += upper_align<4>(info.attrSize); 13 | if (!(info.attrSpecs & ATTR_SPEC_NOTNULL)) ++nullableNum; 14 | } 15 | data = new char[projectedSize]; 16 | isnull = new bool[nullableNum]; 17 | } 18 | 19 | QL_ProjectionIterator::~QL_ProjectionIterator() { 20 | delete[] data; 21 | delete[] isnull; 22 | } 23 | 24 | RC QL_ProjectionIterator::GetNextRec(RM_Record &rec) { 25 | RM_Record input; 26 | char *inputData; 27 | bool *inputIsnull; 28 | // prevent RM_EOF from triggering error reporting mechanism in TRY 29 | if (int rc = inputIter->GetNextRec(input)) { 30 | if (rc != RM_EOF) { 31 | TRY(rc); 32 | } 33 | return rc; 34 | } 35 | TRY(input.GetData(inputData)); 36 | TRY(input.GetIsnull(inputIsnull)); 37 | for (int i = 0; i < projectFrom.size(); ++i) { 38 | memcpy(data + projectTo[i].offset, inputData + projectFrom[i].offset, (size_t)projectTo[i].attrSize); 39 | if (!(projectFrom[i].attrSpecs & ATTR_SPEC_NOTNULL)) 40 | isnull[projectTo[i].nullableIndex] = inputIsnull[projectFrom[i].nullableIndex]; 41 | } 42 | rec.SetData(data, projectedSize); 43 | rec.SetIsnull(isnull, nullableNum); 44 | return 0; 45 | } 46 | 47 | RC QL_ProjectionIterator::Reset() { 48 | return inputIter->Reset(); 49 | } 50 | 51 | void QL_ProjectionIterator::Print(std::string prefix) { 52 | std::cout << prefix; 53 | std::cout << id << ": "; 54 | std::cout << "PROJECTION"; 55 | for (auto proj : projectTo) 56 | std::cout << " " << proj.attrName; 57 | std::cout << std::endl; 58 | editPrefix(prefix); 59 | inputIter->Print(prefix + "└──"); 60 | } 61 | -------------------------------------------------------------------------------- /src/ql_selectioniter.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2017/1/4. 3 | // 4 | 5 | #include "ql_iterator.h" 6 | 7 | QL_SelectionIterator::QL_SelectionIterator(QL_Iterator *iter, const std::vector &conditions) 8 | : QL_Iterator(), inputIter(iter), conditions(conditions) {} 9 | 10 | RC QL_SelectionIterator::GetNextRec(RM_Record &rec) { 11 | char *data; 12 | bool *isnull; 13 | while (true) { 14 | int retcode = inputIter->GetNextRec(rec); 15 | if (retcode == RM_EOF) return RM_EOF; 16 | TRY(retcode); 17 | TRY(rec.GetData(data)); 18 | TRY(rec.GetIsnull(isnull)); 19 | bool ok = true; 20 | for (int i = 0; i < conditions.size() && ok; ++i) 21 | ok = checkSatisfy(data, isnull, conditions[i]); 22 | if (ok) break; 23 | } 24 | return 0; 25 | } 26 | 27 | RC QL_SelectionIterator::Reset() { 28 | return inputIter->Reset(); 29 | } 30 | 31 | void QL_SelectionIterator::Print(std::string prefix) { 32 | std::cout << prefix; 33 | std::cout << id << ": "; 34 | std::cout << "SELECTION"; 35 | for (auto cond : conditions) 36 | std::cout << " " << cond; 37 | std::cout << std::endl; 38 | editPrefix(prefix); 39 | inputIter->Print(prefix + "└──"); 40 | } 41 | -------------------------------------------------------------------------------- /src/redbase.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // redbase.cc 3 | // 4 | // Author: Jason McHugh (mchughj@cs.stanford.edu) 5 | // 6 | // This shell is provided for the student. 7 | 8 | #include 9 | #include "redbase.h" 10 | #include "rm.h" 11 | #include "sm.h" 12 | #include "ql.h" 13 | 14 | #include 15 | 16 | DEFINE_string(c, "-", 17 | "batch mode rather than interactive mode.\n" \ 18 | " reads commands from \n" \ 19 | " use '-' (without quotes) to use interactive mode."); 20 | 21 | DECLARE_bool(n); 22 | 23 | using namespace std; 24 | 25 | PF_Manager pfm; 26 | RM_Manager rmm(pfm); 27 | IX_Manager ixm(pfm); 28 | SM_Manager smm(ixm, rmm); 29 | QL_Manager qlm(smm, ixm, rmm); 30 | 31 | extern FILE* yyin; 32 | extern bool output_prompt; 33 | 34 | // 35 | // main 36 | // 37 | int main(int argc, char *argv[]) { 38 | FLAGS_logtostderr = 1; 39 | gflags::ParseCommandLineFlags(&argc, &argv, true); 40 | google::InitGoogleLogging(argv[0]); 41 | 42 | if (FLAGS_c != "-") { 43 | yyin = fopen(FLAGS_c.c_str(), "r"); 44 | CHECK(yyin != NULL) << "cannot open " << FLAGS_c; 45 | output_prompt = false; 46 | } else { 47 | output_prompt = true; 48 | } 49 | 50 | RBparse(pfm, smm, qlm); 51 | 52 | if (yyin != stdin) { 53 | fclose(yyin); 54 | } 55 | 56 | cout << "Bye.\n"; 57 | } 58 | -------------------------------------------------------------------------------- /src/redbase.h: -------------------------------------------------------------------------------- 1 | // 2 | // redbase.h 3 | // global declarations 4 | // 5 | #ifndef REDBASE_H 6 | #define REDBASE_H 7 | 8 | // Please DO NOT include any other files in this file. 9 | 10 | // 11 | // Globally-useful defines 12 | // 13 | #define MAXNAME 24 // maximum length of a relation 14 | // or attribute name 15 | #define MAXSTRINGLEN 255 // maximum length of a 16 | // string-type attribute 17 | #define MAXATTRS 40 // maximum number of attributes 18 | // in a relation 19 | #define MAXINSERTATTRS 1024 // maximum number of attributes 20 | // in a single INSERT command 21 | 22 | //#define yywrap() 1 23 | inline static int yywrap() { 24 | return 1; 25 | } 26 | void yyerror(const char *); 27 | 28 | // 29 | // Return codes 30 | // 31 | typedef int RC; 32 | 33 | #define OK_RC 0 // OK_RC return code is guaranteed to always be 0 34 | 35 | #define START_PF_ERR (-1) 36 | #define END_PF_ERR (-100) 37 | #define START_RM_ERR (-101) 38 | #define END_RM_ERR (-200) 39 | #define START_IX_ERR (-201) 40 | #define END_IX_ERR (-300) 41 | #define START_SM_ERR (-301) 42 | #define END_SM_ERR (-400) 43 | #define START_QL_ERR (-401) 44 | #define END_QL_ERR (-500) 45 | 46 | #define START_PF_WARN 1 47 | #define END_PF_WARN 100 48 | #define START_RM_WARN 101 49 | #define END_RM_WARN 200 50 | #define START_IX_WARN 201 51 | #define END_IX_WARN 300 52 | #define START_SM_WARN 301 53 | #define END_SM_WARN 400 54 | #define START_QL_WARN 401 55 | #define END_QL_WARN 500 56 | 57 | // ALL_PAGES is defined and used by the ForcePages method defined in RM 58 | // and PF layers 59 | const int ALL_PAGES = -1; 60 | 61 | // 62 | // Attribute types 63 | // 64 | enum AttrType { 65 | INT, 66 | FLOAT, 67 | STRING 68 | }; 69 | 70 | enum ValueType { 71 | VT_NULL, 72 | VT_INT, 73 | VT_FLOAT, 74 | VT_STRING, 75 | }; 76 | 77 | // Attribute specifications 78 | enum AttrSpec { 79 | ATTR_SPEC_NONE = 0x0, 80 | ATTR_SPEC_NOTNULL = 0x1, 81 | ATTR_SPEC_PRIMARYKEY = 0x2, 82 | }; 83 | 84 | // 85 | // Comparison operators 86 | // 87 | enum CompOp { 88 | NO_OP, // no comparison 89 | ISNULL_OP, NOTNULL_OP, // unary operators 90 | EQ_OP, NE_OP, LT_OP, GT_OP, LE_OP, GE_OP // binary atomic operators 91 | }; 92 | 93 | // 94 | // Pin Strategy Hint 95 | // 96 | enum ClientHint { 97 | NO_HINT // default value 98 | }; 99 | 100 | // 101 | // TRUE, FALSE and BOOLEAN 102 | // 103 | #ifndef BOOLEAN 104 | typedef char Boolean; 105 | #endif 106 | 107 | #ifndef FALSE 108 | #define FALSE 0 109 | #endif 110 | 111 | #ifndef TRUE 112 | #define TRUE 1 113 | #endif 114 | 115 | #ifndef NULL 116 | #define NULL 0 117 | #endif 118 | 119 | 120 | #define CVOID(_x) (*(reinterpret_cast(&(_x)))) 121 | 122 | #define ARR_PTR(_name, _type, _size) \ 123 | auto __##_name##__ = std::make_unique<_type[]>((size_t)_size); \ 124 | _type * _name = __##_name##__.get() 125 | //#define ARR_PTR(_name, _type, _size) _type * _name = new _type[(size_t)_size] 126 | 127 | #ifdef __cplusplus 128 | template 129 | inline T upper_align(T x) { 130 | return (x + (N - 1)) & ~((unsigned)(N - 1)); 131 | } 132 | 133 | #include 134 | 135 | #define TRY(_x) \ 136 | if (int __rc = (_x)) { \ 137 | VLOG(1) << "non-zero return code " << __rc; \ 138 | return __rc; \ 139 | } 140 | 141 | #else 142 | 143 | #define TRY(_x) \ 144 | if (int __rc = (-x)) { \ 145 | return __rc; \ 146 | } 147 | 148 | #endif // __cpluslus 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /src/rm.h: -------------------------------------------------------------------------------- 1 | // 2 | // rm.h 3 | // 4 | // Record Manager component interface 5 | // 6 | // This file does not include the interface for the RID class. This is 7 | // found in rm_rid.h 8 | // 9 | 10 | #ifndef RM_H 11 | #define RM_H 12 | 13 | // Please DO NOT include any files other than redbase.h and pf.h in this 14 | // file. When you submit your code, the test program will be compiled 15 | // with your rm.h and your redbase.h, along with the standard pf.h that 16 | // was given to you. Your rm.h, your redbase.h, and the standard pf.h 17 | // should therefore be self-contained (i.e., should not depend upon 18 | // declarations in any other file). 19 | 20 | // Do not change the following includes 21 | #include "redbase.h" 22 | #include "rm_rid.h" 23 | #include "pf.h" 24 | 25 | #include 26 | #include 27 | 28 | // 29 | // RM_Record: RM Record interface 30 | // 31 | class RM_Record { 32 | friend class RM_FileHandle; 33 | friend class RM_FileScan; 34 | 35 | char *pData; 36 | bool *isnull; 37 | RID rid; 38 | public: 39 | RM_Record (); 40 | RM_Record(const RM_Record&) = delete; 41 | ~RM_Record(); 42 | 43 | RM_Record& operator=(const RM_Record&) = delete; 44 | 45 | void SetData(char *data, size_t size); 46 | void SetIsnull(bool* isnull, short nullableNum); 47 | 48 | // Return the data corresponding to the record. Sets *pData to the 49 | // record contents. 50 | RC GetData(char *&pData) const; 51 | RC GetIsnull(bool *&pData) const; 52 | 53 | // Return the RID associated with the record 54 | RC GetRid (RID &rid) const; 55 | }; 56 | 57 | // 58 | // RM_FileHandle: RM File interface 59 | // 60 | class RM_FileHandle { 61 | friend class RM_Manager; 62 | friend class RM_FileScan; 63 | 64 | PF_FileHandle pfHandle; 65 | short recordSize; 66 | short recordsPerPage; 67 | int firstFreePage; 68 | short pageHeaderSize; 69 | 70 | short nullableNum; 71 | short* nullableOffsets; 72 | 73 | bool isHeaderDirty; 74 | public: 75 | RM_FileHandle (); 76 | ~RM_FileHandle(); 77 | 78 | // Given a RID, return the record 79 | RC GetRec (const RID &rid, RM_Record &rec) const; 80 | 81 | // Insert a new record 82 | // `isnull' gives the information for each nullable fields 83 | RC InsertRec (const char *pData, RID &rid, bool *isnull = NULL); 84 | 85 | RC DeleteRec (const RID &rid); // Delete a record 86 | RC UpdateRec (const RM_Record &rec); // Update a record 87 | 88 | // Forces a page (along with any contents stored in this class) 89 | // from the buffer pool to disk. Default value forces all pages. 90 | RC ForcePages (PageNum pageNum = ALL_PAGES); 91 | }; 92 | 93 | // 94 | // RM_FileScan: condition-based scan of records in the file 95 | // 96 | class RM_FileScan { 97 | const RM_FileHandle *fileHandle; 98 | AttrType attrType; 99 | int attrLength; 100 | int attrOffset; 101 | CompOp compOp; 102 | 103 | union { 104 | int intVal; 105 | float floatVal; 106 | char *stringVal; 107 | } value; 108 | 109 | bool scanOpened; 110 | PageNum currentPageNum; 111 | SlotNum currentSlotNum; 112 | short recordSize; 113 | int nullableIndex; 114 | 115 | bool checkSatisfy(char *data, bool isnull); 116 | public: 117 | RM_FileScan (); 118 | ~RM_FileScan (); 119 | 120 | RC OpenScan (const RM_FileHandle &fileHandle, 121 | AttrType attrType, 122 | int attrLength, 123 | int attrOffset, 124 | CompOp compOp, 125 | void *value, 126 | ClientHint pinHint = NO_HINT); // Initialize a file scan 127 | RC GetNextRec(RM_Record &rec); // Get next matching record 128 | RC CloseScan (); // Close the scan 129 | }; 130 | 131 | // 132 | // RM_Manager: provides RM file management 133 | // 134 | class RM_Manager { 135 | PF_Manager *pfm; 136 | public: 137 | RM_Manager (PF_Manager &pfm); 138 | ~RM_Manager (); 139 | 140 | RC CreateFile (const char *fileName, int recordSize, 141 | short nullableNum = 0, short *nullableOffsets = NULL); 142 | RC DestroyFile(const char *fileName); 143 | RC OpenFile (const char *fileName, RM_FileHandle &fileHandle); 144 | 145 | RC CloseFile (RM_FileHandle &fileHandle); 146 | }; 147 | 148 | // 149 | // Print-error function 150 | // 151 | void RM_PrintError(RC rc); 152 | 153 | #define RM_FILE_NOT_OPENED (START_RM_WARN + 0) // file is not opened 154 | #define RM_SLOTNUM_OUT_OF_RANGE (START_RM_WARN + 1) // SlotNum < 0 || >= records/page 155 | #define RM_RECORD_DELETED (START_RM_WARN + 2) // record already deleted 156 | #define RM_EOF (START_RM_WARN + 3) 157 | #define RM_UNINITIALIZED_RECORD (START_RM_WARN + 4) 158 | #define RM_UNINITIALIZED_RID (START_RM_WARN + 5) 159 | #define RM_SCAN_NOT_OPENED (START_RM_WARN + 6) 160 | #define RM_SCAN_NOT_CLOSED (START_RM_WARN + 7) 161 | #define RM_LASTWARN RM_SCAN_NOT_CLOSED 162 | 163 | #define RM_RECORDSIZE_TOO_LARGE (START_RM_ERR - 0) // record size larger than PF_PAGE_SIZE 164 | #define RM_BAD_NULLABLE_NUM (START_RM_ERR - 1) // nullableNum out of range 165 | #define RM_LASTERROR RM_BAD_NULLABLE_NUM 166 | 167 | #endif 168 | -------------------------------------------------------------------------------- /src/rm_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/10/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "rm.h" 9 | 10 | using namespace std; 11 | 12 | // 13 | // Error table 14 | // 15 | static const char *RM_WarnMsg[] = { 16 | "file is not opened", 17 | "SlotNum is out of range", 18 | "record is already deleted", 19 | "no more records in scan", 20 | "Record is not properly initialized", 21 | "RID is not properly initialized", 22 | "scan is not opened", 23 | "last opened scan is not closed" 24 | }; 25 | 26 | static const char *RM_ErrorMsg[] = { 27 | "recordSize is too large for current pagefile system", 28 | "nullable num read from the header is out of range", 29 | }; 30 | 31 | void RM_PrintError(RC rc) { 32 | // Check the return code is within proper limits 33 | if (rc >= START_RM_WARN && rc <= RM_LASTWARN) 34 | // Print warning 35 | cerr << "RM warning: " << RM_WarnMsg[rc - START_RM_WARN] << "\n"; 36 | // Error codes are negative, so invert everything 37 | else if (-rc >= -START_RM_ERR && -rc < -RM_LASTERROR) 38 | // Print error 39 | cerr << "RM error: " << RM_ErrorMsg[-rc + START_RM_ERR] << "\n"; 40 | else if (rc == 0) 41 | cerr << "RM_PrintError called with return code of 0\n"; 42 | else 43 | PF_PrintError(rc); 44 | } 45 | -------------------------------------------------------------------------------- /src/rm_filehandle.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/10/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include "pf.h" 8 | #include "rm.h" 9 | #include "rm_internal.h" 10 | 11 | /* RM FileHandle */ 12 | RM_FileHandle::RM_FileHandle() { 13 | recordSize = 0; 14 | } 15 | 16 | RM_FileHandle::~RM_FileHandle() {} 17 | 18 | RC RM_FileHandle::GetRec(const RID &rid, RM_Record &rec) const { 19 | if (recordSize == 0) return RM_FILE_NOT_OPENED; 20 | PageNum pageNum; 21 | SlotNum slotNum; 22 | PF_PageHandle pageHandle; 23 | char *data; 24 | TRY(rid.GetPageNum(pageNum)); 25 | TRY(rid.GetSlotNum(slotNum)); 26 | if (slotNum >= recordsPerPage || slotNum < 0) 27 | return RM_SLOTNUM_OUT_OF_RANGE; 28 | TRY(pfHandle.GetThisPage(pageNum, pageHandle)); 29 | TRY(pageHandle.GetData(data)); 30 | 31 | rec.rid = rid; 32 | rec.SetData(data + pageHeaderSize + recordSize * slotNum, (size_t)recordSize); 33 | 34 | if (nullableNum > 0) { 35 | bool *isnull = new bool[nullableNum]; 36 | for (int i = 0; i < nullableNum; ++i) { 37 | isnull[i] = getBitMap(((RM_PageHeader *)data)->bitmap, 38 | recordsPerPage + slotNum * nullableNum + i); 39 | } 40 | rec.SetIsnull(isnull, nullableNum); 41 | } 42 | 43 | TRY(pfHandle.UnpinPage(pageNum)); 44 | return 0; 45 | } 46 | 47 | RC RM_FileHandle::InsertRec(const char *pData, RID &rid, bool *isnull) { 48 | if (recordSize == 0) return RM_FILE_NOT_OPENED; 49 | PageNum pageNum; 50 | SlotNum slotNum; 51 | PF_PageHandle pageHandle; 52 | char *data, *destination; 53 | 54 | if (firstFreePage != kLastFreePage) { 55 | TRY(pfHandle.GetThisPage(firstFreePage, pageHandle)); 56 | TRY(pageHandle.GetPageNum(pageNum)); 57 | TRY(pageHandle.GetData(data)); 58 | slotNum = ((RM_PageHeader *)data)->firstFreeRecord; 59 | destination = data + pageHeaderSize + recordSize * slotNum; 60 | ((RM_PageHeader *)data)->firstFreeRecord = *(short *)destination; 61 | if (*(short *)destination == kLastFreeRecord) { 62 | firstFreePage = ((RM_PageHeader *)data)->nextFreePage; 63 | isHeaderDirty = true; 64 | } 65 | } else { 66 | TRY(pfHandle.GetLastPage(pageHandle)); 67 | TRY(pageHandle.GetPageNum(pageNum)); 68 | TRY(pageHandle.GetData(data)); 69 | CHECK(((RM_PageHeader *)data)->allocatedRecords <= recordsPerPage); 70 | if (((RM_PageHeader *)data)->allocatedRecords == recordsPerPage) { 71 | TRY(pfHandle.UnpinPage(pageNum)); 72 | TRY(pfHandle.AllocatePage(pageHandle)); 73 | TRY(pageHandle.GetPageNum(pageNum)); 74 | TRY(pageHandle.GetData(data)); 75 | *(RM_PageHeader *)data = {kLastFreeRecord, 0, kLastFreePage}; 76 | memset(data + offsetof(RM_PageHeader, bitmap), 0, 77 | (size_t)(recordsPerPage * (nullableNum + 1))); 78 | } 79 | slotNum = ((RM_PageHeader *)data)->allocatedRecords; 80 | destination = data + pageHeaderSize + recordSize * slotNum; 81 | // LOG(INFO) << "recordSize = " << recordSize << " slotnum = " << slotNum; 82 | // LOG(INFO) << "recordsPerPage = " << recordsPerPage << " allocated = " << 83 | // ((RM_PageHeader *)data)->allocatedRecords; 84 | ++((RM_PageHeader *)data)->allocatedRecords; 85 | } 86 | memcpy(destination, pData, (size_t)recordSize); 87 | setBitMap(((RM_PageHeader *)data)->bitmap, slotNum, true); 88 | for (int i = 0; i < nullableNum; ++i) { 89 | setBitMap(((RM_PageHeader *)data)->bitmap, 90 | recordsPerPage + slotNum * nullableNum + i, isnull[i]); 91 | } 92 | rid = RID(pageNum, slotNum); 93 | 94 | TRY(pfHandle.MarkDirty(pageNum)); 95 | TRY(pfHandle.UnpinPage(pageNum)); 96 | return 0; 97 | } 98 | 99 | RC RM_FileHandle::DeleteRec(const RID &rid) { 100 | if (recordSize == 0) return RM_FILE_NOT_OPENED; 101 | PageNum pageNum; 102 | SlotNum slotNum; 103 | PF_PageHandle pageHandle; 104 | char *data; 105 | TRY(rid.GetPageNum(pageNum)); 106 | TRY(rid.GetSlotNum(slotNum)); 107 | if (slotNum >= recordsPerPage || slotNum < 0) 108 | return RM_SLOTNUM_OUT_OF_RANGE; 109 | TRY(pfHandle.GetThisPage(pageNum, pageHandle)); 110 | TRY(pageHandle.GetData(data)); 111 | 112 | if (getBitMap(((RM_PageHeader *)data)->bitmap, slotNum) == 0) 113 | return RM_RECORD_DELETED; 114 | setBitMap(((RM_PageHeader *)data)->bitmap, slotNum, false); 115 | *(short *)(data + pageHeaderSize + recordSize * slotNum) = ((RM_PageHeader *)data)->firstFreeRecord; 116 | if (((RM_PageHeader *)data)->firstFreeRecord == kLastFreeRecord) { 117 | ((RM_PageHeader *)data)->nextFreePage = firstFreePage; 118 | firstFreePage = pageNum; 119 | isHeaderDirty = true; 120 | } 121 | ((RM_PageHeader *)data)->firstFreeRecord = (short)slotNum; 122 | 123 | TRY(pfHandle.MarkDirty(pageNum)); 124 | TRY(pfHandle.UnpinPage(pageNum)); 125 | return 0; 126 | } 127 | 128 | RC RM_FileHandle::UpdateRec(const RM_Record &rec) { 129 | if (recordSize == 0) return RM_FILE_NOT_OPENED; 130 | PageNum pageNum; 131 | SlotNum slotNum; 132 | PF_PageHandle pageHandle; 133 | char *data; 134 | TRY(rec.rid.GetPageNum(pageNum)); 135 | TRY(rec.rid.GetSlotNum(slotNum)); 136 | if (slotNum >= recordsPerPage || slotNum < 0) 137 | return RM_SLOTNUM_OUT_OF_RANGE; 138 | TRY(pfHandle.GetThisPage(pageNum, pageHandle)); 139 | TRY(pageHandle.GetData(data)); 140 | 141 | memcpy(data + pageHeaderSize + recordSize * slotNum, rec.pData, (size_t)recordSize); 142 | for (int i = 0; i < nullableNum; ++i) { 143 | setBitMap(((RM_PageHeader *)data)->bitmap, 144 | recordsPerPage + slotNum * nullableNum + i, rec.isnull[i]); 145 | } 146 | 147 | TRY(pfHandle.MarkDirty(pageNum)); 148 | TRY(pfHandle.UnpinPage(pageNum)); 149 | return 0; 150 | } 151 | 152 | RC RM_FileHandle::ForcePages(PageNum pageNum) { 153 | return pfHandle.ForcePages(pageNum); 154 | } 155 | -------------------------------------------------------------------------------- /src/rm_filescan.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/10/17. 3 | // 4 | 5 | #include "rm.h" 6 | #include "rm_internal.h" 7 | 8 | #include 9 | 10 | RM_FileScan::RM_FileScan() { 11 | scanOpened = false; 12 | } 13 | 14 | RM_FileScan::~RM_FileScan() {} 15 | 16 | RC RM_FileScan::OpenScan(const RM_FileHandle &fileHandle, AttrType attrType, int attrLength, int attrOffset, CompOp compOp, void *value, ClientHint pinHint) { 17 | if (scanOpened) return RM_SCAN_NOT_CLOSED; 18 | 19 | this->fileHandle = &fileHandle; 20 | this->attrType = attrType; 21 | this->attrLength = attrLength; 22 | this->attrOffset = attrOffset; 23 | this->compOp = compOp; 24 | if (value == NULL) { 25 | this->value.stringVal = NULL; 26 | } else { 27 | switch (attrType) { 28 | case INT: 29 | this->value.intVal = *(int *)value; 30 | break; 31 | case FLOAT: 32 | this->value.floatVal = *(float *)value; 33 | break; 34 | case STRING: 35 | this->value.stringVal = new char[attrLength + 1]; 36 | memcpy(this->value.stringVal, value, (size_t)attrLength); 37 | // strncpy(this->value.stringVal, (char*)value, attrLength); 38 | this->value.stringVal[attrLength] = '\0'; 39 | break; 40 | } 41 | } 42 | 43 | RM_FileHeader *fileHeader; 44 | PF_PageHandle pageHandle; 45 | scanOpened = true; 46 | TRY(fileHandle.pfHandle.GetFirstPage(pageHandle)); 47 | TRY(pageHandle.GetData(CVOID(fileHeader))); 48 | recordSize = fileHeader->recordSize; 49 | nullableIndex = -1; 50 | int nullableNum = fileHeader->nullableNum; 51 | for (int i = 0; i < nullableNum; ++i) { 52 | if (fileHeader->nullableOffsets[i] == attrOffset) { 53 | VLOG(2) << "nullableIndex = " << i; 54 | nullableIndex = i; 55 | break; 56 | } 57 | } 58 | if (nullableIndex == -1) { 59 | VLOG(3) << "given offset was not found to be a nullable field."; 60 | } 61 | currentPageNum = 1; 62 | currentSlotNum = 0; 63 | TRY(fileHandle.pfHandle.UnpinPage(0)); 64 | 65 | return 0; 66 | } 67 | 68 | RC RM_FileScan::GetNextRec(RM_Record &rec) { 69 | if (!scanOpened) return RM_SCAN_NOT_OPENED; 70 | 71 | char *data; 72 | PF_PageHandle pageHandle; 73 | bool found = false; 74 | TRY(fileHandle->pfHandle.GetThisPage(currentPageNum, pageHandle)); 75 | while (true) { 76 | TRY(pageHandle.GetData(data)); 77 | int cnt = ((RM_PageHeader *)data)->allocatedRecords; 78 | unsigned char *bitMap = ((RM_PageHeader *)data)->bitmap; 79 | for (; currentSlotNum < cnt; ++currentSlotNum) { 80 | if (getBitMap(bitMap, currentSlotNum) == 0) continue; 81 | char *pData = data + fileHandle->pageHeaderSize + recordSize * currentSlotNum; 82 | bool isnull = false; 83 | if (nullableIndex != -1) { 84 | isnull = getBitMap(((RM_PageHeader *)data)->bitmap, 85 | fileHandle->recordsPerPage + 86 | currentSlotNum * fileHandle->nullableNum + nullableIndex); 87 | } 88 | if (checkSatisfy(pData, isnull)) { 89 | found = true; 90 | break; 91 | } 92 | } 93 | TRY(fileHandle->pfHandle.UnpinPage(currentPageNum)); 94 | if (found) break; 95 | int rc = fileHandle->pfHandle.GetNextPage(currentPageNum, pageHandle); 96 | if (rc == PF_EOF) return RM_EOF; 97 | else if (rc != 0) return rc; 98 | TRY(pageHandle.GetPageNum(currentPageNum)); 99 | currentSlotNum = 0; 100 | } 101 | 102 | TRY(fileHandle->GetRec(RID(currentPageNum, currentSlotNum), rec)); 103 | ++currentSlotNum; 104 | return 0; 105 | } 106 | 107 | RC RM_FileScan::CloseScan() { 108 | if (!scanOpened) return RM_SCAN_NOT_OPENED; 109 | scanOpened = false; 110 | if (attrType == STRING && value.stringVal != NULL) 111 | delete[] value.stringVal; 112 | return 0; 113 | } 114 | 115 | bool RM_FileScan::checkSatisfy(char *data, bool isnull) { 116 | if (compOp == NO_OP) return true; 117 | if (compOp == ISNULL_OP) { 118 | return isnull; 119 | } 120 | if (compOp == NOTNULL_OP) { 121 | return !isnull; 122 | } 123 | if (isnull) return false; 124 | switch (attrType) { 125 | case INT: { 126 | int curVal = *(int *)(data + attrOffset); 127 | switch (compOp) { 128 | case NO_OP: 129 | return true; 130 | case EQ_OP: 131 | return curVal == value.intVal; 132 | case NE_OP: 133 | return curVal != value.intVal; 134 | case LT_OP: 135 | return curVal < value.intVal; 136 | case GT_OP: 137 | return curVal > value.intVal; 138 | case LE_OP: 139 | return curVal <= value.intVal; 140 | case GE_OP: 141 | return curVal >= value.intVal; 142 | default: 143 | CHECK(false); 144 | } 145 | } 146 | case FLOAT: { 147 | float curVal = *(float *)(data + attrOffset); 148 | switch (compOp) { 149 | case NO_OP: 150 | return true; 151 | case EQ_OP: 152 | return curVal == value.floatVal; 153 | case NE_OP: 154 | return curVal != value.floatVal; 155 | case LT_OP: 156 | return curVal < value.floatVal; 157 | case GT_OP: 158 | return curVal > value.floatVal; 159 | case LE_OP: 160 | return curVal <= value.floatVal; 161 | case GE_OP: 162 | return curVal >= value.floatVal; 163 | default: 164 | CHECK(false); 165 | } 166 | } 167 | case STRING: { 168 | char *curVal = data + attrOffset; 169 | switch (compOp) { 170 | case NO_OP: 171 | return true; 172 | case EQ_OP: 173 | return strcmp(curVal, value.stringVal) == 0; 174 | case NE_OP: 175 | return strcmp(curVal, value.stringVal) != 0; 176 | case LT_OP: 177 | return strcmp(curVal, value.stringVal) < 0; 178 | case GT_OP: 179 | return strcmp(curVal, value.stringVal) > 0; 180 | case LE_OP: 181 | return strcmp(curVal, value.stringVal) <= 0; 182 | case GE_OP: 183 | return strcmp(curVal, value.stringVal) >= 0; 184 | default: 185 | CHECK(false); 186 | } 187 | } 188 | } 189 | assert(0); 190 | return false; 191 | } 192 | -------------------------------------------------------------------------------- /src/rm_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/10/16. 3 | // 4 | 5 | #ifndef RM_INTERNAL_H 6 | #define RM_INTERNAL_H 7 | 8 | static const int kLastFreePage = -1; 9 | static const int kLastFreeRecord = -2; 10 | 11 | struct RM_PageHeader { 12 | short firstFreeRecord; 13 | short allocatedRecords; 14 | int nextFreePage; 15 | unsigned char bitmap[1]; 16 | }; 17 | 18 | struct RM_FileHeader { 19 | short recordSize; 20 | short recordsPerPage; 21 | short nullableNum; 22 | int firstFreePage; 23 | short nullableOffsets[1]; 24 | }; 25 | 26 | inline bool getBitMap(unsigned char *bitMap, int pos) { 27 | return (bool)(bitMap[pos >> 3] >> (pos & 0x7) & 1); 28 | } 29 | 30 | inline void setBitMap(unsigned char *bitMap, int pos, bool value) { 31 | if (value) { 32 | bitMap[pos >> 3] |= (unsigned char)(1 << (pos & 0x7)); 33 | } else { 34 | bitMap[pos >> 3] &= (unsigned char)~(1 << (pos & 0x7)); 35 | } 36 | } 37 | 38 | #endif //REBASE_RM_INTERNAL_H 39 | -------------------------------------------------------------------------------- /src/rm_manager.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/10/16. 3 | // 4 | 5 | #include "pf.h" 6 | #include "rm.h" 7 | #include "rm_internal.h" 8 | 9 | #include 10 | 11 | /* RM Manager */ 12 | RM_Manager::RM_Manager(PF_Manager &pfm) { 13 | this->pfm = &pfm; 14 | } 15 | 16 | RM_Manager::~RM_Manager() {} 17 | 18 | RC RM_Manager::CreateFile(const char *fileName, int recordSize, 19 | short nullableNum, short *nullableOffsets) { 20 | if (recordSize > PF_PAGE_SIZE) { 21 | return RM_RECORDSIZE_TOO_LARGE; 22 | } 23 | if (sizeof(RM_PageHeader) + nullableNum * sizeof(short) > PF_PAGE_SIZE) { 24 | return RM_RECORDSIZE_TOO_LARGE; 25 | } 26 | pfm->CreateFile(fileName); 27 | // initialize header 28 | PF_FileHandle fileHandle; 29 | PF_PageHandle pageHandle; 30 | RM_FileHeader *fileHeader; 31 | RM_PageHeader *pageHeader; 32 | TRY(pfm->OpenFile(fileName, fileHandle)); 33 | TRY(fileHandle.AllocatePage(pageHandle)); 34 | TRY(pageHandle.GetData(CVOID(fileHeader))); 35 | 36 | // total size = sizeof PageHeader + bitmap[ = records * (1 + nullable)] + 37 | // records * recordSize 38 | short recordsPerPage = (PF_PAGE_SIZE - sizeof(RM_PageHeader)) / 39 | (recordSize + nullableNum + 1); 40 | if (upper_align<4>(recordsPerPage * (nullableNum + 1)) + 41 | sizeof(RM_PageHeader) + recordSize * recordsPerPage > PF_PAGE_SIZE) 42 | --recordsPerPage; 43 | fileHeader->recordSize = (short)recordSize; 44 | fileHeader->recordsPerPage = recordsPerPage; 45 | fileHeader->nullableNum = nullableNum; 46 | fileHeader->firstFreePage = kLastFreePage; 47 | for (int i = 0; i < nullableNum; ++i) { 48 | fileHeader->nullableOffsets[i] = nullableOffsets[i]; 49 | } 50 | 51 | TRY(fileHandle.MarkDirty(0)); 52 | TRY(fileHandle.UnpinPage(0)); 53 | 54 | TRY(fileHandle.AllocatePage(pageHandle)); 55 | TRY(pageHandle.GetData(CVOID(pageHeader))); 56 | 57 | *pageHeader = {kLastFreeRecord, 0, kLastFreePage}; 58 | memset(pageHeader + offsetof(RM_PageHeader, bitmap), 0, 59 | (size_t)(recordsPerPage * (nullableNum + 1))); 60 | 61 | TRY(fileHandle.MarkDirty(1)); 62 | TRY(fileHandle.UnpinPage(1)); 63 | 64 | TRY(pfm->CloseFile(fileHandle)); 65 | return 0; 66 | } 67 | 68 | // must ensure file is not open 69 | RC RM_Manager::DestroyFile(const char *fileName) { 70 | return pfm->DestroyFile(fileName); 71 | } 72 | 73 | RC RM_Manager::OpenFile(const char *fileName, RM_FileHandle &fileHandle) { 74 | PF_FileHandle pfHandle; 75 | TRY(pfm->OpenFile(fileName, pfHandle)); 76 | fileHandle.pfHandle = pfHandle; 77 | PF_PageHandle pageHandle; 78 | RM_FileHeader *data; 79 | TRY(pfHandle.GetFirstPage(pageHandle)); 80 | TRY(pageHandle.GetData(CVOID(data))); 81 | 82 | fileHandle.recordSize = data->recordSize; 83 | fileHandle.recordsPerPage = data->recordsPerPage; 84 | fileHandle.nullableNum = data->nullableNum; 85 | if (data->nullableNum < 0) { 86 | return RM_BAD_NULLABLE_NUM; 87 | } else if (data->nullableNum > 0) { 88 | fileHandle.nullableOffsets = new short[data->nullableNum]; 89 | } else { 90 | fileHandle.nullableOffsets = NULL; 91 | } 92 | for (int i = 0; i < data->nullableNum; ++i) { 93 | fileHandle.nullableOffsets[i] = data->nullableOffsets[i]; 94 | } 95 | fileHandle.firstFreePage = data->firstFreePage; 96 | fileHandle.isHeaderDirty = false; 97 | fileHandle.pageHeaderSize = sizeof(RM_PageHeader) + upper_align<4>( 98 | data->recordsPerPage * (1 + data->nullableNum)); 99 | 100 | TRY(pfHandle.UnpinPage(0)); 101 | return 0; 102 | } 103 | 104 | RC RM_Manager::CloseFile(RM_FileHandle &fileHandle) { 105 | if (fileHandle.isHeaderDirty) { 106 | PF_PageHandle pageHandle; 107 | RM_FileHeader *header; 108 | TRY(fileHandle.pfHandle.GetFirstPage(pageHandle)); 109 | TRY(pageHandle.GetData(CVOID(header))); 110 | 111 | header->recordSize = fileHandle.recordSize; 112 | header->recordsPerPage = fileHandle.recordsPerPage; 113 | header->nullableNum = fileHandle.nullableNum; 114 | header->firstFreePage = fileHandle.firstFreePage; 115 | for (int i = 0; i < fileHandle.nullableNum; ++i) { 116 | header->nullableOffsets[i] = fileHandle.nullableOffsets[i]; 117 | } 118 | delete[] fileHandle.nullableOffsets; 119 | 120 | TRY(fileHandle.pfHandle.MarkDirty(0)); 121 | TRY(fileHandle.pfHandle.UnpinPage(0)); 122 | } 123 | TRY(pfm->CloseFile(fileHandle.pfHandle)); 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/rm_record.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Kanari on 2016/10/17. 3 | // 4 | 5 | #include "rm.h" 6 | 7 | RM_Record::RM_Record() { 8 | pData = NULL; 9 | isnull = NULL; 10 | } 11 | 12 | RM_Record::~RM_Record() { 13 | if (pData != NULL) { 14 | delete[] pData; 15 | } 16 | if (isnull != NULL) { 17 | delete[] isnull; 18 | } 19 | } 20 | 21 | void RM_Record::SetData(char *data, size_t size) { 22 | if (pData != NULL) { 23 | delete[] pData; 24 | } 25 | pData = new char[size]; 26 | memcpy(pData, data, size); 27 | } 28 | 29 | void RM_Record::SetIsnull(bool *isnull, short nullableNum) { 30 | if (this->isnull != NULL) { 31 | delete[] this->isnull; 32 | } 33 | this->isnull = new bool[nullableNum]; 34 | memcpy(this->isnull, isnull, nullableNum * sizeof(bool)); 35 | } 36 | 37 | RC RM_Record::GetData(char *&pData) const { 38 | pData = this->pData; 39 | return pData == NULL ? RM_UNINITIALIZED_RECORD : 0; 40 | } 41 | 42 | RC RM_Record::GetIsnull(bool *&isnull) const { 43 | isnull = this->isnull; 44 | return pData == NULL ? RM_UNINITIALIZED_RECORD : 0; 45 | } 46 | 47 | RC RM_Record::GetRid(RID &rid) const { 48 | rid = this->rid; 49 | return pData == NULL ? RM_UNINITIALIZED_RECORD : 0; 50 | } 51 | -------------------------------------------------------------------------------- /src/rm_rid.cc: -------------------------------------------------------------------------------- 1 | #include "rm.h" 2 | #include "rm_rid.h" 3 | 4 | static const int kUninitializedRidNums = -1; 5 | 6 | RID::RID() { 7 | pageNum = kUninitializedRidNums; 8 | slotNum = kUninitializedRidNums; 9 | } 10 | 11 | RID::RID(PageNum pageNum, SlotNum slotNum) { 12 | this->pageNum = pageNum; 13 | this->slotNum = slotNum; 14 | } 15 | 16 | RID::~RID() {} 17 | 18 | RC RID::GetPageNum(PageNum &pageNum) const { 19 | pageNum = this->pageNum; 20 | return pageNum == kUninitializedRidNums ? RM_UNINITIALIZED_RID : 0; 21 | } 22 | 23 | RC RID::GetSlotNum(SlotNum &slotNum) const { 24 | slotNum = this->slotNum; 25 | return slotNum == kUninitializedRidNums ? RM_UNINITIALIZED_RID : 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/rm_rid.h: -------------------------------------------------------------------------------- 1 | // 2 | // rm_rid.h 3 | // 4 | // The Record Id interface 5 | // 6 | 7 | #ifndef RM_RID_H 8 | #define RM_RID_H 9 | 10 | // We separate the interface of RID from the rest of RM because some 11 | // components will require the use of RID but not the rest of RM. 12 | 13 | #include "redbase.h" 14 | 15 | // 16 | // PageNum: uniquely identifies a page in a file 17 | // 18 | typedef int PageNum; 19 | 20 | // 21 | // SlotNum: uniquely identifies a record in a page 22 | // 23 | typedef int SlotNum; 24 | 25 | // 26 | // RID: Record id interface 27 | // 28 | class RID { 29 | public: 30 | RID(); // Default constructor 31 | RID(PageNum pageNum, SlotNum slotNum); 32 | ~RID(); // Destructor 33 | 34 | RC GetPageNum(PageNum &pageNum) const; // Return page number 35 | RC GetSlotNum(SlotNum &slotNum) const; // Return slot number 36 | 37 | inline bool operator==(const RID& r) const { 38 | return pageNum == r.pageNum && slotNum == r.slotNum; 39 | } 40 | 41 | private: 42 | 43 | PageNum pageNum; 44 | SlotNum slotNum; 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/scan.l: -------------------------------------------------------------------------------- 1 | %{ 2 | /* 3 | * scan.l: lex spec for RQL 4 | * 5 | * Authors: Dallan Quass 6 | * Jan Jannink 7 | * 8 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 9 | */ 10 | 11 | #include 12 | #include "redbase.h" /* parse.h needs the definition of real */ 13 | #include "parser_internal.h" /* y.tab.h needs the definition of NODE */ 14 | #include "y.tab.h" 15 | 16 | static int get_id(char *s); /* defined in scanhelp.c */ 17 | static char *get_qstring(char *qstring, int len); 18 | 19 | %} 20 | letter [A-Za-z] 21 | digit [0-9] 22 | num {digit}+ 23 | s_num [+\-]?{num} 24 | %x comment 25 | %x shell_cmd 26 | %% 27 | "/*" {BEGIN(comment);} 28 | [^*] {/* ignore the text of the comment */} 29 | "*/" {BEGIN(INITIAL);} 30 | \* {/* ignore *'s that aren't part of */} 31 | [ \n\t] {/* ignore spaces, tabs, and newlines */} 32 | {s_num} {sscanf(yytext, "%d", &yylval.ival); 33 | return T_INT;} 34 | {s_num}\.{num} {sscanf(yytext, "%f", &yylval.rval); 35 | return T_REAL;} 36 | {s_num}\.{num}[Ee]{s_num} {sscanf(yytext, "%f", &yylval.rval); 37 | return T_REAL;} 38 | \'([^\'\n]|(\'\'))*\' {yylval.sval = get_qstring(yytext, yyleng); 39 | return T_QSTRING;} 40 | \'([^\'\n]|(\'\'))*\n {printf("newline in string constant\n");} 41 | {letter}({letter}|{digit}|_)* {return get_id(yylval.sval = yytext);} 42 | "<" {return T_LT;} 43 | "<=" {return T_LE;} 44 | ">" {return T_GT;} 45 | ">=" {return T_GE;} 46 | "=" {return T_EQ;} 47 | "!=" {return T_NE;} 48 | "<>" {return T_NE;} 49 | ! {BEGIN(shell_cmd);} 50 | [^\n]* {yylval.sval = yytext; return T_SHELL_CMD;} 51 | \n {BEGIN(INITIAL);} 52 | [*/+\-=<>':;,.|&()] {return yytext[0];} 53 | <> {return T_EOF;} 54 | . {printf("illegal character [%c]\n", yytext[0]);} 55 | %% 56 | #include "scanhelp.h" 57 | -------------------------------------------------------------------------------- /src/scanhelp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * scanhelp.c: help functions for lexer 3 | * 4 | * Authors: Jan Jannink 5 | * Jason McHugh 6 | * 7 | * originally by: Mark McAuliffe, University of Wisconsin - Madison, 1991 8 | * 9 | * 1997 Changes: "print", "buffer", "reset" and "io" added. 10 | * 1998 Changes: "resize", "queryplans", "on" and "off" added. 11 | * 12 | * 13 | * This file is not compiled separately; it is #included into lex.yy.c . 14 | */ 15 | 16 | /* 17 | * size of buffer of strings 18 | */ 19 | #define MAXCHAR 8192 20 | 21 | /* 22 | * buffer for string allocation 23 | */ 24 | static char charpool[MAXCHAR]; 25 | static int charptr = 0; 26 | 27 | static int lower(char *dst, char *src, int max); 28 | static char *mk_string(char *s, int len); 29 | 30 | /* 31 | * string_alloc: returns a pointer to a string of length len if possible 32 | */ 33 | static char *string_alloc(int len) { 34 | char *s; 35 | 36 | if (charptr + len > MAXCHAR) { 37 | fprintf(stderr, "%s:%s out of memory\n", __FILE__, __FUNCTION__); 38 | exit(1); 39 | } 40 | 41 | s = charpool + charptr; 42 | charptr += len; 43 | 44 | return s; 45 | } 46 | 47 | /* 48 | * reset_charptr: releases all memory allocated in preparation for the 49 | * next query. 50 | * 51 | * No return value. 52 | */ 53 | void reset_charptr(void) { 54 | charptr = 0; 55 | } 56 | 57 | /* 58 | * reset_scanner: resets the scanner after a syntax error 59 | * 60 | * No return value. 61 | */ 62 | void reset_scanner(void) { 63 | charptr = 0; 64 | yyrestart(yyin); 65 | } 66 | 67 | /* 68 | * get_id: determines whether s is a reserved word, and returns the 69 | * appropriate token value if it is. Otherwise, it returns the token 70 | * value corresponding to a string. If s is longer than the maximum token 71 | * length (MAXSTRINGLEN) then it returns NOTOKEN, so that the parser will 72 | * flag an error (this is a stupid kludge). 73 | */ 74 | static int get_id(char *s) { 75 | static char string[MAXSTRINGLEN]; 76 | int len; 77 | 78 | if ((len = lower(string, s, MAXSTRINGLEN)) == MAXSTRINGLEN) 79 | return NOTOKEN; 80 | 81 | /* SM layer lexemes */ 82 | 83 | if (!strcmp(string, "create")) 84 | return yylval.ival = RW_CREATE; 85 | if (!strcmp(string, "drop")) 86 | return yylval.ival = RW_DROP; 87 | if (!strcmp(string, "table")) 88 | return yylval.ival = RW_TABLE; 89 | if (!strcmp(string, "index")) 90 | return yylval.ival = RW_INDEX; 91 | if (!strcmp(string, "load")) 92 | return yylval.ival = RW_LOAD; 93 | if (!strcmp(string, "help")) 94 | return yylval.ival = RW_HELP; 95 | if (!strcmp(string, "exit")) 96 | return yylval.ival = RW_EXIT; 97 | if (!strcmp(string, "print")) 98 | return yylval.ival = RW_PRINT; 99 | if (!strcmp(string, "set")) 100 | return yylval.ival = RW_SET; 101 | 102 | if (!strcmp(string, "and")) 103 | return yylval.ival = RW_AND; 104 | 105 | if (!strcmp(string, "into")) 106 | return yylval.ival = RW_INTO; 107 | if (!strcmp(string, "values")) 108 | return yylval.ival = RW_VALUES; 109 | 110 | 111 | /* QL layer lexemes */ 112 | if (!strcmp(string, "select")) 113 | return yylval.ival = RW_SELECT; 114 | if (!strcmp(string, "from")) 115 | return yylval.ival = RW_FROM; 116 | if (!strcmp(string, "where")) 117 | return yylval.ival = RW_WHERE; 118 | if (!strcmp(string, "insert")) 119 | return yylval.ival = RW_INSERT; 120 | if (!strcmp(string, "delete")) 121 | return yylval.ival = RW_DELETE; 122 | if (!strcmp(string, "update")) 123 | return yylval.ival = RW_UPDATE; 124 | 125 | /* IO Statistics lexemes */ 126 | if (!strcmp(string, "reset")) 127 | return yylval.ival = RW_RESET; 128 | if (!strcmp(string, "io")) 129 | return yylval.ival = RW_IO; 130 | 131 | if (!strcmp(string, "resize")) 132 | return yylval.ival = RW_RESIZE; 133 | if (!strcmp(string, "buffer")) 134 | return yylval.ival = RW_BUFFER; 135 | 136 | if (!strcmp(string, "queryplans")) 137 | return yylval.ival = RW_QUERY_PLAN; 138 | if (!strcmp(string, "on")) 139 | return yylval.ival = RW_ON; 140 | if (!strcmp(string, "off")) 141 | return yylval.ival = RW_OFF; 142 | 143 | /* newly added lexems */ 144 | 145 | if (!strcmp(string, "database")) 146 | return yylval.ival = RW_DATABASE; 147 | if (!strcmp(string, "databases")) 148 | return yylval.ival = RW_DATABASES; 149 | if (!strcmp(string, "tables")) 150 | return yylval.ival = RW_TABLES; 151 | if (!strcmp(string, "show")) 152 | return yylval.ival = RW_SHOW; 153 | if (!strcmp(string, "use")) 154 | return yylval.ival = RW_USE; 155 | if (!strcmp(string, "primary")) 156 | return yylval.ival = RW_PRIMARY; 157 | if (!strcmp(string, "key")) 158 | return yylval.ival = RW_KEY; 159 | if (!strcmp(string, "not")) 160 | return yylval.ival = RW_NOT; 161 | if (!strcmp(string, "null")) 162 | return yylval.ival = RW_NULL; 163 | if (!strcmp(string, "is")) 164 | return yylval.ival = RW_IS; 165 | if (!strcmp(string, "desc")) 166 | return yylval.ival = RW_DESC; 167 | 168 | 169 | /* unresolved lexemes are strings */ 170 | 171 | yylval.sval = mk_string(s, len); 172 | return T_STRING; 173 | } 174 | 175 | /* 176 | * lower: copies src to dst, converting it to lowercase, stopping at the 177 | * end of src or after max characters. 178 | * 179 | * Returns: 180 | * the length of dst (which may be less than the length of src, if 181 | * src is too long). 182 | */ 183 | static int lower(char *dst, char *src, int max) { 184 | int len; 185 | 186 | for (len = 0; len < max && src[len] != '\0'; ++len) { 187 | dst[len] = src[len]; 188 | if (src[len] >= 'A' && src[len] <= 'Z') 189 | dst[len] += 'a' - 'A'; 190 | } 191 | dst[len] = '\0'; 192 | 193 | return len; 194 | } 195 | 196 | /* 197 | * get_qstring: removes the quotes from a quoted string, allocates 198 | * space for the resulting string. 199 | * 200 | * Returns: 201 | * a pointer to the new string 202 | */ 203 | static char *get_qstring(char *qstring, int len) { 204 | /* replace ending quote with \0 */ 205 | qstring[len - 1] = '\0'; 206 | 207 | /* copy everything following beginning quote */ 208 | return mk_string(qstring + 1, len - 2); 209 | } 210 | 211 | /* 212 | * mk_string: allocates space for a string of length len and copies s into 213 | * it. 214 | * 215 | * Returns: 216 | * a pointer to the new string 217 | */ 218 | static char *mk_string(char *s, int len) { 219 | char *copy; 220 | 221 | /* allocate space for new string */ 222 | if ((copy = string_alloc(len + 1)) == NULL) { 223 | printf("out of string space\n"); 224 | exit(1); 225 | } 226 | 227 | /* copy the string */ 228 | strncpy(copy, s, len + 1); 229 | return copy; 230 | } 231 | -------------------------------------------------------------------------------- /src/sm.h: -------------------------------------------------------------------------------- 1 | // 2 | // sm.h 3 | // Data Manager Component Interface 4 | // 5 | 6 | #ifndef SM_H 7 | #define SM_H 8 | 9 | // Please do not include any other files than the ones below in this file. 10 | 11 | #include 12 | #include 13 | #include "redbase.h" // Please don't change these lines 14 | #include "rm.h" 15 | #include "ix.h" 16 | #include 17 | #include 18 | #include "parser.h" 19 | #include "catalog.h" 20 | #include "printer.h" 21 | 22 | // 23 | // SM_Manager: provides data management 24 | // 25 | class SM_Manager { 26 | friend class QL_Manager; 27 | 28 | RM_Manager *rmm; 29 | IX_Manager *ixm; 30 | 31 | RM_FileHandle relcat, attrcat; 32 | public: 33 | SM_Manager (IX_Manager &ixm_, RM_Manager &rmm_); 34 | ~SM_Manager (); // Destructor 35 | 36 | RC OpenDb (const char *dbName); // Open the database 37 | RC CloseDb (); // close the database 38 | 39 | RC CreateTable(const char *relName, // create relation relName 40 | int attrCount, // number of attributes 41 | AttrInfo *attributes); // attribute data 42 | RC DropTable (const char *relName); // destroy a relation 43 | 44 | RC CreateIndex(const char *relName, // create an index for 45 | const char *attrName); // relName.attrName 46 | RC DropIndex (const char *relName, // destroy index on 47 | const char *attrName); // relName.attrName 48 | 49 | RC Load (const char *relName, // load relName from 50 | const char *fileName); // fileName 51 | 52 | RC Help (); // Print relations in db 53 | RC Help (const char *relName); // print schema of relName 54 | 55 | RC Print (const char *relName); // print relName contents 56 | 57 | RC Set (const char *paramName, // set parameter to 58 | const char *value); // value 59 | 60 | RC GetRelEntry(const char *relName, RelCatEntry &relEntry); 61 | RC GetAttrEntry(const char *relName, const char *attrName, AttrCatEntry &attrEntry); 62 | RC GetDataAttrInfo(const char *relName, int &attrCount, std::vector &attributes, bool sort = false); 63 | RC UpdateRelEntry(const char *relName, const RelCatEntry &relEntry); 64 | RC UpdateAttrEntry(const char *relName, const char *attrName, const AttrCatEntry &attrEntry); 65 | private: 66 | RC GetRelCatEntry(const char *relName, RM_Record &rec); 67 | RC GetAttrCatEntry(const char *relName, const char *attrName, RM_Record &rec); 68 | }; 69 | 70 | // 71 | // Print-error function 72 | // 73 | void SM_PrintError(RC rc); 74 | 75 | 76 | #define SM_REL_EXISTS (START_SM_WARN + 0) 77 | #define SM_REL_NOTEXIST (START_SM_WARN + 1) 78 | #define SM_ATTR_NOTEXIST (START_SM_WARN + 2) 79 | #define SM_INDEX_EXISTS (START_SM_WARN + 3) 80 | #define SM_INDEX_NOTEXIST (START_SM_WARN + 4) 81 | #define SM_FILE_FORMAT_INCORRECT (START_SM_WARN + 5) 82 | #define SM_FILE_NOT_FOUND (START_SM_WARN + 6) 83 | #define SM_LASTWARN SM_FILE_NOT_FOUND 84 | 85 | 86 | #define SM_CHDIR_FAILED (START_SM_ERR - 0) 87 | #define SM_CATALOG_CORRUPT (START_SM_ERR - 1) 88 | #define SM_LASTERROR SM_CATALOG_CORRUPT 89 | 90 | #endif // SM_H 91 | -------------------------------------------------------------------------------- /src/sm_error.cc: -------------------------------------------------------------------------------- 1 | // 2 | // File: sm_error.cc 3 | // Description: SM_PrintError function 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include "sm.h" 10 | 11 | using namespace std; 12 | 13 | // 14 | // Error table 15 | // 16 | static const char *SM_WarnMsg[] = { 17 | "relation already exists", 18 | "relation does not exist", 19 | "attribute does not exist for given relation", 20 | "index already exists for given attribute", 21 | "index does not exist for given attribute", 22 | "file to load has incorrect format", 23 | "file not found", 24 | "length of string-typed attribute should not exceed MAXSTRINGLEN=255" 25 | }; 26 | 27 | static const char *SM_ErrorMsg[] = { 28 | "chdir command execution failed", 29 | "database catalog file is corrupt", 30 | }; 31 | 32 | // 33 | // SM_PrintError 34 | // 35 | // Desc: Send a message corresponding to a SM return code to cerr 36 | // In: rc - return code for which a message is desired 37 | // 38 | void SM_PrintError(RC rc) { 39 | // Check the return code is within proper limits 40 | if (rc >= START_SM_WARN && rc <= SM_LASTWARN) 41 | // Print warning 42 | cerr << "SM warning: " << SM_WarnMsg[rc - START_SM_WARN] << "\n"; 43 | // Error codes are negative, so invert everything 44 | else if ((-rc >= -START_SM_ERR) && -rc <= -SM_LASTERROR) { 45 | // Print error 46 | cerr << "SM error: " << SM_ErrorMsg[-rc + START_SM_ERR] << "\n"; 47 | } else if (rc == 0) 48 | cerr << "SM_PrintError called with return code of 0\n"; 49 | else { 50 | // Print error 51 | cerr << "rc was " << rc << endl; 52 | cerr << "START_SM_ERR was " << START_SM_ERR << endl; 53 | cerr << "SM_LASTERROR was " << SM_LASTERROR << endl; 54 | cerr << "SM error: " << rc << " is out of bounds\n"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/statistics.cc: -------------------------------------------------------------------------------- 1 | // 2 | // statistics.cc 3 | // 4 | 5 | // This file holds the implementation for the StatisticsMgr class. 6 | // 7 | // The class is designed to dynamically track statistics for the client. 8 | // You can add any statistic that you would like to track via a call to 9 | // StatisticsMgr::Register. 10 | 11 | // There is no need to setup in advance which statistics that you want to 12 | // track. The call to Register is sufficient. 13 | 14 | // This is essentially a (poor-man's) simplified version of gprof. 15 | 16 | // Andre Bergholz, who was the TA for the 2000 offering has written 17 | // some (or maybe all) of this code. 18 | 19 | #include 20 | #include 21 | #include "statistics.h" 22 | 23 | using namespace std; 24 | 25 | // 26 | // Here are Statistics Keys utilized by the PF layer of the Redbase 27 | // project. 28 | // 29 | const char *PF_GETPAGE = "GETPAGE"; 30 | const char *PF_PAGEFOUND = "PAGEFOUND"; 31 | const char *PF_PAGENOTFOUND = "PAGENOTFOUND"; 32 | const char *PF_READPAGE = "READPAGE"; // IO 33 | const char *PF_WRITEPAGE = "WRITEPAGE"; // IO 34 | const char *PF_FLUSHPAGES = "FLUSHPAGES"; 35 | 36 | // 37 | // Statistic class 38 | // 39 | // This class will track a single statistic 40 | 41 | // 42 | // Default Constructor utilized by the templates 43 | // 44 | Statistic::Statistic() { 45 | psKey = NULL; 46 | iValue = 0; 47 | } 48 | 49 | 50 | // 51 | // Constructor utilized by the StatisticMgr class 52 | // 53 | // We are assured by the StatisticMgr that psKey_ is not a NULL pointer. 54 | // 55 | Statistic::Statistic(const char *psKey_) { 56 | psKey = new char[strlen(psKey_) + 1]; 57 | strcpy (psKey, psKey_); 58 | 59 | iValue = 0; 60 | } 61 | 62 | // 63 | // Copy constructor 64 | // 65 | Statistic::Statistic(const Statistic &stat) { 66 | psKey = new char[strlen(stat.psKey) + 1]; 67 | strcpy (psKey, stat.psKey); 68 | 69 | iValue = stat.iValue; 70 | } 71 | 72 | // 73 | // Equality constructor 74 | // 75 | Statistic& Statistic::operator=(const Statistic &stat) { 76 | if (this == &stat) 77 | return *this; 78 | 79 | delete [] psKey; 80 | psKey = new char[strlen(stat.psKey) + 1]; 81 | strcpy (psKey, stat.psKey); 82 | 83 | iValue = stat.iValue; 84 | 85 | return *this; 86 | } 87 | 88 | // 89 | // Destructor 90 | // 91 | Statistic::~Statistic() { 92 | delete [] psKey; 93 | } 94 | 95 | Boolean Statistic::operator==(const char *psKey_) const { 96 | return (strcmp(psKey_, psKey) == 0); 97 | } 98 | 99 | // -------------------------------------------------------------- 100 | 101 | // 102 | // StatisticMgr class 103 | // 104 | // This class will track a dynamic list of statistics. 105 | // 106 | 107 | // 108 | // Register 109 | // 110 | // Register a change to a statistic. The psKey is the char* name of 111 | // the statistic to be tracked. This method will look for the statistic 112 | // name withing its list of statistics and perform the operation over the 113 | // stored value. The piValue is utilized for some of the operations. 114 | // 115 | // Note: if the statistic isn't found (as it will not be the very first 116 | // time) then it will be initialized to 0 - the default value. 117 | // 118 | RC StatisticsMgr::Register (const char *psKey, const Stat_Operation op, 119 | const int *const piValue) { 120 | int i, iCount; 121 | Statistic *pStat = NULL; 122 | 123 | if (psKey == NULL || (op != STAT_ADDONE && piValue == NULL)) 124 | return STAT_INVALID_ARGS; 125 | 126 | iCount = llStats.GetLength(); 127 | 128 | for (i = 0; i < iCount; i++) { 129 | pStat = llStats[i]; 130 | if (*pStat == psKey) 131 | break; 132 | } 133 | 134 | // Check to see if we found the Stat 135 | if (i == iCount) 136 | // We haven't found it so create a new statistic 137 | // with the key psKey and initial value of 0. 138 | pStat = new Statistic( psKey ); 139 | 140 | // Now perform the operation over the statistic 141 | switch (op) { 142 | case STAT_ADDONE: 143 | pStat->iValue++; 144 | break; 145 | case STAT_ADDVALUE: 146 | pStat->iValue += *piValue; 147 | break; 148 | case STAT_SETVALUE: 149 | pStat->iValue = *piValue; 150 | break; 151 | case STAT_MULTVALUE: 152 | pStat->iValue *= *piValue; 153 | break; 154 | case STAT_DIVVALUE: 155 | pStat->iValue = (int) (pStat->iValue / (*piValue)); 156 | break; 157 | case STAT_SUBVALUE: 158 | pStat->iValue -= *piValue; 159 | break; 160 | }; 161 | 162 | // Finally, if the statistic wasn't in the original list then add it to 163 | // the list. 164 | // JASON:: Confirm that it makes a copy of the object in line 229 of 165 | // linkedlist.h. 166 | if (i == iCount) { 167 | llStats.Append(*pStat); 168 | delete pStat; 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | // 175 | // Print 176 | // 177 | // Print out the information pertaining to a specific statistic 178 | RC StatisticsMgr::Print(const char *psKey) { 179 | if (psKey == NULL) 180 | return STAT_INVALID_ARGS; 181 | 182 | int *iValue = Get(psKey); 183 | 184 | if (iValue) 185 | cout << psKey << "::" << *iValue << "\n"; 186 | else 187 | return STAT_UNKNOWN_KEY; 188 | 189 | delete iValue; 190 | 191 | return 0; 192 | } 193 | 194 | 195 | // 196 | // Get 197 | // 198 | // The Get method will return a pointer to the integer value associated 199 | // with a particular statistic. If it cannot find the statistic then it 200 | // will return NULL. The caller must remember to delete the memory 201 | // returned when done. 202 | // 203 | int *StatisticsMgr::Get(const char *psKey) { 204 | int i, iCount; 205 | Statistic *pStat = NULL; 206 | 207 | iCount = llStats.GetLength(); 208 | 209 | for (i = 0; i < iCount; i++) { 210 | pStat = llStats[i]; 211 | if (*pStat == psKey) 212 | break; 213 | } 214 | 215 | // Check to see if we found the Stat 216 | if (i == iCount) 217 | return NULL; 218 | 219 | return new int(pStat->iValue); 220 | } 221 | 222 | // 223 | // Print 224 | // 225 | // Print out all the statistics tracked 226 | // 227 | void StatisticsMgr::Print() { 228 | int i, iCount; 229 | Statistic *pStat = NULL; 230 | 231 | iCount = llStats.GetLength(); 232 | 233 | for (i = 0; i < iCount; i++) { 234 | pStat = llStats[i]; 235 | cout << pStat->psKey << "::" << pStat->iValue << "\n"; 236 | } 237 | } 238 | 239 | // 240 | // Reset 241 | // 242 | // Reset a specific statistic. The easiest way to do this is to remove it 243 | // completely from the list 244 | // 245 | RC StatisticsMgr::Reset(const char *psKey) { 246 | int i, iCount; 247 | Statistic *pStat = NULL; 248 | 249 | if (psKey == NULL) 250 | return STAT_INVALID_ARGS; 251 | 252 | iCount = llStats.GetLength(); 253 | 254 | for (i = 0; i < iCount; i++) { 255 | pStat = llStats[i]; 256 | if (*pStat == psKey) 257 | break; 258 | } 259 | 260 | // If we found the statistic then remove it from the list 261 | if (i != iCount) 262 | llStats.Delete(i); 263 | else 264 | return STAT_UNKNOWN_KEY; 265 | 266 | return 0; 267 | } 268 | 269 | // 270 | // Reset 271 | // 272 | // Reset all of the statistics. The easiest way is to tell the linklist of 273 | // elements to Erase itself. 274 | // 275 | void StatisticsMgr::Reset() { 276 | llStats.Erase(); 277 | } 278 | -------------------------------------------------------------------------------- /src/statistics.h: -------------------------------------------------------------------------------- 1 | // 2 | // statistics.h 3 | // 4 | 5 | // This file contains the interface for the statistics class 6 | // which can be setup to dynamically track statistics within a 7 | // C++ program. 8 | 9 | // This is essentially a (poor-man's) simplified version of gprof. 10 | 11 | // My intent here is to allow the StatisticsMgr to be used with very little 12 | // setup on the part of the client. Notice that you don't need to setup 13 | // all the statistics that you will be tracking. You just Register the 14 | // statistic as you go. In the end the Print or Get methods will allow you 15 | // to report all the statistics. 16 | 17 | // Andre Bergholz, who was the TA for the 2000 offering, has written 18 | // some (or probably all) of this code. 19 | 20 | #ifndef STATISTICS_H 21 | #define STATISTICS_H 22 | 23 | // Some common definitions that might not already be set 24 | #ifndef Boolean 25 | typedef char Boolean; 26 | #endif 27 | 28 | #ifndef TRUE 29 | #define TRUE 1 30 | #endif 31 | 32 | #ifndef FALSE 33 | #define FALSE 0 34 | #endif 35 | 36 | #ifndef NULL 37 | #define NULL 0 38 | #endif 39 | 40 | #ifndef RC 41 | typedef int RC; 42 | #endif 43 | 44 | #ifndef STAT_BASE 45 | const int STAT_BASE = 9000; 46 | #endif 47 | 48 | // This include must come after the common defines 49 | #include "linkedlist.h" // Template class for the link list 50 | 51 | // A single statistic will be tracked by a Statistic class 52 | class Statistic { 53 | public: 54 | Statistic(); 55 | ~Statistic(); 56 | Statistic(const char *psName); 57 | 58 | // Copy constructor 59 | Statistic(const Statistic &stat); 60 | 61 | // Equality constructor 62 | Statistic& operator=(const Statistic &stat); 63 | 64 | // Check for equality between a Statistic and a name based upon the 65 | // names given to the current statistic. 66 | Boolean operator==(const char *psName_) const; 67 | 68 | // The name or key given to the statistic that this structure is 69 | // tracking 70 | char *psKey; 71 | 72 | // Currently, I have only allowed the statistic to track integer values. 73 | // Initial value will be 0. 74 | int iValue; 75 | }; 76 | 77 | // These are the different operations that a single statistic can undergo 78 | // during a call to StatisticsMgr::Register. 79 | enum Stat_Operation { 80 | STAT_ADDONE, 81 | STAT_ADDVALUE, 82 | STAT_SETVALUE, 83 | STAT_MULTVALUE, 84 | STAT_DIVVALUE, 85 | STAT_SUBVALUE 86 | }; 87 | 88 | // The StatisticsMgr will track a group of statistics 89 | class StatisticsMgr { 90 | 91 | public: 92 | StatisticsMgr() {}; 93 | ~StatisticsMgr() {}; 94 | 95 | // Add a new statistic or register a change to an existing statistic. 96 | // The piValue for can be NULL, except for those operations that require 97 | // it. When adding the default value is 0 with the Stat_Operation being 98 | // performed over the initial value. 99 | RC Register(const char *psKey, const Stat_Operation op, 100 | const int *const piValue = NULL); 101 | 102 | // Get will return the value associated with a particular statistic. 103 | // Caller is responsible for deleting the memory returned. 104 | int *Get(const char *psKey); 105 | 106 | // Print out a specific statistic 107 | RC Print(const char *psKey); 108 | 109 | // Print out all the statistics tracked 110 | void Print(); 111 | 112 | // Reset a specific statistic 113 | RC Reset(const char *psKey); 114 | 115 | // Reset all of the statistics 116 | void Reset(); 117 | 118 | private: 119 | LinkList llStats; 120 | }; 121 | 122 | // 123 | // Return codes 124 | // 125 | const int STAT_INVALID_ARGS = STAT_BASE+1; // Bad Args in call to method 126 | const int STAT_UNKNOWN_KEY = STAT_BASE+2; // No such Key being tracked 127 | 128 | // 129 | // The following are specifically for tracking the statistics in the PF 130 | // component of Redbase. When statistics are utilized, these constants 131 | // will be used as the keys for the statistics manager. 132 | // 133 | extern const char *PF_GETPAGE; 134 | extern const char *PF_PAGEFOUND; 135 | extern const char *PF_PAGENOTFOUND; 136 | extern const char *PF_READPAGE; // IO 137 | extern const char *PF_WRITEPAGE; // IO 138 | extern const char *PF_FLUSHPAGES; 139 | 140 | #endif 141 | 142 | --------------------------------------------------------------------------------