├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── ci.yaml ├── .gitignore ├── .gitmodules ├── .idea ├── .gitignore ├── SamehadaDB.iml ├── codeStyles │ └── codeStyleConfig.xml ├── modules.xml ├── runConfigurations │ ├── go_TestKeyDuplicateSkipListPrallelTxnStrideFloat.xml │ ├── go_TestKeyDuplicateSkipListPrallelTxnStrideVarchar.xml │ ├── go_TestSkipListBench8_2.xml │ ├── go_TestSkipListParallelSimpleInteger3Stride.xml │ ├── go_build_Samehada_Server.xml │ ├── go_test_SamehadaDB__short_.xml │ ├── go_test_TestKeyDuplicateInsertDeleteWithBTreeIndexXXX.xml │ ├── go_test_TestKeyDuplicateInsertDeleteWithSkipListIndexInt.xml │ ├── go_test_TestParallelQueryIssue.xml │ ├── go_test_TestSkipList.xml │ ├── go_test_TestSkipList_LongRun_only.xml │ ├── go_test_TestSkipList__short_.xml │ ├── go_test_blink_tree.xml │ ├── go_test_executors.xml │ ├── go_test_executors__short_.xml │ ├── go_test_index_test.xml │ ├── go_test_lib.xml │ └── go_test_recovery.xml └── vcs.xml ├── LICENSE ├── README.md ├── SamehadaDB_logo.png ├── demo-client ├── index.html ├── jquery-3.7.1.min.js ├── jquery.binarytransport.js └── msgpack.min.js ├── lib ├── catalog │ ├── catalog_interface │ │ └── catalog_interface.go │ ├── catalog_test │ │ └── table_catalog_reload_test.go │ ├── schemas.go │ ├── statistics.go │ ├── table_catalog.go │ └── table_metadata.go ├── common │ ├── assert.go │ ├── config.go │ ├── logger.go │ └── rwlatch.go ├── concurrency │ ├── checkpoint_manager.go │ └── statistics_updater.go ├── container │ ├── btree │ │ ├── btree_test.go │ │ ├── parent_bufmgr_impl.go │ │ └── parent_page_impl.go │ ├── hash │ │ ├── hash_table_page_test.go │ │ ├── hash_table_test.go │ │ ├── hash_util.go │ │ ├── linear_probe_hash_table.go │ │ └── linear_probe_hash_table_iterator.go │ └── skip_list │ │ ├── skip_list.go │ │ ├── skip_list_bench │ │ └── skip_list_bench_test.go │ │ ├── skip_list_iterator.go │ │ └── skip_list_test │ │ └── skip_list_test.go ├── errors │ └── type.go ├── execution │ ├── executors │ │ ├── aggregation_executor.go │ │ ├── delete_executor.go │ │ ├── execution_engine.go │ │ ├── executor.go │ │ ├── executor_context.go │ │ ├── executor_test │ │ │ ├── btree_index_executor_test.go │ │ │ ├── executor_test.go │ │ │ ├── skiplist_index_executor_test.go │ │ │ └── uniq_skiplist_index_executor_test.go │ │ ├── executor_util.go │ │ ├── hash_join_executor.go │ │ ├── index_join_executor.go │ │ ├── insert_executor.go │ │ ├── limit_executor.go │ │ ├── nested_loop_join_executor.go │ │ ├── orderby_executor.go │ │ ├── point_scan_with_index_executor.go │ │ ├── projection_executor.go │ │ ├── range_scan_with_index_executor.go │ │ ├── selection_executor.go │ │ ├── seq_scan_executor.go │ │ └── update_executor.go │ ├── expression │ │ ├── abstract_expression.go │ │ ├── aggregate_value.go │ │ ├── column_value.go │ │ ├── comparison.go │ │ ├── constant_value.go │ │ ├── expression.go │ │ ├── expression_util.go │ │ └── loggical_op.go │ └── plans │ │ ├── aggregation.go │ │ ├── delete.go │ │ ├── hash_join.go │ │ ├── index_join.go │ │ ├── insert.go │ │ ├── limit.go │ │ ├── nested_loop_join.go │ │ ├── orderby.go │ │ ├── plan.go │ │ ├── plan_util.go │ │ ├── point_scan_with_index.go │ │ ├── projection.go │ │ ├── range_scan_with_index.go │ │ ├── selection.go │ │ ├── seq_scan.go │ │ └── update.go ├── go.mod ├── go.sum ├── materialization │ ├── tmp_tuple.go │ └── tmp_tuple_page.go ├── parser │ ├── agg_func_visitor.go │ ├── assign_visitor.go │ ├── binary_op_visitor.go │ ├── child_data_visitor.go │ ├── join_visitor.go │ ├── parser.go │ ├── parser_expressions.go │ ├── parser_test.go │ ├── parser_util.go │ ├── print_nodes_visitor.go │ ├── root_sql_visitor.go │ ├── select_fields_visitor.go │ └── visitor.go ├── planner │ ├── optimizer │ │ ├── optimizer.go │ │ ├── optimizer_test.go │ │ └── selinger_optimizer.go │ ├── planner.go │ └── simple_planner.go ├── recovery │ ├── log_manager.go │ ├── log_record.go │ ├── log_recovery │ │ └── log_recovery.go │ └── recovery_test │ │ └── log_recovery_test.go ├── samehada │ ├── request_manager.go │ ├── samehada.go │ ├── samehada_instance.go │ ├── samehada_test │ │ └── samehada_test.go │ └── samehada_util │ │ ├── samehada_util.go │ │ └── samehada_util_test.go ├── storage │ ├── access │ │ ├── lock_manager.go │ │ ├── table_heap.go │ │ ├── table_heap_iterator.go │ │ ├── table_heap_test.go │ │ ├── table_page.go │ │ ├── transaction.go │ │ └── transaction_manager.go │ ├── buffer │ │ ├── buffer_pool_manager.go │ │ ├── buffer_pool_manager_test.go │ │ ├── circular_list.go │ │ ├── clock_replacer.go │ │ └── clock_replacer_test.go │ ├── disk │ │ ├── disk_manager.go │ │ ├── disk_manager_impl.go │ │ ├── disk_manager_test.go │ │ ├── testing.go │ │ └── virtual_disk_manager_impl.go │ ├── index │ │ ├── btree_index.go │ │ ├── index.go │ │ ├── index_common │ │ │ └── index_common.go │ │ ├── index_constants │ │ │ └── index_constants.go │ │ ├── index_range_scan_iterator.go │ │ ├── index_test │ │ │ ├── btree_index_test.go │ │ │ ├── hash_table_index_test.go │ │ │ └── skip_list_index_test.go │ │ ├── linear_probe_hash_table_index.go │ │ ├── skip_list_index.go │ │ └── uniq_skip_list_index.go │ ├── page │ │ ├── hash_table_block_page.go │ │ ├── hash_table_header_page.go │ │ ├── page.go │ │ ├── page_test.go │ │ ├── rid.go │ │ ├── rid_test.go │ │ └── skip_list_page │ │ │ ├── skip_list_block_page.go │ │ │ └── skip_list_header_page.go │ ├── table │ │ ├── column │ │ │ └── column.go │ │ └── schema │ │ │ └── schema.go │ └── tuple │ │ ├── tuple.go │ │ └── tuple_test.go ├── testing │ ├── testing_assert │ │ └── assert.go │ ├── testing_pattern_fw │ │ └── test_petternize_fw.go │ ├── testing_tbl_gen │ │ └── table_generator.go │ └── testing_util │ │ └── testing_utils.go └── types │ ├── column_type_id.go │ ├── column_value.go │ ├── lsn.go │ ├── page_id.go │ ├── txn_id.go │ └── uint32.go ├── licenses ├── BusTub │ └── LICENSE ├── LICENSE ├── go-bustub │ └── LICENSE └── goostub │ └── LICENSE ├── samehada_logo2.webp ├── server ├── go.mod ├── go.sum ├── main.go └── signal_handle │ └── signal_handler.go └── todo-comment-lister.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.go text 7 | *.md text 8 | LICENSE text 9 | 10 | # Declare files that will always have LF line endings on checkout. 11 | *.mod text eol=lf 12 | *.sum text eol=lf 13 | 14 | # Denote all files that are truly binary and should not be modified. 15 | *.png binary 16 | *.jpg binary 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ryogrid] 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: testing 2 | 3 | on: [push] 4 | 5 | jobs: 6 | # for all tests 7 | setup: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: set up 11 | uses: actions/setup-go@v2 12 | with: 13 | go-version: 1.18 14 | id: go 15 | - name: check out 16 | uses: actions/checkout@v2 17 | 18 | # cache environment 19 | - name: Cache 20 | uses: actions/cache@v4 21 | with: 22 | path: ~/go/pkg/mod 23 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 24 | restore-keys: | 25 | ${{ runner.os }}-go- 26 | 27 | # build testing 28 | build: 29 | needs: setup 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: build 34 | run: cd server; go build -o samehada-db-server main.go 35 | 36 | # exec unit tests 37 | test: 38 | needs: setup 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | - name: change virtual file use flag to false 43 | run: sed -i "/EnableOnMemStorage/c const EnableOnMemStorage = false" lib/common/config.go 44 | - name: test 45 | run: cd lib; go test ./... -v -short 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.xml 2 | *.txt 3 | *.db 4 | *.log 5 | *.pdf 6 | 7 | .vscode/ 8 | cpp_impl/ 9 | execution/pkg/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryogrid/SamehadaDB/443040120976a2935ba89adcc197a644c7b0236b/.gitmodules -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/SamehadaDB.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_TestKeyDuplicateSkipListPrallelTxnStrideFloat.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_TestKeyDuplicateSkipListPrallelTxnStrideVarchar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_TestSkipListBench8_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_TestSkipListParallelSimpleInteger3Stride.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_build_Samehada_Server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_SamehadaDB__short_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_TestKeyDuplicateInsertDeleteWithBTreeIndexXXX.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_TestKeyDuplicateInsertDeleteWithSkipListIndexInt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_TestParallelQueryIssue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_TestSkipList.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_TestSkipList_LongRun_only.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_TestSkipList__short_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_blink_tree.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_executors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_executors__short_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_index_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_lib.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_test_recovery.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 SamehadaDB team members 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SamehadaDB_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryogrid/SamehadaDB/443040120976a2935ba89adcc197a644c7b0236b/SamehadaDB_logo.png -------------------------------------------------------------------------------- /demo-client/jquery.binarytransport.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * jquery.binarytransport.js 4 | * 5 | * @description. jQuery ajax transport for making binary data type requests. 6 | * @version 1.0 7 | * @author Henry Algus 8 | * 9 | */ 10 | 11 | (function($, undefined) { 12 | "use strict"; 13 | 14 | // use this transport for "binary" data type 15 | $.ajaxTransport("+binary", function(options, originalOptions, jqXHR) { 16 | // check for conditions and support for blob / arraybuffer response type 17 | if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob))))) { 18 | return { 19 | // create new XMLHttpRequest 20 | send: function(headers, callback) { 21 | // setup all variables 22 | var xhr = new XMLHttpRequest(), 23 | url = options.url, 24 | type = options.type, 25 | async = options.async !== false, 26 | // blob or arraybuffer. Default is blob 27 | dataType = options.responseType || "blob", 28 | data = options.data || null, 29 | username = options.username || null, 30 | password = options.password || null; 31 | 32 | xhr.addEventListener('load', function() { 33 | var data = {}; 34 | data[options.dataType] = xhr.response; 35 | // make callback and send data 36 | callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders()); 37 | }); 38 | xhr.addEventListener('error', function() { 39 | var data = {}; 40 | data[options.dataType] = xhr.response; 41 | // make callback and send data 42 | callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders()); 43 | }); 44 | 45 | xhr.open(type, url, async, username, password); 46 | 47 | // setup custom headers 48 | for (var i in headers) { 49 | xhr.setRequestHeader(i, headers[i]); 50 | } 51 | 52 | xhr.responseType = dataType; 53 | xhr.send(data); 54 | }, 55 | abort: function() {} 56 | }; 57 | } 58 | }); 59 | })(window.jQuery); 60 | -------------------------------------------------------------------------------- /lib/catalog/catalog_interface/catalog_interface.go: -------------------------------------------------------------------------------- 1 | package catalog_interface 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/storage/index" 5 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 6 | "github.com/ryogrid/SamehadaDB/lib/types" 7 | ) 8 | 9 | type CatalogInterface interface { 10 | GetRollbackNeededIndexes(indexMap map[uint32][]index.Index, oid uint32) []index.Index 11 | GetColValFromTupleForRollback(tuple_ *tuple.Tuple, colIdx uint32, oid uint32) *types.Value 12 | } 13 | -------------------------------------------------------------------------------- /lib/catalog/catalog_test/table_catalog_reload_test.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package catalog_test 5 | 6 | import ( 7 | "fmt" 8 | "github.com/ryogrid/SamehadaDB/lib/catalog" 9 | "github.com/ryogrid/SamehadaDB/lib/common" 10 | "github.com/ryogrid/SamehadaDB/lib/samehada" 11 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 12 | "github.com/ryogrid/SamehadaDB/lib/storage/index/index_constants" 13 | "github.com/ryogrid/SamehadaDB/lib/storage/table/column" 14 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 15 | testingpkg "github.com/ryogrid/SamehadaDB/lib/testing/testing_assert" 16 | "github.com/ryogrid/SamehadaDB/lib/types" 17 | "os" 18 | "testing" 19 | ) 20 | 21 | // test reloading serialized catalog info in db file at lauching system 22 | func TestTableCatalogReload(t *testing.T) { 23 | common.TempSuppressOnMemStorageMutex.Lock() 24 | common.TempSuppressOnMemStorage = true 25 | 26 | if !common.EnableOnMemStorage || common.TempSuppressOnMemStorage { 27 | os.Remove(t.Name() + ".db") 28 | } 29 | samehada_instance := samehada.NewSamehadaInstance(t.Name(), common.BufferPoolMaxFrameNumForTest) 30 | bpm := buffer.NewBufferPoolManager(uint32(32), samehada_instance.GetDiskManager(), samehada_instance.GetLogManager()) 31 | 32 | txn := samehada_instance.GetTransactionManager().Begin(nil) 33 | catalog_old := catalog.BootstrapCatalog(bpm, samehada_instance.GetLogManager(), samehada_instance.GetLockManager(), txn) 34 | 35 | columnA := column.NewColumn("a", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 36 | columnB := column.NewColumn("b", types.Integer, true, index_constants.INDEX_KIND_HASH, types.PageID(-1), nil) 37 | schema_ := schema.NewSchema([]*column.Column{columnA, columnB}) 38 | 39 | catalog_old.CreateTable("test_1", schema_, txn) 40 | bpm.FlushAllPages() 41 | 42 | samehada_instance.CloseFilesForTesting() 43 | 44 | fmt.Println("Shutdown system...") 45 | 46 | samehada_instance_new := samehada.NewSamehadaInstance(t.Name(), common.BufferPoolMaxFrameNumForTest) 47 | txn_new := samehada_instance_new.GetTransactionManager().Begin(nil) 48 | catalog_recov := catalog.RecoveryCatalogFromCatalogPage(samehada_instance_new.GetBufferPoolManager(), samehada_instance_new.GetLogManager(), samehada_instance_new.GetLockManager(), txn_new, true) 49 | 50 | columnToCheck := catalog_recov.GetTableByOID(1).Schema().GetColumn(1) 51 | 52 | testingpkg.Assert(t, columnToCheck.GetColumnName() == "test_1.b", "") 53 | testingpkg.Assert(t, columnToCheck.GetType() == 4, "") 54 | testingpkg.Assert(t, columnToCheck.HasIndex() == true, "") 55 | 56 | common.TempSuppressOnMemStorage = false 57 | common.TempSuppressOnMemStorageMutex.Unlock() 58 | } 59 | -------------------------------------------------------------------------------- /lib/catalog/schemas.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package catalog 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/index/index_constants" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/table/column" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 10 | "github.com/ryogrid/SamehadaDB/lib/types" 11 | ) 12 | 13 | func TableCatalogSchema() *schema.Schema { 14 | oidColumn := column.NewColumn("oid", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 15 | nameColumn := column.NewColumn("name", types.Varchar, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 16 | firstPageColumn := column.NewColumn("first_page", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 17 | return schema.NewSchema([]*column.Column{oidColumn, nameColumn, firstPageColumn}) 18 | } 19 | 20 | func ColumnsCatalogSchema() *schema.Schema { 21 | tableOIDColumn := column.NewColumn("table_oid", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 22 | typeColumn := column.NewColumn("type", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 23 | nameColumn := column.NewColumn("name", types.Varchar, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 24 | fixedLengthColumn := column.NewColumn("fixed_length", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 25 | variableLengthColumn := column.NewColumn("variable_length", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 26 | offsetColumn := column.NewColumn("offset", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 27 | hasIndexColumn := column.NewColumn("has_index", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 28 | indexKind := column.NewColumn("index_kind", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 29 | indexHeaderPageId := column.NewColumn("index_header_page_id", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 30 | 31 | return schema.NewSchema([]*column.Column{ 32 | tableOIDColumn, 33 | typeColumn, 34 | nameColumn, 35 | fixedLengthColumn, 36 | variableLengthColumn, 37 | offsetColumn, 38 | hasIndexColumn, 39 | indexKind, 40 | indexHeaderPageId}) 41 | } 42 | -------------------------------------------------------------------------------- /lib/common/assert.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/devlights/gomy/output" 5 | "runtime" 6 | "sync" 7 | ) 8 | 9 | func SH_Assert(condition bool, msg string) { 10 | if !condition { 11 | panic(msg) 12 | } 13 | } 14 | 15 | type SH_Mutex struct { 16 | mutex *sync.Mutex 17 | isLocked bool 18 | } 19 | 20 | func NewSH_Mutex() *SH_Mutex { 21 | return &SH_Mutex{new(sync.Mutex), false} 22 | } 23 | func (m *SH_Mutex) Lock() { 24 | SH_Assert(!m.isLocked, "Mutex is already locked") 25 | m.mutex.Lock() 26 | m.isLocked = true 27 | } 28 | 29 | func (m *SH_Mutex) Unlock() { 30 | SH_Assert(m.isLocked, "Mutex is not locked") 31 | m.mutex.Unlock() 32 | m.isLocked = false 33 | } 34 | 35 | // REFERENCES 36 | // - https://pkg.go.dev/runtime#Stack 37 | // - https://stackoverflow.com/questions/19094099/how-to-dump-goroutine-stacktraces 38 | func RuntimeStack() error { 39 | // channels 40 | var ( 41 | chAll = make(chan []byte, 1) 42 | ) 43 | 44 | // funcs 45 | var ( 46 | getStack = func(all bool) []byte { 47 | // From src/runtime/debug/stack.go 48 | var ( 49 | buf = make([]byte, 1024) 50 | ) 51 | 52 | for { 53 | n := runtime.Stack(buf, all) 54 | if n < len(buf) { 55 | return buf[:n] 56 | } 57 | buf = make([]byte, 2*len(buf)) 58 | } 59 | } 60 | ) 61 | 62 | // all goroutin 63 | go func(ch chan<- []byte) { 64 | defer close(ch) 65 | ch <- getStack(true) 66 | }(chAll) 67 | 68 | // result of runtime.Stack(true) 69 | for v := range chAll { 70 | output.Stdoutl("=== stack-all ", string(v)) 71 | } 72 | 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /lib/common/config.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/pzhzqt/goostub 2 | // there is license and copyright notice in licenses/goostub dir 3 | 4 | package common 5 | 6 | import ( 7 | "sync" 8 | "time" 9 | ) 10 | 11 | var LogTimeout time.Duration 12 | 13 | const EnableDebug bool = false //true 14 | 15 | // use on memory virtual storage or not 16 | const EnableOnMemStorage = true 17 | 18 | // when this is true, virtual storage use is suppressed 19 | // for test case which can't work with virtual storage 20 | var TempSuppressOnMemStorage = false 21 | var TempSuppressOnMemStorageMutex sync.Mutex 22 | 23 | // TODO: for debugging 24 | var NewRIDAtNormal = false 25 | var NewRIDAtRollback = false 26 | 27 | const ( 28 | // invalid page id 29 | InvalidPageID = -1 30 | // invalid transaction id 31 | InvalidTxnID = -1 32 | // invalid log sequence number 33 | InvalidLSN = -1 34 | // the header page id 35 | HeaderPageID = 0 36 | // size of a data page in byte 37 | PageSize = 4096 //4096 //1024 //512 38 | BufferPoolMaxFrameNumForTest = 32 39 | // number for calculate log buffer size (number of page size) 40 | LogBufferSizeBase = 128 41 | // size of a log buffer in byte 42 | LogBufferSize = (LogBufferSizeBase + 1) * PageSize 43 | // size of hash bucket 44 | BucketSizeOfHashIndex = 10 45 | // probability used for determin node level on SkipList 46 | SkipListProb = 0.5 //0.25 47 | ActiveLogKindSetting = INFO | NOT_ABORABLE_TXN_FEATURE //| COMMIT_ABORT_HANDLE_INFO | NOT_ABORABLE_TXN_FEATURE | DEBUGGING | RDB_OP_FUNC_CALL // | DEBUG_INFO //| BUFFER_INTERNAL_STATE //| DEBUGGING | DEBUG_INFO //| PIN_COUNT_ASSERT //DEBUG_INFO_DETAIL //DEBUGGING 48 | KernelThreadNum = 24 49 | MaxTxnThreadNum = KernelThreadNum * 1 50 | ) 51 | 52 | type TxnID int32 // transaction id type 53 | type SlotOffset uintptr // slot offset type 54 | -------------------------------------------------------------------------------- /lib/common/logger.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "fmt" 4 | 5 | type LogLevel int32 6 | 7 | const ( 8 | DEBUG_INFO_DETAIL LogLevel = 1 9 | DEBUG_INFO = 2 10 | CACHE_OUT_IN_INFO = 2 << 1 11 | RDB_OP_FUNC_CALL = 2 << 2 // print several info at core functions (ex: CRUD at TableHeap and SkipList/SkipListBlockPage) 12 | BUFFER_INTERNAL_STATE = 2 << 3 // print internal state of buffer of BufferPoolManager 13 | PIN_COUNT_ASSERT = 2 << 4 14 | COMMIT_ABORT_HANDLE_INFO = 2 << 5 15 | NOT_ABORABLE_TXN_FEATURE = 2 << 6 16 | DEBUGGING = 2 << 7 // print debug info for a debugging period (not permanently used) 17 | INFO = 2 << 8 18 | WARN = 2 << 9 19 | ERROR = 2 << 10 20 | FATAL = 2 << 11 21 | ) 22 | 23 | func ShPrintf(logLevel LogLevel, fmtStl string, a ...interface{}) { 24 | if logLevel&ActiveLogKindSetting > 0 { 25 | fmt.Printf(fmtStl, a...) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/common/rwlatch.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/pzhzqt/goostub 2 | // there is license and copyright notice in licenses/goostub dir 3 | 4 | package common 5 | 6 | import ( 7 | "fmt" 8 | "math" 9 | "sync" 10 | "sync/atomic" 11 | ) 12 | 13 | type ReaderWriterLatch interface { 14 | WLock() 15 | WUnlock() 16 | RLock() 17 | RUnlock() 18 | PrintDebugInfo() 19 | } 20 | 21 | type readerWriterLatch struct { 22 | mutex *sync.RWMutex 23 | } 24 | 25 | const ( 26 | MaxReaders = math.MaxUint32 27 | ) 28 | 29 | func NewRWLatch() ReaderWriterLatch { 30 | latch := readerWriterLatch{} 31 | latch.mutex = new(sync.RWMutex) 32 | return &latch 33 | } 34 | 35 | func (l *readerWriterLatch) WLock() { 36 | l.mutex.Lock() 37 | } 38 | 39 | func (l *readerWriterLatch) WUnlock() { 40 | l.mutex.Unlock() 41 | } 42 | 43 | func (l *readerWriterLatch) RLock() { 44 | l.mutex.RLock() 45 | } 46 | 47 | func (l *readerWriterLatch) RUnlock() { 48 | l.mutex.RUnlock() 49 | } 50 | 51 | func (l *readerWriterLatch) PrintDebugInfo() { 52 | //do nothing 53 | } 54 | 55 | // for debug of cuncurrent code on single thread running 56 | type readerWriterLatchDummy struct { 57 | readerCnt int32 58 | writerCnt int32 59 | } 60 | 61 | func NewRWLatchDummy() ReaderWriterLatch { 62 | latch := readerWriterLatchDummy{0, 0} 63 | 64 | return &latch 65 | } 66 | 67 | func (l *readerWriterLatchDummy) WLock() { 68 | l.writerCnt++ 69 | 70 | if l.writerCnt != 1 { 71 | fmt.Printf("readerCnt: %d, writerCnt: %d\n", l.readerCnt, l.writerCnt) 72 | panic("double Write WLock!") 73 | } 74 | } 75 | 76 | func (l *readerWriterLatchDummy) WUnlock() { 77 | l.writerCnt-- 78 | 79 | if l.writerCnt != 0 { 80 | fmt.Printf("readerCnt: %d, writerCnt: %d\n", l.readerCnt, l.writerCnt) 81 | panic("double Write WUnlock!") 82 | } 83 | } 84 | 85 | func (l *readerWriterLatchDummy) RLock() { 86 | l.readerCnt++ 87 | 88 | if l.readerCnt != 1 { 89 | fmt.Printf("readerCnt: %d, writerCnt: %d\n", l.readerCnt, l.writerCnt) 90 | panic("double Reader WLock!") 91 | } 92 | 93 | } 94 | 95 | func (l *readerWriterLatchDummy) RUnlock() { 96 | l.readerCnt-- 97 | 98 | if l.readerCnt != 0 { 99 | fmt.Printf("readerCnt: %d, writerCnt: %d\n", l.readerCnt, l.writerCnt) 100 | panic("double Reader WUnlock!") 101 | } 102 | } 103 | 104 | func (l *readerWriterLatchDummy) PrintDebugInfo() { 105 | //do nothing 106 | } 107 | 108 | type readerWriterLatchDebug struct { 109 | mutex *sync.RWMutex 110 | readerCnt int32 111 | writerCnt int32 112 | } 113 | 114 | func NewRWLatchDebug() ReaderWriterLatch { 115 | latch := readerWriterLatchDebug{new(sync.RWMutex), 0, 0} 116 | 117 | return &latch 118 | } 119 | 120 | func (l *readerWriterLatchDebug) WLock() { 121 | atomic.AddInt32(&l.writerCnt, 1) 122 | //l.writerCnt++ 123 | fmt.Printf("WLock: readerCnt=%d, writerCnt=%d\n", l.readerCnt, l.writerCnt) 124 | 125 | l.mutex.Lock() 126 | } 127 | 128 | func (l *readerWriterLatchDebug) WUnlock() { 129 | atomic.AddInt32(&l.writerCnt, -1) 130 | //l.writerCnt-- 131 | fmt.Printf("WUnlock: readerCnt=%d, writerCnt=%d\n", l.readerCnt, l.writerCnt) 132 | 133 | l.mutex.Unlock() 134 | } 135 | 136 | func (l *readerWriterLatchDebug) RLock() { 137 | atomic.AddInt32(&l.readerCnt, 1) 138 | //l.readerCnt++ 139 | fmt.Printf("RLock: readerCnt=%d, writerCnt=%d\n", l.readerCnt, l.writerCnt) 140 | 141 | l.mutex.RLock() 142 | } 143 | 144 | func (l *readerWriterLatchDebug) RUnlock() { 145 | atomic.AddInt32(&l.readerCnt, -1) 146 | //l.readerCnt-- 147 | fmt.Printf("RUnlock: readerCnt=%d, writerCnt=%d\n", l.readerCnt, l.writerCnt) 148 | 149 | l.mutex.RUnlock() 150 | } 151 | 152 | func (l *readerWriterLatchDebug) PrintDebugInfo() { 153 | fmt.Printf("PrintDebugInfo: readerCnt=%d, writerCnt=%d\n", l.readerCnt, l.writerCnt) 154 | } 155 | -------------------------------------------------------------------------------- /lib/concurrency/checkpoint_manager.go: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ryogrid/SamehadaDB/lib/recovery" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 8 | "time" 9 | ) 10 | 11 | /** 12 | * CheckpointManager creates consistent checkpoints by blocking all other transactions temporarily. 13 | */ 14 | type CheckpointManager struct { 15 | transaction_manager *access.TransactionManager 16 | log_manager *recovery.LogManager 17 | buffer_pool_manager *buffer.BufferPoolManager 18 | // checkpointing thread works when this flag is true 19 | isCheckpointActive bool 20 | } 21 | 22 | func NewCheckpointManager( 23 | transaction_manager *access.TransactionManager, 24 | log_manager *recovery.LogManager, 25 | buffer_pool_manager *buffer.BufferPoolManager) *CheckpointManager { 26 | return &CheckpointManager{transaction_manager, log_manager, buffer_pool_manager, true} 27 | } 28 | 29 | func (checkpoint_manager *CheckpointManager) StartCheckpointTh() { 30 | go func() { 31 | for checkpoint_manager.IsCheckpointActive() { 32 | time.Sleep(time.Second * 30) 33 | if !checkpoint_manager.IsCheckpointActive() { 34 | break 35 | } 36 | fmt.Println("CheckpointTh: start checkpointing.") 37 | checkpoint_manager.BeginCheckpoint() 38 | checkpoint_manager.EndCheckpoint() 39 | fmt.Println("CheckpointTh: finish checkpointing.") 40 | } 41 | }() 42 | } 43 | 44 | func (checkpoint_manager *CheckpointManager) BeginCheckpoint() { 45 | // Block all the transactions and ensure that both the WAL and all dirty buffer pool pages are persisted to disk, 46 | // creating a consistent checkpoint. Do NOT allow transactions to resume at the end of this method, resume them 47 | // in CheckpointManager::EndCheckpoint() instead. This is for grading purposes. 48 | checkpoint_manager.transaction_manager.BlockAllTransactions() 49 | isSuccess := checkpoint_manager.buffer_pool_manager.FlushAllDirtyPages() 50 | if !isSuccess { 51 | fmt.Println("flush all dirty pages failed! at checkpointing (NOT BUG)") 52 | } 53 | checkpoint_manager.log_manager.Flush() 54 | } 55 | 56 | func (checkpoint_manager *CheckpointManager) EndCheckpoint() { 57 | // Allow transactions to resume, completing the checkpoint. 58 | checkpoint_manager.transaction_manager.ResumeTransactions() 59 | } 60 | 61 | func (checkpoint_manager *CheckpointManager) StopCheckpointTh() { 62 | checkpoint_manager.isCheckpointActive = false 63 | } 64 | 65 | func (checkpoint_manager *CheckpointManager) IsCheckpointActive() bool { 66 | return checkpoint_manager.isCheckpointActive 67 | } 68 | -------------------------------------------------------------------------------- /lib/concurrency/statistics_updater.go: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ryogrid/SamehadaDB/lib/catalog" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 7 | "time" 8 | ) 9 | 10 | type StatisticsUpdater struct { 11 | transaction_manager *access.TransactionManager 12 | c *catalog.Catalog 13 | // updater thread works when this flag is true 14 | isUpdaterActive bool 15 | } 16 | 17 | func NewStatisticsUpdater( 18 | transaction_manager *access.TransactionManager, c *catalog.Catalog) *StatisticsUpdater { 19 | 20 | return &StatisticsUpdater{transaction_manager, c, true} 21 | } 22 | 23 | func (updater *StatisticsUpdater) StartStaticsUpdaterTh() { 24 | go func() { 25 | for updater.IsUpdaterActive() { 26 | if !updater.IsUpdaterActive() { 27 | break 28 | } 29 | fmt.Println("StatisticsUpdaterTh: start updating.") 30 | updater.BeginStatsUpdate() 31 | updater.UpdateAllTablesStatistics() 32 | updater.EndStatsUpdate() 33 | fmt.Println("StatisticsUpdaterTh: finish updating.") 34 | time.Sleep(time.Second * 10) 35 | } 36 | }() 37 | } 38 | 39 | func (updater *StatisticsUpdater) UpdateAllTablesStatistics() { 40 | txn := updater.transaction_manager.Begin(nil) 41 | defer updater.transaction_manager.Commit(updater.c, txn) 42 | 43 | tables := updater.c.GetAllTables() 44 | for _, table_ := range tables { 45 | stat := table_.GetStatistics() 46 | err := stat.Update(table_, txn) 47 | if err != nil { 48 | // note: already updated table's statistics are not rollbacked 49 | // in current impl 50 | return 51 | } 52 | } 53 | } 54 | 55 | func (updater *StatisticsUpdater) BeginStatsUpdate() { 56 | // do nothing 57 | } 58 | 59 | func (updater *StatisticsUpdater) EndStatsUpdate() { 60 | // do nothing 61 | } 62 | 63 | func (updater *StatisticsUpdater) StopStatsUpdateTh() { 64 | updater.isUpdaterActive = false 65 | } 66 | 67 | func (updater *StatisticsUpdater) IsUpdaterActive() bool { 68 | return updater.isUpdaterActive 69 | } 70 | -------------------------------------------------------------------------------- /lib/container/btree/parent_bufmgr_impl.go: -------------------------------------------------------------------------------- 1 | package btree 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 5 | "github.com/ryogrid/SamehadaDB/lib/types" 6 | "github.com/ryogrid/bltree-go-for-embedding/interfaces" 7 | ) 8 | 9 | type ParentBufMgrImpl struct { 10 | *buffer.BufferPoolManager 11 | //allocedPageNum int32 12 | } 13 | 14 | func NewParentBufMgrImpl(bpm *buffer.BufferPoolManager) interfaces.ParentBufMgr { 15 | return &ParentBufMgrImpl{bpm} 16 | } 17 | 18 | func (p *ParentBufMgrImpl) FetchPPage(pageID int32) interfaces.ParentPage { 19 | //fmt.Println("ParentBufMgrImpl:FetchPPage pageID:", pageID) 20 | //p.allocedPageNum++ 21 | //fmt.Println("ParentBufMgrImpl:FetchPPage allocedPageNum:", p.allocedPageNum) 22 | return &ParentPageImpl{p.FetchPage(types.PageID(pageID))} 23 | } 24 | 25 | func (p *ParentBufMgrImpl) UnpinPPage(pageID int32, isDirty bool) error { 26 | //fmt.Println("ParentBufMgrImpl:UnpinPPage pageID:", pageID, "isDirty:", isDirty) 27 | //p.allocedPageNum-- 28 | //fmt.Println("ParentBufMgrImpl:UnpinPPage allocedPageNum:", p.allocedPageNum) 29 | return p.UnpinPage(types.PageID(pageID), isDirty) 30 | } 31 | 32 | func (p *ParentBufMgrImpl) NewPPage() interfaces.ParentPage { 33 | //fmt.Println("ParentBufMgrImpl:NewPPage") 34 | //p.allocedPageNum++ 35 | //fmt.Println("ParentBufMgrImpl:NewPPage allocedPageNum:", p.allocedPageNum) 36 | return &ParentPageImpl{p.NewPage()} 37 | } 38 | 39 | func (p *ParentBufMgrImpl) DeallocatePPage(pageID int32, isNoWait bool) error { 40 | return p.DeallocatePage(types.PageID(pageID), isNoWait) 41 | } 42 | -------------------------------------------------------------------------------- /lib/container/btree/parent_page_impl.go: -------------------------------------------------------------------------------- 1 | package btree 2 | 3 | import "github.com/ryogrid/SamehadaDB/lib/storage/page" 4 | 5 | type ParentPageImpl struct { 6 | *page.Page 7 | } 8 | 9 | func (p *ParentPageImpl) DecPPinCount() { 10 | p.DecPinCount() 11 | } 12 | 13 | func (p *ParentPageImpl) PPinCount() int32 { 14 | return p.PinCount() 15 | } 16 | 17 | func (p *ParentPageImpl) GetPPageId() int32 { 18 | return int32(p.GetPageId()) 19 | } 20 | 21 | func (p *ParentPageImpl) DataAsSlice() []byte { 22 | return (*p.Data())[:] 23 | } 24 | -------------------------------------------------------------------------------- /lib/container/hash/hash_table_test.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package hash 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | testingpkg "github.com/ryogrid/SamehadaDB/lib/testing/testing_assert" 10 | "github.com/ryogrid/SamehadaDB/lib/types" 11 | "testing" 12 | 13 | "github.com/ryogrid/SamehadaDB/lib/recovery" 14 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 15 | "github.com/ryogrid/SamehadaDB/lib/storage/disk" 16 | ) 17 | 18 | func IntToBytes(val int) []byte { 19 | buf := new(bytes.Buffer) 20 | binary.Write(buf, binary.LittleEndian, int32(val)) 21 | return buf.Bytes() 22 | } 23 | 24 | func TestLinearProbeHashTable(t *testing.T) { 25 | diskManager := disk.NewDiskManagerTest() 26 | defer diskManager.ShutDown() 27 | bpm := buffer.NewBufferPoolManager(uint32(10), diskManager, recovery.NewLogManager(&diskManager)) 28 | 29 | ht := NewLinearProbeHashTable(bpm, 1000, types.InvalidPageID) 30 | 31 | for i := 0; i < 5; i++ { 32 | ht.Insert(IntToBytes(i), uint64(i)) 33 | res := ht.GetValue(IntToBytes(i)) 34 | if len(res) == 0 { 35 | t.Errorf("result should not be nil") 36 | } else { 37 | testingpkg.Equals(t, uint64(i), res[0]) 38 | } 39 | } 40 | 41 | for i := 0; i < 5; i++ { 42 | res := ht.GetValue(IntToBytes(i)) 43 | if len(res) == 0 { 44 | t.Errorf("result should not be nil") 45 | } else { 46 | testingpkg.Equals(t, uint64(i), res[0]) 47 | } 48 | } 49 | 50 | // test for duplicate values 51 | for i := 0; i < 5; i++ { 52 | if i == 0 { 53 | testingpkg.Nok(t, ht.Insert(IntToBytes(i), uint64(2*i))) 54 | } else { 55 | testingpkg.Ok(t, ht.Insert(IntToBytes(i), uint64(2*i))) 56 | } 57 | ht.Insert(IntToBytes(i), uint64(2*i)) 58 | res := ht.GetValue(IntToBytes(i)) 59 | if i == 0 { 60 | testingpkg.Equals(t, 1, len(res)) 61 | testingpkg.Equals(t, uint64(i), res[0]) 62 | } else { 63 | testingpkg.Equals(t, 2, len(res)) 64 | if res[0] == uint64(i) { 65 | testingpkg.Equals(t, uint64(2*i), res[1]) 66 | } else { 67 | testingpkg.Equals(t, uint64(2*i), res[0]) 68 | testingpkg.Equals(t, uint64(i), res[1]) 69 | } 70 | } 71 | } 72 | 73 | // look for a key that does not exist 74 | res := ht.GetValue(IntToBytes(20)) 75 | testingpkg.Equals(t, 0, len(res)) 76 | 77 | // delete some values 78 | for i := 0; i < 5; i++ { 79 | ht.Remove(IntToBytes(i), uint64(i)) 80 | res := ht.GetValue(IntToBytes(i)) 81 | 82 | if i == 0 { 83 | testingpkg.Equals(t, 0, len(res)) 84 | } else { 85 | testingpkg.Equals(t, 1, len(res)) 86 | testingpkg.Equals(t, uint64(2*i), res[0]) 87 | } 88 | } 89 | 90 | // remove several entries and re-insert these entry and check got value 91 | for i := 1; i < 5; i++ { 92 | ht.Remove(IntToBytes(i), uint64(i*2)) 93 | ht.Insert(IntToBytes(i), uint64(i*3)) 94 | res := ht.GetValue(IntToBytes(i)) 95 | 96 | testingpkg.Equals(t, 1, len(res)) 97 | testingpkg.Equals(t, uint64(3*i), res[0]) 98 | } 99 | 100 | bpm.FlushAllPages() 101 | } 102 | -------------------------------------------------------------------------------- /lib/container/hash/hash_util.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | 7 | "github.com/ryogrid/SamehadaDB/lib/types" 8 | "github.com/spaolacci/murmur3" 9 | ) 10 | 11 | /** @return the hash of the value */ 12 | func HashValue(val *types.Value) uint32 { 13 | switch val.ValueType() { 14 | case types.Integer: 15 | raw := val.Serialize() 16 | return GenHashMurMur(raw) 17 | case types.Float: 18 | raw := val.Serialize() 19 | return GenHashMurMur(raw) 20 | case types.Varchar: 21 | raw := val.Serialize() 22 | return GenHashMurMur(raw) 23 | default: 24 | fmt.Println(val.ValueType()) 25 | panic("not supported type!") 26 | } 27 | } 28 | 29 | func GenHashMurMur(key []byte) uint32 { 30 | h := murmur3.New128() 31 | h.Write(key) 32 | 33 | hash := h.Sum(nil) 34 | 35 | return binary.LittleEndian.Uint32(hash) 36 | } 37 | -------------------------------------------------------------------------------- /lib/container/hash/linear_probe_hash_table_iterator.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package hash 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/page" 8 | "unsafe" 9 | 10 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 11 | "github.com/ryogrid/SamehadaDB/lib/types" 12 | ) 13 | 14 | type hashTableIterator struct { 15 | bpm *buffer.BufferPoolManager 16 | headerPage *page.HashTableHeaderPage 17 | bucket uint64 18 | offset uint64 19 | blockId types.PageID 20 | blockPage *page.HashTableBlockPage 21 | } 22 | 23 | func newHashTableIterator(bpm *buffer.BufferPoolManager, header *page.HashTableHeaderPage, bucket uint64, offset uint64) *hashTableIterator { 24 | blockPageId := header.GetBlockPageId(bucket) 25 | 26 | bPageData := bpm.FetchPage(blockPageId).Data() 27 | blockPage := (*page.HashTableBlockPage)(unsafe.Pointer(bPageData)) 28 | 29 | return &hashTableIterator{bpm, header, bucket, offset, blockPageId, blockPage} 30 | } 31 | 32 | func (itr *hashTableIterator) next() { 33 | itr.offset++ 34 | // reached end of the current block page, we need to go to the next one 35 | if itr.offset >= page.BlockArraySize { 36 | itr.bucket += 1 37 | itr.offset = 0 38 | 39 | // we need to go to the first block 40 | if itr.bucket >= itr.headerPage.NumBlocks() { 41 | itr.bucket = 0 42 | } 43 | 44 | itr.bpm.UnpinPage(itr.blockId, true) 45 | itr.blockId = itr.headerPage.GetBlockPageId(itr.bucket) 46 | 47 | bPageData := itr.bpm.FetchPage(itr.blockId).Data() 48 | itr.blockPage = (*page.HashTableBlockPage)(unsafe.Pointer(bPageData)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/container/skip_list/skip_list_iterator.go: -------------------------------------------------------------------------------- 1 | package skip_list 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/samehada/samehada_util" 5 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/index/index_common" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/page" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/page/skip_list_page" 9 | "github.com/ryogrid/SamehadaDB/lib/types" 10 | ) 11 | 12 | type SkipListIterator struct { 13 | sl *SkipList 14 | bpm *buffer.BufferPoolManager 15 | curNode *skip_list_page.SkipListBlockPage 16 | curEntry *index_common.IndexEntry 17 | rangeStartKey *types.Value 18 | rangeEndKey *types.Value 19 | keyType types.TypeID 20 | entryList []*index_common.IndexEntry 21 | curEntryIdx int32 22 | } 23 | 24 | func NewSkipListIterator(sl *SkipList, rangeStartKey *types.Value, rangeEndKey *types.Value) *SkipListIterator { 25 | ret := new(SkipListIterator) 26 | 27 | headerPage := sl.getHeaderPage() 28 | 29 | ret.sl = sl 30 | ret.bpm = sl.bpm 31 | 32 | ret.rangeStartKey = rangeStartKey 33 | ret.rangeEndKey = rangeEndKey 34 | ret.keyType = headerPage.GetKeyType() 35 | ret.entryList = make([]*index_common.IndexEntry, 0) 36 | 37 | ret.initRIDList(sl) 38 | 39 | return ret 40 | } 41 | 42 | func (itr *SkipListIterator) initRIDList(sl *SkipList) { 43 | curPageSlotIdx := int32(0) 44 | // set appropriate start position 45 | if itr.rangeStartKey != nil { 46 | found, node, slotIdx := itr.sl.FindNodeWithEntryIdxForItr(itr.rangeStartKey) 47 | // locking is not needed because already have lock with FindNodeWithEntryIdxForItr method call 48 | if found { 49 | // considering increment of curPageSlotIdx after here 50 | // because slotIdx is match indx of rangeStartKey 51 | curPageSlotIdx = slotIdx - 1 52 | } else { 53 | // considering increment of curPageSlotIdx after here 54 | // because slotIdx is nearest smaller key of rangeStartKey 55 | curPageSlotIdx = slotIdx 56 | } 57 | itr.curNode = node 58 | } else { 59 | itr.curNode = sl.getStartNode() 60 | itr.curNode.RLatch() 61 | itr.curNode.AddRLatchRecord(-10000) 62 | // for keepping pin count is one after iterator finishd using startNode 63 | sl.bpm.IncPinOfPage(itr.curNode) 64 | } 65 | 66 | for { 67 | if curPageSlotIdx+1 >= itr.curNode.GetEntryCnt() { 68 | prevNodeId := itr.curNode.GetPageId() 69 | nextNodeId := itr.curNode.GetForwardEntry(0) 70 | prevNode := itr.curNode 71 | itr.curNode = skip_list_page.FetchAndCastToBlockPage(itr.bpm, nextNodeId) 72 | itr.curNode.RLatch() 73 | itr.curNode.AddRLatchRecord(-10000) 74 | itr.bpm.UnpinPage(prevNodeId, false) 75 | prevNode.RemoveRLatchRecord(-10000) 76 | prevNode.RUnlatch() 77 | curPageSlotIdx = -1 78 | if itr.curNode.GetSmallestKey(itr.keyType).IsInfMax() { 79 | // reached tail node 80 | itr.bpm.UnpinPage(itr.curNode.GetPageId(), false) 81 | itr.curNode.RemoveRLatchRecord(-10000) 82 | itr.curNode.RUnlatch() 83 | return 84 | } 85 | } 86 | 87 | // always having RLatch of itr.curNode here 88 | 89 | curPageSlotIdx++ 90 | 91 | if itr.rangeEndKey != nil && itr.curNode.GetEntry(int(curPageSlotIdx), itr.keyType).Key.CompareGreaterThan(*itr.rangeEndKey) { 92 | itr.bpm.UnpinPage(itr.curNode.GetPageId(), false) 93 | itr.curNode.RemoveRLatchRecord(-10000) 94 | itr.curNode.RUnlatch() 95 | break 96 | } 97 | 98 | itr.entryList = append(itr.entryList, itr.curNode.GetEntry(int(curPageSlotIdx), itr.keyType)) 99 | } 100 | } 101 | 102 | func (itr *SkipListIterator) Next() (done bool, err error, key *types.Value, rid *page.RID) { 103 | if itr.curEntryIdx < int32(len(itr.entryList)) { 104 | ret := itr.entryList[itr.curEntryIdx] 105 | itr.curEntryIdx++ 106 | tmpRID := samehada_util.UnpackUint64toRID(ret.Value) 107 | return false, nil, samehada_util.GetPonterOfValue(ret.Key), &tmpRID 108 | } else { 109 | return true, nil, nil, nil 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/errors/type.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package errors 5 | 6 | type Error string 7 | 8 | func (e Error) Error() string { return string(e) } 9 | -------------------------------------------------------------------------------- /lib/execution/executors/delete_executor.go: -------------------------------------------------------------------------------- 1 | package executors 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 10 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 11 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 12 | ) 13 | 14 | /** 15 | * DeleteExecutor executes a sequential scan over a table and delete tuples according to predicate. 16 | */ 17 | type DeleteExecutor struct { 18 | context *ExecutorContext 19 | plan *plans.DeletePlanNode 20 | child Executor 21 | txn *access.Transaction 22 | } 23 | 24 | func NewDeleteExecutor(context *ExecutorContext, plan *plans.DeletePlanNode, child Executor) Executor { 25 | return &DeleteExecutor{context, plan, child, context.GetTransaction()} 26 | } 27 | 28 | func (e *DeleteExecutor) Init() { 29 | e.child.Init() 30 | } 31 | 32 | // Next implements the next method for the sequential scan operator 33 | // It uses the table heap iterator to iterate through the table heap 34 | // tyring to find a tuple to be deleted. It performs selection on-the-fly 35 | // if find tuple to be delete, mark it to be deleted at commit and return value 36 | func (e *DeleteExecutor) Next() (*tuple.Tuple, Done, error) { 37 | 38 | // iterates through the table heap trying to select a tuple that matches the predicate 39 | for t, done, err := e.child.Next(); !done; t, done, err = e.child.Next() { 40 | if t == nil { 41 | err_ := errors.New("e.it.Next returned nil") 42 | e.txn.SetState(access.ABORTED) 43 | return nil, true, err_ 44 | } 45 | if err != nil { 46 | e.txn.SetState(access.ABORTED) 47 | return nil, true, err 48 | } 49 | if e.txn.GetState() == access.ABORTED { 50 | return nil, true, err 51 | } 52 | 53 | rid := t.GetRID() 54 | tableMetadata := e.child.GetTableMetaData() 55 | is_marked := tableMetadata.Table().MarkDelete(rid, tableMetadata.OID(), e.txn, false) 56 | if !is_marked { 57 | err := errors.New("marking tuple deleted failed. PageId:SlotNum = " + string(rid.GetPageId()) + ":" + fmt.Sprint(rid.GetSlotNum())) 58 | e.txn.SetState(access.ABORTED) 59 | return nil, false, err 60 | } 61 | 62 | // removing index entry is done at commit phase because delete operation uses marking technique 63 | 64 | colNum := tableMetadata.GetColumnNum() 65 | for ii := 0; ii < int(colNum); ii++ { 66 | ret := tableMetadata.GetIndex(ii) 67 | if ret == nil { 68 | continue 69 | } else { 70 | index_ := ret 71 | index_.DeleteEntry(t, *rid, e.txn) 72 | } 73 | } 74 | 75 | return t, false, nil 76 | } 77 | 78 | return nil, true, nil 79 | } 80 | 81 | func (e *DeleteExecutor) GetOutputSchema() *schema.Schema { return e.plan.OutputSchema() } 82 | 83 | func (e *DeleteExecutor) GetTableMetaData() *catalog.TableMetadata { return e.child.GetTableMetaData() } 84 | -------------------------------------------------------------------------------- /lib/execution/executors/execution_engine.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package executors 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 10 | ) 11 | 12 | // ExecutionEngine is the query execution engine. 13 | // 14 | // It is an implementation of the GetRangeScanIterator Model (also called Pipeline model or Volcano) 15 | // It receives a Plan, create a Executor for that plan and execute it 16 | // All executors follow the same pattern implementing the Executor interface 17 | // Executors are the operators in relation algebra 18 | type ExecutionEngine struct { 19 | } 20 | 21 | func (e *ExecutionEngine) Execute(plan plans.Plan, context *ExecutorContext) []*tuple.Tuple { 22 | executor := e.CreateExecutor(plan, context) 23 | executor.Init() 24 | 25 | tuples := []*tuple.Tuple{} 26 | for { 27 | tuple, done, err := executor.Next() 28 | if err != nil { 29 | context.txn.SetState(access.ABORTED) 30 | return nil 31 | } 32 | if done { 33 | break 34 | } 35 | 36 | if tuple != nil { 37 | tuples = append(tuples, tuple) 38 | } 39 | } 40 | 41 | return tuples 42 | } 43 | 44 | func (e *ExecutionEngine) CreateExecutor(plan plans.Plan, context *ExecutorContext) Executor { 45 | switch p := plan.(type) { 46 | case *plans.InsertPlanNode: 47 | return NewInsertExecutor(context, p) 48 | case *plans.SeqScanPlanNode: 49 | return NewSeqScanExecutor(context, p) 50 | case *plans.PointScanWithIndexPlanNode: 51 | return NewPointScanWithIndexExecutor(context, p) 52 | case *plans.RangeScanWithIndexPlanNode: 53 | return NewRangeScanWithIndexExecutor(context, p) 54 | case *plans.LimitPlanNode: 55 | return NewLimitExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 56 | case *plans.DeletePlanNode: 57 | return NewDeleteExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 58 | case *plans.UpdatePlanNode: 59 | return NewUpdateExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 60 | case *plans.HashJoinPlanNode: 61 | return NewHashJoinExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context), e.CreateExecutor(plan.GetChildAt(1), context)) 62 | case *plans.IndexJoinPlanNode: 63 | return NewIndexJoinExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 64 | case *plans.NestedLoopJoinPlanNode: 65 | return NewNestedLoopJoinExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context), e.CreateExecutor(plan.GetChildAt(1), context)) 66 | case *plans.AggregationPlanNode: 67 | return NewAggregationExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 68 | case *plans.OrderbyPlanNode: 69 | return NewOrderbyExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 70 | case *plans.SelectionPlanNode: 71 | return NewSelectionExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 72 | case *plans.ProjectionPlanNode: 73 | return NewProjectionExecutor(context, p, e.CreateExecutor(plan.GetChildAt(0), context)) 74 | } 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /lib/execution/executors/executor.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package executors 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 10 | ) 11 | 12 | type Done bool 13 | 14 | // Executor represents a relational algebra operator in the ite 15 | // 16 | // Init initializes this executor. 17 | // This function must be called before Next() is called! 18 | // 19 | // Next produces the next tuple 20 | type Executor interface { 21 | Init() 22 | Next() (*tuple.Tuple, Done, error) 23 | GetOutputSchema() *schema.Schema 24 | GetTableMetaData() *catalog.TableMetadata 25 | } 26 | -------------------------------------------------------------------------------- /lib/execution/executors/executor_context.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package executors 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 10 | ) 11 | 12 | /** 13 | * ExecutorContext stores all the context necessary to run an executor. 14 | */ 15 | type ExecutorContext struct { 16 | catalog *catalog.Catalog 17 | bpm *buffer.BufferPoolManager 18 | txn *access.Transaction 19 | } 20 | 21 | func NewExecutorContext(catalog *catalog.Catalog, bpm *buffer.BufferPoolManager, txn *access.Transaction) *ExecutorContext { 22 | return &ExecutorContext{catalog, bpm, txn} 23 | } 24 | 25 | func (e *ExecutorContext) GetCatalog() *catalog.Catalog { 26 | return e.catalog 27 | } 28 | 29 | func (e *ExecutorContext) GetBufferPoolManager() *buffer.BufferPoolManager { 30 | return e.bpm 31 | } 32 | 33 | func (e *ExecutorContext) GetTransaction() *access.Transaction { 34 | return e.txn 35 | } 36 | 37 | func (e *ExecutorContext) SetTransaction(txn *access.Transaction) { 38 | e.txn = txn 39 | } 40 | -------------------------------------------------------------------------------- /lib/execution/executors/executor_util.go: -------------------------------------------------------------------------------- 1 | package executors 2 | -------------------------------------------------------------------------------- /lib/execution/executors/insert_executor.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package executors 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 10 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 11 | ) 12 | 13 | /** 14 | * InsertExecutor executes an insert into a table. 15 | * Inserted values can either be embedded in the plan itself ("raw insert") or come from a child executor. 16 | */ 17 | type InsertExecutor struct { 18 | context *ExecutorContext 19 | plan *plans.InsertPlanNode 20 | tableMetadata *catalog.TableMetadata 21 | } 22 | 23 | func NewInsertExecutor(context *ExecutorContext, plan *plans.InsertPlanNode) Executor { 24 | tableMetadata := context.GetCatalog().GetTableByOID(plan.GetTableOID()) 25 | 26 | return &InsertExecutor{context, plan, tableMetadata} 27 | } 28 | 29 | func (e *InsertExecutor) Init() { 30 | 31 | } 32 | 33 | // Next inserts the tuples into the tables 34 | // Note that Insert does not return any tuple 35 | // We return an error if the insert failed for any reason, and return nil if all inserts succeeded. 36 | func (e *InsertExecutor) Next() (*tuple.Tuple, Done, error) { 37 | // let's assume it is raw insert 38 | 39 | for _, values := range e.plan.GetRawValues() { 40 | tuple_ := tuple.NewTupleFromSchema(values, e.tableMetadata.Schema()) 41 | tableHeap := e.tableMetadata.Table() 42 | rid, err := tableHeap.InsertTuple(tuple_, e.context.txn, e.tableMetadata.OID(), false) 43 | if err != nil { 44 | return nil, true, err 45 | } 46 | 47 | colNum := e.tableMetadata.GetColumnNum() 48 | for ii := 0; ii < int(colNum); ii++ { 49 | ret := e.tableMetadata.GetIndex(ii) 50 | if ret == nil { 51 | continue 52 | } else { 53 | index_ := ret 54 | index_.InsertEntry(tuple_, *rid, e.context.txn) 55 | } 56 | } 57 | } 58 | 59 | return nil, true, nil 60 | } 61 | 62 | func (e *InsertExecutor) GetOutputSchema() *schema.Schema { 63 | return e.plan.OutputSchema() 64 | } 65 | 66 | func (e *InsertExecutor) GetTableMetaData() *catalog.TableMetadata { return e.tableMetadata } 67 | -------------------------------------------------------------------------------- /lib/execution/executors/limit_executor.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package executors 5 | 6 | import ( 7 | "errors" 8 | "github.com/ryogrid/SamehadaDB/lib/catalog" 9 | 10 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 11 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 12 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 13 | ) 14 | 15 | // LimitExecutor implements the limit/offset operation 16 | type LimitExecutor struct { 17 | context *ExecutorContext 18 | plan *plans.LimitPlanNode // contains information about limit and offset 19 | child Executor // the child executor that will provide tuples to the limit executor 20 | emitted uint32 // counts the number of tuples processed. It is compared to the LIMIT 21 | skipped uint32 // counts the number of tuples skiped. It is compared to the OFFSET 22 | } 23 | 24 | func NewLimitExecutor(context *ExecutorContext, plan *plans.LimitPlanNode, child Executor) Executor { 25 | return &LimitExecutor{context, plan, child, 0, 0} 26 | } 27 | 28 | func (e *LimitExecutor) Init() { 29 | e.child.Init() 30 | } 31 | 32 | func (e *LimitExecutor) Next() (*tuple.Tuple, Done, error) { 33 | for t, done, err := e.child.Next(); !done; t, done, err = e.child.Next() { 34 | if err != nil { 35 | return nil, done, err 36 | } 37 | if t == nil && done == false { 38 | err := errors.New("e.child.Next returned nil unexpectedly.") 39 | return nil, true, err 40 | } 41 | 42 | if e.skipped < e.plan.GetOffset() { 43 | e.skipped++ 44 | continue 45 | } 46 | 47 | e.emitted++ 48 | if e.emitted > e.plan.GetLimit() { 49 | return nil, true, nil 50 | } 51 | 52 | return t, false, nil 53 | } 54 | 55 | return nil, true, nil 56 | } 57 | 58 | func (e *LimitExecutor) GetOutputSchema() *schema.Schema { 59 | return e.plan.OutputSchema() 60 | } 61 | 62 | func (e *LimitExecutor) GetTableMetaData() *catalog.TableMetadata { return e.child.GetTableMetaData() } 63 | -------------------------------------------------------------------------------- /lib/execution/executors/nested_loop_join_executor.go: -------------------------------------------------------------------------------- 1 | package executors 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 9 | "github.com/ryogrid/SamehadaDB/lib/types" 10 | ) 11 | 12 | type NestedLoopJoinExecutor struct { 13 | context *ExecutorContext 14 | plan *plans.NestedLoopJoinPlanNode 15 | left Executor 16 | right Executor 17 | retTuples []*tuple.Tuple 18 | curIdx int32 19 | } 20 | 21 | func NewNestedLoopJoinExecutor(exec_ctx *ExecutorContext, plan *plans.NestedLoopJoinPlanNode, left Executor, 22 | right Executor) *NestedLoopJoinExecutor { 23 | ret := new(NestedLoopJoinExecutor) 24 | ret.plan = plan 25 | ret.left = left 26 | ret.right = right 27 | ret.context = exec_ctx 28 | ret.retTuples = make([]*tuple.Tuple, 0) 29 | return ret 30 | } 31 | 32 | func (e *NestedLoopJoinExecutor) GetOutputSchema() *schema.Schema { return e.plan.OutputSchema() } 33 | 34 | func (e *NestedLoopJoinExecutor) Init() { 35 | e.left.Init() 36 | e.right.Init() 37 | 38 | rightTuples := make([]*tuple.Tuple, 0) 39 | for rightTuple, doneRight, errRight := e.right.Next(); !doneRight; rightTuple, doneRight, errRight = e.right.Next() { 40 | if errRight != nil { 41 | e.context.txn.SetState(access.ABORTED) 42 | return 43 | } 44 | rightTuples = append(rightTuples, rightTuple) 45 | } 46 | 47 | for leftTuple, doneLeft, errLeft := e.left.Next(); !doneLeft; leftTuple, doneLeft, errLeft = e.left.Next() { 48 | if errLeft != nil { 49 | e.context.txn.SetState(access.ABORTED) 50 | return 51 | } 52 | for _, rightTuple := range rightTuples { 53 | e.retTuples = append(e.retTuples, e.MakeOutputTuple(leftTuple, rightTuple)) 54 | } 55 | } 56 | } 57 | 58 | // TODO: (SDB) need to refactor NestedLoopJoinExecutor::Next method to use GetExpr method of Column class 59 | func (e *NestedLoopJoinExecutor) Next() (*tuple.Tuple, Done, error) { 60 | if e.curIdx >= int32(len(e.retTuples)) { 61 | return nil, true, nil 62 | } 63 | ret := e.retTuples[e.curIdx] 64 | e.curIdx++ 65 | return ret, false, nil 66 | } 67 | 68 | func (e *NestedLoopJoinExecutor) MakeOutputTuple(left_tuple *tuple.Tuple, right_tuple *tuple.Tuple) *tuple.Tuple { 69 | outputColumnCnt := int(e.GetOutputSchema().GetColumnCount()) 70 | leftColumnCnt := int(e.left.GetOutputSchema().GetColumnCount()) 71 | values := make([]types.Value, outputColumnCnt) 72 | for ii := 0; ii < outputColumnCnt; ii++ { 73 | if ii < leftColumnCnt { 74 | values[ii] = left_tuple.GetValue(e.left.GetOutputSchema(), uint32(ii)) 75 | } else { 76 | values[ii] = right_tuple.GetValue(e.right.GetOutputSchema(), uint32(ii-leftColumnCnt)) 77 | } 78 | } 79 | return tuple.NewTupleFromSchema(values, e.GetOutputSchema()) 80 | } 81 | 82 | // can not be used 83 | func (e *NestedLoopJoinExecutor) GetTableMetaData() *catalog.TableMetadata { return nil } 84 | -------------------------------------------------------------------------------- /lib/execution/executors/point_scan_with_index_executor.go: -------------------------------------------------------------------------------- 1 | package executors 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/samehada/samehada_util" 5 | 6 | "github.com/ryogrid/SamehadaDB/lib/catalog" 7 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 10 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 11 | "github.com/ryogrid/SamehadaDB/lib/types" 12 | ) 13 | 14 | /** 15 | * PointScanWithIndexExecutor executes scan with hash index to filter rows matches predicate. 16 | */ 17 | type PointScanWithIndexExecutor struct { 18 | context *ExecutorContext 19 | plan *plans.PointScanWithIndexPlanNode 20 | tableMetadata *catalog.TableMetadata 21 | txn *access.Transaction 22 | foundTuples []*tuple.Tuple 23 | } 24 | 25 | func NewPointScanWithIndexExecutor(context *ExecutorContext, plan *plans.PointScanWithIndexPlanNode) Executor { 26 | tableMetadata := context.GetCatalog().GetTableByOID(plan.GetTableOID()) 27 | 28 | return &PointScanWithIndexExecutor{context, plan, tableMetadata, context.GetTransaction(), make([]*tuple.Tuple, 0)} 29 | } 30 | 31 | func (e *PointScanWithIndexExecutor) Init() { 32 | comparison := e.plan.GetPredicate() 33 | schema_ := e.tableMetadata.Schema() 34 | colIdxOfPred := comparison.GetLeftSideColIdx() 35 | 36 | index_ := e.tableMetadata.GetIndex(int(colIdxOfPred)) 37 | if index_ == nil { 38 | panic("PointScanWithIndexExecutor assumed index does not exist!.") 39 | } 40 | 41 | scanKey := samehada_util.GetPonterOfValue(comparison.GetRightSideValue(nil, schema_)) 42 | dummyTuple := tuple.GenTupleForIndexSearch(schema_, colIdxOfPred, scanKey) 43 | rids := index_.ScanKey(dummyTuple, e.txn) 44 | 45 | for _, rid := range rids { 46 | rid_ := rid 47 | tuple_, err := e.tableMetadata.Table().GetTuple(&rid_, e.txn) 48 | if tuple_ == nil && err != access.ErrSelfDeletedCase { 49 | //fmt.Println("PointScanWithIndexExecutor:Init ErrSelfDeletedCase!") 50 | e.foundTuples = make([]*tuple.Tuple, 0) 51 | e.txn.SetState(access.ABORTED) 52 | return 53 | } 54 | if err == access.ErrSelfDeletedCase { 55 | continue 56 | } 57 | 58 | if !tuple_.GetValue(schema_, colIdxOfPred).CompareEquals(*scanKey) { 59 | // found record is updated and commited case 60 | e.foundTuples = make([]*tuple.Tuple, 0) 61 | e.txn.SetState(access.ABORTED) 62 | return 63 | } 64 | e.foundTuples = append(e.foundTuples, tuple_) 65 | } 66 | } 67 | 68 | func (e *PointScanWithIndexExecutor) Next() (*tuple.Tuple, Done, error) { 69 | if e.txn.GetState() == access.ABORTED { 70 | return nil, true, access.ErrGeneral 71 | } 72 | 73 | if len(e.foundTuples) > 0 { 74 | tuple_ := e.foundTuples[0] 75 | e.foundTuples = e.foundTuples[1:] 76 | retTuple := e.projects(tuple_) 77 | retTuple.SetRID(tuple_.GetRID()) 78 | return retTuple, false, nil 79 | } 80 | 81 | return nil, true, nil 82 | } 83 | 84 | // project applies the projection operator defined by the output schema 85 | func (e *PointScanWithIndexExecutor) projects(tuple_ *tuple.Tuple) *tuple.Tuple { 86 | outputSchema := e.plan.OutputSchema() 87 | 88 | values := []types.Value{} 89 | for i := uint32(0); i < outputSchema.GetColumnCount(); i++ { 90 | colIndex := e.tableMetadata.Schema().GetColIndex(outputSchema.GetColumns()[i].GetColumnName()) 91 | values = append(values, tuple_.GetValue(e.tableMetadata.Schema(), colIndex)) 92 | } 93 | 94 | return tuple.NewTupleFromSchema(values, outputSchema) 95 | } 96 | 97 | func (e *PointScanWithIndexExecutor) GetOutputSchema() *schema.Schema { 98 | return e.plan.OutputSchema() 99 | } 100 | 101 | func (e *PointScanWithIndexExecutor) GetTableMetaData() *catalog.TableMetadata { 102 | return e.tableMetadata 103 | } 104 | -------------------------------------------------------------------------------- /lib/execution/executors/projection_executor.go: -------------------------------------------------------------------------------- 1 | package executors 2 | 3 | import ( 4 | "errors" 5 | "github.com/ryogrid/SamehadaDB/lib/catalog" 6 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 10 | "github.com/ryogrid/SamehadaDB/lib/types" 11 | ) 12 | 13 | type ProjectionExecutor struct { 14 | context *ExecutorContext 15 | plan *plans.ProjectionPlanNode // contains information about where clause 16 | child Executor // the child executor that will provide tuples to the this executor 17 | } 18 | 19 | func NewProjectionExecutor(context *ExecutorContext, plan *plans.ProjectionPlanNode, child Executor) Executor { 20 | return &ProjectionExecutor{context, plan, child} 21 | } 22 | 23 | func (e *ProjectionExecutor) Init() { 24 | e.child.Init() 25 | } 26 | 27 | func (e *ProjectionExecutor) Next() (*tuple.Tuple, Done, error) { 28 | for t, done, err := e.child.Next(); !done; t, done, err = e.child.Next() { 29 | if err != nil { 30 | return nil, done, err 31 | } 32 | if t == nil && done == false { 33 | err := errors.New("e.child.Next returned nil unexpectedly.") 34 | e.context.txn.SetState(access.ABORTED) 35 | return nil, true, err 36 | } 37 | 38 | return e.projects(t), false, nil 39 | } 40 | 41 | return nil, true, nil 42 | } 43 | 44 | func (e *ProjectionExecutor) GetOutputSchema() *schema.Schema { 45 | return e.plan.OutputSchema() 46 | } 47 | 48 | // project applies the projection operator defined by the output schema 49 | func (e *ProjectionExecutor) projects(tuple_ *tuple.Tuple) *tuple.Tuple { 50 | srcOutSchema := e.plan.GetChildAt(0).OutputSchema() 51 | projectSchema := e.plan.OutputSchema() 52 | 53 | values := []types.Value{} 54 | for i := uint32(0); i < projectSchema.GetColumnCount(); i++ { 55 | colIndex := srcOutSchema.GetColIndex(projectSchema.GetColumns()[i].GetColumnName()) 56 | values = append(values, tuple_.GetValue(srcOutSchema, colIndex)) 57 | } 58 | 59 | return tuple.NewTupleFromSchema(values, projectSchema) 60 | } 61 | 62 | func (e *ProjectionExecutor) GetTableMetaData() *catalog.TableMetadata { 63 | return e.child.GetTableMetaData() 64 | } 65 | -------------------------------------------------------------------------------- /lib/execution/executors/selection_executor.go: -------------------------------------------------------------------------------- 1 | package executors 2 | 3 | import ( 4 | "errors" 5 | "github.com/ryogrid/SamehadaDB/lib/catalog" 6 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 7 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 10 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 11 | ) 12 | 13 | // do filtering according to WHERE clause for Plan(Executor) which has no filtering feature 14 | 15 | type SlectionExecutor struct { 16 | context *ExecutorContext 17 | plan *plans.SelectionPlanNode // contains information about where clause 18 | child Executor // the child executor that will provide tuples to the this executor 19 | } 20 | 21 | func NewSelectionExecutor(context *ExecutorContext, plan *plans.SelectionPlanNode, child Executor) Executor { 22 | return &SlectionExecutor{context, plan, child} 23 | } 24 | 25 | func (e *SlectionExecutor) Init() { 26 | e.child.Init() 27 | } 28 | 29 | func (e *SlectionExecutor) Next() (*tuple.Tuple, Done, error) { 30 | for t, done, err := e.child.Next(); !done; t, done, err = e.child.Next() { 31 | if err != nil { 32 | return nil, done, err 33 | } 34 | if t == nil && done == false { 35 | err := errors.New("e.child.Next returned nil unexpectedly.") 36 | e.context.txn.SetState(access.ABORTED) 37 | return nil, true, err 38 | } 39 | 40 | if !e.selects(t, e.plan.GetPredicate()) { 41 | continue 42 | } 43 | 44 | return t, false, nil 45 | } 46 | 47 | return nil, true, nil 48 | } 49 | 50 | func (e *SlectionExecutor) GetOutputSchema() *schema.Schema { 51 | return e.plan.OutputSchema() 52 | } 53 | 54 | // select evaluates an expression on the tuple 55 | func (e *SlectionExecutor) selects(tuple *tuple.Tuple, predicate expression.Expression) bool { 56 | return predicate == nil || predicate.Evaluate(tuple, e.GetOutputSchema()).ToBoolean() 57 | } 58 | 59 | func (e *SlectionExecutor) GetTableMetaData() *catalog.TableMetadata { 60 | return e.child.GetTableMetaData() 61 | } 62 | -------------------------------------------------------------------------------- /lib/execution/executors/seq_scan_executor.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package executors 5 | 6 | import ( 7 | "errors" 8 | "github.com/ryogrid/SamehadaDB/lib/catalog" 9 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 10 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 11 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 12 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 13 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 14 | "github.com/ryogrid/SamehadaDB/lib/types" 15 | ) 16 | 17 | /** 18 | * SeqScanExecutor executes a sequential scan over a table. 19 | */ 20 | type SeqScanExecutor struct { 21 | context *ExecutorContext 22 | plan *plans.SeqScanPlanNode 23 | tableMetadata *catalog.TableMetadata 24 | it *access.TableHeapIterator 25 | txn *access.Transaction 26 | } 27 | 28 | // NewSeqScanExecutor creates a new sequential executor 29 | func NewSeqScanExecutor(context *ExecutorContext, plan *plans.SeqScanPlanNode) Executor { 30 | tableMetadata := context.GetCatalog().GetTableByOID(plan.GetTableOID()) 31 | 32 | return &SeqScanExecutor{context, plan, tableMetadata, nil, context.GetTransaction()} 33 | } 34 | 35 | func (e *SeqScanExecutor) Init() { 36 | e.it = e.tableMetadata.Table().Iterator(e.txn) 37 | } 38 | 39 | // Next implements the next method for the sequential scan operator 40 | // It uses the table heap iterator to iterate through the table heap 41 | // tyring to find a tuple. It performs selection and projection on-the-fly 42 | func (e *SeqScanExecutor) Next() (*tuple.Tuple, Done, error) { 43 | 44 | // iterates through the table heap trying to select a tuple that matches the predicate 45 | for t := e.it.Current(); !e.it.End(); t = e.it.Next() { 46 | if t == nil { 47 | err := errors.New("e.it.Next returned nil") 48 | return nil, true, err 49 | } 50 | 51 | if e.selects(t, e.plan.GetPredicate()) { 52 | break 53 | } 54 | } 55 | 56 | // if the iterator is not in the end, projects the current tuple into the output schema 57 | if !e.it.End() { 58 | defer e.it.Next() // advances the iterator after projection 59 | ret := e.projects(e.it.Current()) 60 | ret.SetRID(e.it.Current().GetRID()) 61 | return ret, false, nil 62 | } 63 | 64 | return nil, true, nil 65 | } 66 | 67 | // select evaluates an expression on the tuple 68 | func (e *SeqScanExecutor) selects(tuple *tuple.Tuple, predicate expression.Expression) bool { 69 | return predicate == nil || predicate.Evaluate(tuple, e.tableMetadata.Schema()).ToBoolean() 70 | } 71 | 72 | // project applies the projection operator defined by the output schema 73 | func (e *SeqScanExecutor) projects(tuple_ *tuple.Tuple) *tuple.Tuple { 74 | outputSchema := e.plan.OutputSchema() 75 | 76 | values := []types.Value{} 77 | for i := uint32(0); i < outputSchema.GetColumnCount(); i++ { 78 | colName := outputSchema.GetColumns()[i].GetColumnName() 79 | 80 | colIndex := e.tableMetadata.Schema().GetColIndex(colName) 81 | values = append(values, tuple_.GetValue(e.tableMetadata.Schema(), colIndex)) 82 | } 83 | 84 | return tuple.NewTupleFromSchema(values, outputSchema) 85 | } 86 | 87 | func (e *SeqScanExecutor) GetOutputSchema() *schema.Schema { 88 | return e.plan.OutputSchema() 89 | } 90 | 91 | func (e *SeqScanExecutor) GetTableMetaData() *catalog.TableMetadata { 92 | return e.tableMetadata 93 | } 94 | -------------------------------------------------------------------------------- /lib/execution/expression/abstract_expression.go: -------------------------------------------------------------------------------- 1 | package expression 2 | 3 | import "github.com/ryogrid/SamehadaDB/lib/types" 4 | 5 | type AbstractExpression struct { 6 | /** The children of this expression. Note that the order of appearance of children may matter. */ 7 | children [2]Expression 8 | /** The return type of this expression. */ 9 | ret_type types.TypeID 10 | } 11 | -------------------------------------------------------------------------------- /lib/execution/expression/aggregate_value.go: -------------------------------------------------------------------------------- 1 | package expression 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 5 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 6 | "github.com/ryogrid/SamehadaDB/lib/types" 7 | ) 8 | 9 | /** 10 | * AggregateValueExpression represents aggregations such as MAX(a), MIN(b), COUNT(c) 11 | */ 12 | type AggregateValueExpression struct { 13 | *AbstractExpression 14 | is_group_by_term_ bool 15 | term_idx_ uint32 16 | } 17 | 18 | /** 19 | * Creates a new AggregateValueExpression. 20 | * @param is_group_by_term true if this is a group by 21 | * @param term_idx the index of the term 22 | * @param ret_type the return type of the aggregate value expression 23 | */ 24 | 25 | func NewAggregateValueExpression(is_group_by_term bool, term_idx uint32, ret_type types.TypeID) Expression { 26 | return &AggregateValueExpression{&AbstractExpression{[2]Expression{}, ret_type}, is_group_by_term, term_idx} 27 | } 28 | 29 | func (a *AggregateValueExpression) Evaluate(tuple *tuple.Tuple, schema *schema.Schema) types.Value { 30 | panic("Aggregation should only refer to group-by and aggregates.") 31 | } 32 | 33 | func (a *AggregateValueExpression) EvaluateJoin(left_tuple *tuple.Tuple, left_schema *schema.Schema, right_tuple *tuple.Tuple, right_schema *schema.Schema) types.Value { 34 | panic("Aggregation should only refer to group-by and aggregates.") 35 | } 36 | 37 | func (a *AggregateValueExpression) EvaluateAggregate(group_bys []*types.Value, aggregates []*types.Value) types.Value { 38 | if a.is_group_by_term_ { 39 | return *group_bys[a.term_idx_] 40 | } else { 41 | return *aggregates[a.term_idx_] 42 | } 43 | } 44 | 45 | func (a *AggregateValueExpression) GetChildAt(child_idx uint32) Expression { 46 | if int(child_idx) >= len(a.children) { 47 | return nil 48 | } 49 | return a.children[child_idx] 50 | } 51 | 52 | func (a *AggregateValueExpression) GetReturnType() types.TypeID { 53 | return a.ret_type 54 | } 55 | 56 | func (a *AggregateValueExpression) GetType() ExpressionType { 57 | return EXPRESSION_TYPE_AGGREGATE_VALUE 58 | } 59 | -------------------------------------------------------------------------------- /lib/execution/expression/column_value.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package expression 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 9 | "github.com/ryogrid/SamehadaDB/lib/types" 10 | "strings" 11 | ) 12 | 13 | /** 14 | * ColumnValue maintains the tuple index and column index relative to a particular schema or join. 15 | */ 16 | type ColumnValue struct { 17 | *AbstractExpression 18 | tupleIndexForJoin uint32 // Tuple index 0 = left side of join, tuple index 1 = right side of join 19 | colIndex uint32 // Column index refers to the index within the schema of the tuple, e.g. schema {A,B,C} has indexes {0,1,2} 20 | } 21 | 22 | func NewColumnValue(tupleIndex uint32, colIndex uint32, colType types.TypeID) Expression { 23 | return &ColumnValue{&AbstractExpression{[2]Expression{}, colType}, tupleIndex, colIndex} 24 | } 25 | 26 | func (c *ColumnValue) Evaluate(tuple *tuple.Tuple, schema *schema.Schema) types.Value { 27 | return tuple.GetValue(schema, c.colIndex) 28 | } 29 | 30 | func (c *ColumnValue) SetTupleIndex(tupleIndex uint32) { 31 | c.tupleIndexForJoin = tupleIndex 32 | } 33 | 34 | func (c *ColumnValue) SetColIndex(colIndex uint32) { 35 | c.colIndex = colIndex 36 | } 37 | 38 | func (c *ColumnValue) GetColIndex() uint32 { 39 | return c.colIndex 40 | } 41 | 42 | func (c *ColumnValue) EvaluateJoin(left_tuple *tuple.Tuple, left_schema *schema.Schema, right_tuple *tuple.Tuple, right_schema *schema.Schema) types.Value { 43 | if c.tupleIndexForJoin == 0 { 44 | return left_tuple.GetValue(left_schema, c.colIndex) 45 | } else { 46 | return right_tuple.GetValue(right_schema, c.colIndex) 47 | } 48 | } 49 | 50 | func (c *ColumnValue) EvaluateAggregate(group_bys []*types.Value, aggregates []*types.Value) types.Value { 51 | panic("Aggregation should only refer to group-by and aggregates.") 52 | } 53 | 54 | func (c *ColumnValue) GetChildAt(child_idx uint32) Expression { 55 | if int(child_idx) >= len(c.children) { 56 | return nil 57 | } 58 | return c.children[child_idx] 59 | } 60 | 61 | func (c *ColumnValue) GetReturnType() types.TypeID { return c.ret_type } 62 | 63 | func (c *ColumnValue) SetReturnType(valueType types.TypeID) { 64 | c.ret_type = valueType 65 | } 66 | 67 | func MakeColumnValueExpression(schema_ *schema.Schema, tuple_idx_on_join uint32, 68 | col_name string) Expression { 69 | // note: alphabets on column name is stored in lowercase 70 | 71 | col_idx := schema_.GetColIndex(strings.ToLower(col_name)) 72 | col_type := schema_.GetColumn(col_idx).GetType() 73 | col_val := NewColumnValue(tuple_idx_on_join, col_idx, col_type) 74 | return col_val 75 | } 76 | 77 | func (c *ColumnValue) GetType() ExpressionType { 78 | return EXPRESSION_TYPE_COLUMN_VALUE 79 | } 80 | -------------------------------------------------------------------------------- /lib/execution/expression/comparison.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package expression 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 9 | "github.com/ryogrid/SamehadaDB/lib/types" 10 | ) 11 | 12 | type ComparisonType int 13 | 14 | /** ComparisonType represents the type of comparison that we want to perform. */ 15 | const ( 16 | Equal ComparisonType = iota 17 | NotEqual 18 | GreaterThan // A > B 19 | GreaterThanOrEqual // A >= B 20 | LessThan // A < B 21 | LessThanOrEqual // A <= B 22 | ) 23 | 24 | /** 25 | * ComparisonExpression represents two expressions being compared. 26 | */ 27 | type Comparison struct { 28 | *AbstractExpression 29 | comparisonType ComparisonType 30 | } 31 | 32 | func NewComparison(left Expression, right Expression, comparisonType ComparisonType, colType types.TypeID) Expression { 33 | ret := &Comparison{&AbstractExpression{[2]Expression{left, right}, colType}, comparisonType} 34 | return ret 35 | } 36 | 37 | func (c *Comparison) Evaluate(tuple_ *tuple.Tuple, schema_ *schema.Schema) types.Value { 38 | if c == nil { 39 | return types.NewBoolean(false) 40 | } 41 | lhs := c.children[0].Evaluate(tuple_, schema_) 42 | rhs := c.children[1].Evaluate(tuple_, schema_) 43 | return types.NewBoolean(c.performComparison(lhs, rhs)) 44 | } 45 | 46 | func (c *Comparison) performComparison(lhs types.Value, rhs types.Value) bool { 47 | switch c.comparisonType { 48 | case Equal: 49 | return lhs.CompareEquals(rhs) 50 | case NotEqual: 51 | return lhs.CompareNotEquals(rhs) 52 | case GreaterThan: 53 | return lhs.CompareGreaterThan(rhs) 54 | case GreaterThanOrEqual: 55 | return lhs.CompareGreaterThanOrEqual(rhs) 56 | case LessThan: 57 | return lhs.CompareLessThan(rhs) 58 | case LessThanOrEqual: 59 | return lhs.CompareLessThanOrEqual(rhs) 60 | default: 61 | panic("illegal comparisonType is passed!") 62 | } 63 | 64 | } 65 | 66 | func (c *Comparison) GetLeftSideColIdx() uint32 { 67 | return c.children[0].(*ColumnValue).colIndex 68 | } 69 | 70 | func (c *Comparison) GetRightSideValue(tuple_ *tuple.Tuple, schema_ *schema.Schema) types.Value { 71 | return c.children[1].Evaluate(tuple_, schema_) 72 | } 73 | 74 | func (c *Comparison) GetComparisonType() ComparisonType { 75 | return c.comparisonType 76 | } 77 | 78 | func (c *Comparison) EvaluateJoin(left_tuple *tuple.Tuple, left_schema *schema.Schema, right_tuple *tuple.Tuple, right_schema *schema.Schema) types.Value { 79 | lhs := c.GetChildAt(0).EvaluateJoin(left_tuple, left_schema, right_tuple, right_schema) 80 | rhs := c.GetChildAt(1).EvaluateJoin(left_tuple, left_schema, right_tuple, right_schema) 81 | return types.NewBoolean(c.performComparison(lhs, rhs)) 82 | } 83 | 84 | func (c *Comparison) EvaluateAggregate(group_bys []*types.Value, aggregates []*types.Value) types.Value { 85 | lhs := c.GetChildAt(0).EvaluateAggregate(group_bys, aggregates) 86 | rhs := c.GetChildAt(1).EvaluateAggregate(group_bys, aggregates) 87 | return types.NewBoolean(c.performComparison(lhs, rhs)) 88 | } 89 | 90 | func (c *Comparison) GetChildAt(child_idx uint32) Expression { 91 | if int(child_idx) >= len(c.children) { 92 | return nil 93 | } 94 | return c.children[child_idx] 95 | } 96 | 97 | func (c *Comparison) SetChildAt(child_idx uint32, child Expression) { 98 | c.children[child_idx] = child 99 | } 100 | 101 | func (c *Comparison) GetType() ExpressionType { 102 | return EXPRESSION_TYPE_COMPARISON 103 | } 104 | -------------------------------------------------------------------------------- /lib/execution/expression/constant_value.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package expression 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 9 | "github.com/ryogrid/SamehadaDB/lib/types" 10 | ) 11 | 12 | /** 13 | * ConstantValue represents constants. 14 | */ 15 | type ConstantValue struct { 16 | *AbstractExpression 17 | value types.Value 18 | } 19 | 20 | func NewConstantValue(value types.Value, colType types.TypeID) Expression { 21 | return &ConstantValue{&AbstractExpression{[2]Expression{}, colType}, value} 22 | } 23 | 24 | func (c *ConstantValue) Evaluate(tuple *tuple.Tuple, schema *schema.Schema) types.Value { 25 | return c.value 26 | } 27 | 28 | func (c *ConstantValue) EvaluateJoin(left_tuple *tuple.Tuple, left_schema *schema.Schema, right_tuple *tuple.Tuple, right_schema *schema.Schema) types.Value { 29 | return c.value 30 | } 31 | 32 | func (c *ConstantValue) EvaluateAggregate(group_bys []*types.Value, aggregates []*types.Value) types.Value { 33 | return c.value 34 | } 35 | 36 | func (c *ConstantValue) GetChildAt(child_idx uint32) Expression { 37 | if int(child_idx) >= len(c.children) { 38 | return nil 39 | } 40 | return c.children[child_idx] 41 | } 42 | 43 | func (c *ConstantValue) GetType() ExpressionType { 44 | return EXPRESSION_TYPE_CONSTANT_VALUE 45 | } 46 | 47 | func (c *ConstantValue) GetValue() *types.Value { 48 | return &c.value 49 | } 50 | -------------------------------------------------------------------------------- /lib/execution/expression/expression.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package expression 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 9 | "github.com/ryogrid/SamehadaDB/lib/types" 10 | ) 11 | 12 | type ExpressionType int 13 | 14 | const ( 15 | EXPRESSION_TYPE_INVALID ExpressionType = iota 16 | EXPRESSION_TYPE_AGGREGATE_VALUE 17 | EXPRESSION_TYPE_COMPARISON 18 | EXPRESSION_TYPE_COLUMN_VALUE 19 | EXPRESSION_TYPE_CONSTANT_VALUE 20 | EXPRESSION_TYPE_LOGICAL_OP 21 | ) 22 | 23 | /** 24 | * Expression interface is the base of all the expressions in the system. 25 | * Expressions are modeled as trees, i.e. every expression may have a variable number of children. 26 | */ 27 | type Expression interface { 28 | Evaluate(*tuple.Tuple, *schema.Schema) types.Value 29 | GetChildAt(uint32) Expression 30 | EvaluateJoin(*tuple.Tuple, *schema.Schema, *tuple.Tuple, *schema.Schema) types.Value 31 | EvaluateAggregate([]*types.Value, []*types.Value) types.Value 32 | GetType() ExpressionType 33 | } 34 | -------------------------------------------------------------------------------- /lib/execution/expression/expression_util.go: -------------------------------------------------------------------------------- 1 | package expression 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/types" 5 | "math" 6 | "strconv" 7 | ) 8 | 9 | // get string of "Reverse Polish Notation" style 10 | func GetExpTreeStr(node interface{}) string { 11 | retStr := "" 12 | 13 | childTraverse := func(exp Expression) string { 14 | var idx uint32 = 0 15 | var tmpStr = "" 16 | for exp.GetChildAt(idx) != nil && idx < math.MaxInt32 { 17 | child := exp.GetChildAt(idx) 18 | tmpStr += GetExpTreeStr(child) 19 | idx++ 20 | } 21 | return tmpStr 22 | } 23 | 24 | switch typedNode := node.(type) { 25 | case *Comparison: 26 | retStr += childTraverse(typedNode) 27 | switch typedNode.comparisonType { 28 | case Equal: 29 | retStr += "= " 30 | case NotEqual: 31 | retStr += "!= " 32 | case GreaterThan: // A > B 33 | retStr += "> " 34 | case GreaterThanOrEqual: // A >= B 35 | retStr += ">= " 36 | case LessThan: // A < B 37 | retStr += "< " 38 | case LessThanOrEqual: // A <= B 39 | retStr += "<= " 40 | default: 41 | panic("illegal comparisonType!") 42 | } 43 | return retStr 44 | case *LogicalOp: 45 | retStr += childTraverse(typedNode) 46 | switch typedNode.logicalOpType { 47 | case AND: 48 | retStr += "AND " 49 | case OR: 50 | retStr += "OR " 51 | case NOT: 52 | retStr += "NOT " 53 | default: 54 | panic("illegal logicalOpType!") 55 | } 56 | return retStr 57 | case *AggregateValueExpression: 58 | panic("AggregateValueExpression is not implemented yet!") 59 | case *ConstantValue: 60 | return typedNode.value.ToString() + " " 61 | case *ColumnValue: 62 | return "colIndex:" + strconv.Itoa(int(typedNode.GetColIndex())) + " " 63 | case *types.Value: 64 | return typedNode.ToString() + "" 65 | default: 66 | panic("illegal type expression object is passed!") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/execution/expression/loggical_op.go: -------------------------------------------------------------------------------- 1 | package expression 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ryogrid/SamehadaDB/lib/samehada/samehada_util" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | ) 10 | 11 | type LogicalOpType int 12 | 13 | /** LogicalOpType represents the type of comparison that we want to perform. */ 14 | const ( 15 | AND LogicalOpType = iota 16 | OR 17 | NOT 18 | ) 19 | 20 | /** 21 | * LogicalOp represents two expressions or one expression being evaluated with logical operator. 22 | */ 23 | type LogicalOp struct { 24 | *AbstractExpression 25 | logicalOpType LogicalOpType 26 | children_left Expression // referenced as children[0] after struct init... 27 | children_right Expression // referenced as children[1] after struct init... 28 | } 29 | 30 | // if logicalOpType is "NOT", right value must be nil 31 | func NewLogicalOp(left Expression, right Expression, logicalOpType LogicalOpType, colType types.TypeID) Expression { 32 | ret := &LogicalOp{&AbstractExpression{[2]Expression{left, right}, colType}, logicalOpType, left, right} 33 | ret.SetChildAt(0, left) 34 | ret.SetChildAt(1, right) 35 | return ret 36 | } 37 | 38 | func (c *LogicalOp) Evaluate(tuple *tuple.Tuple, schema *schema.Schema) types.Value { 39 | if c.logicalOpType == NOT { 40 | lhs := c.children[0].Evaluate(tuple, schema) 41 | return types.NewBoolean(!lhs.ToBoolean()) 42 | } else { 43 | lhs := c.children[0].Evaluate(tuple, schema) 44 | rhs := c.children[1].Evaluate(tuple, schema) 45 | return types.NewBoolean(c.performLogicalOp(lhs, rhs)) 46 | } 47 | } 48 | 49 | func (c *LogicalOp) performLogicalOp(lhs types.Value, rhs types.Value) bool { 50 | switch c.logicalOpType { 51 | case AND: 52 | return lhs.ToBoolean() && rhs.ToBoolean() 53 | case OR: 54 | return lhs.ToBoolean() || rhs.ToBoolean() 55 | case NOT: 56 | fmt.Println(c.logicalOpType) 57 | panic("NOT op is not valid!") 58 | default: 59 | fmt.Println(c.logicalOpType) 60 | panic("unknown logicalOpType is passed!") 61 | } 62 | } 63 | 64 | func (c *LogicalOp) GetLogicalOpType() LogicalOpType { 65 | return c.logicalOpType 66 | } 67 | 68 | func (c *LogicalOp) EvaluateJoin(left_tuple *tuple.Tuple, left_schema *schema.Schema, right_tuple *tuple.Tuple, right_schema *schema.Schema) types.Value { 69 | if c.logicalOpType == NOT { 70 | lhs := c.GetChildAt(0).EvaluateJoin(left_tuple, left_schema, right_tuple, left_schema) 71 | return types.NewBoolean(!lhs.ToBoolean()) 72 | } else { 73 | lhs := c.GetChildAt(0).EvaluateJoin(left_tuple, left_schema, right_tuple, left_schema) 74 | rhs := c.GetChildAt(1).EvaluateJoin(left_tuple, left_schema, right_tuple, right_schema) 75 | return types.NewBoolean(c.performLogicalOp(lhs, rhs)) 76 | } 77 | } 78 | 79 | func (c *LogicalOp) EvaluateAggregate(group_bys []*types.Value, aggregates []*types.Value) types.Value { 80 | if c.logicalOpType == NOT { 81 | lhs := c.GetChildAt(0).EvaluateAggregate(group_bys, aggregates) 82 | return types.NewBoolean(!lhs.ToBoolean()) 83 | } else { 84 | lhs := c.GetChildAt(0).EvaluateAggregate(group_bys, aggregates) 85 | rhs := c.GetChildAt(1).EvaluateAggregate(group_bys, aggregates) 86 | return types.NewBoolean(c.performLogicalOp(lhs, rhs)) 87 | } 88 | } 89 | 90 | func (c *LogicalOp) GetChildAt(child_idx uint32) Expression { 91 | if int(child_idx) >= len(c.children) { 92 | return nil 93 | } 94 | return c.children[child_idx] 95 | } 96 | 97 | func (c *LogicalOp) SetChildAt(child_idx uint32, child Expression) { 98 | c.children[child_idx] = child 99 | } 100 | 101 | func AppendLogicalCondition(baseConds Expression, opType LogicalOpType, addCond Expression) Expression { 102 | samehada_util.SHAssert(opType == AND, "only AND is supported") 103 | return NewLogicalOp(baseConds, addCond, opType, types.Boolean) 104 | } 105 | 106 | func (c *LogicalOp) GetType() ExpressionType { 107 | return EXPRESSION_TYPE_LOGICAL_OP 108 | } 109 | -------------------------------------------------------------------------------- /lib/execution/plans/delete.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | ) 6 | 7 | /** 8 | * DeletePlanNode identifies a table and conditions specify record to be deleted. 9 | */ 10 | type DeletePlanNode struct { 11 | *AbstractPlanNode 12 | stats_ *catalog.TableStatistics 13 | } 14 | 15 | func NewDeletePlanNode(child Plan) Plan { 16 | return &DeletePlanNode{&AbstractPlanNode{nil, []Plan{child}}, child.GetStatistics().GetDeepCopy()} 17 | } 18 | 19 | func (p *DeletePlanNode) GetTableOID() uint32 { 20 | return p.children[0].GetTableOID() 21 | } 22 | 23 | func (p *DeletePlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 24 | return p.children[0].EmitRowCount(c) 25 | } 26 | 27 | func (p *DeletePlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 28 | return p.children[0].EmitRowCount(c) 29 | } 30 | 31 | func (p *DeletePlanNode) GetDebugStr() string { 32 | // TODO: (SDB) [OPT] not implemented yet (DeletePlanNode::GetDebugStr) 33 | panic("not implemented yet") 34 | } 35 | 36 | func (p *DeletePlanNode) GetStatistics() *catalog.TableStatistics { 37 | return p.stats_ 38 | } 39 | 40 | func (p *DeletePlanNode) GetType() PlanType { 41 | return Delete 42 | } 43 | -------------------------------------------------------------------------------- /lib/execution/plans/index_join.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/common" 6 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "math" 9 | ) 10 | 11 | type IndexJoinPlanNode struct { 12 | *AbstractPlanNode 13 | onPredicate expression.Expression 14 | rigthTableOID uint32 15 | rightOutSchema *schema.Schema 16 | stats_ *catalog.TableStatistics 17 | } 18 | 19 | func GenIndexJoinStats(c *catalog.Catalog, leftPlan Plan, rightTableOID uint32) *catalog.TableStatistics { 20 | leftStats := leftPlan.GetStatistics().GetDeepCopy() 21 | tm := c.GetTableByOID(rightTableOID) 22 | leftStats.Concat(tm.GetStatistics().GetDeepCopy()) 23 | return leftStats 24 | } 25 | 26 | func NewIndexJoinPlanNode(c *catalog.Catalog, leftChild Plan, leftKeys []expression.Expression, rightOutSchema *schema.Schema, rightTblOID uint32, rightKeys []expression.Expression) *IndexJoinPlanNode { 27 | if leftKeys == nil || rightKeys == nil { 28 | panic("NewIndexJoinPlanNode needs keys info.") 29 | } 30 | if len(leftKeys) != 1 || len(rightKeys) != 1 { 31 | panic("NewIndexJoinPlanNode supports only one key for left and right now.") 32 | } 33 | 34 | outputSchema := makeMergedOutputSchema(leftChild.OutputSchema(), rightOutSchema) 35 | onPredicate := constructOnExpressionFromKeysInfo(leftKeys, rightKeys) 36 | return &IndexJoinPlanNode{&AbstractPlanNode{outputSchema, []Plan{leftChild}}, onPredicate, rightTblOID, rightOutSchema, GenIndexJoinStats(c, leftChild, rightTblOID)} 37 | } 38 | 39 | func (p *IndexJoinPlanNode) GetLeftPlan() Plan { 40 | common.SH_Assert(len(p.GetChildren()) == 1, "Index joins should have exactly one children plans.") 41 | return p.GetChildAt(0) 42 | } 43 | 44 | func (p *IndexJoinPlanNode) GetRightPlan() Plan { 45 | panic("IndexJoinPlanNode::GetRightPlan() should not be called.") 46 | } 47 | 48 | func (p *IndexJoinPlanNode) GetType() PlanType { return IndexJoin } 49 | 50 | func (p *IndexJoinPlanNode) OnPredicate() expression.Expression { return p.onPredicate } 51 | 52 | // can not be used 53 | func (p *IndexJoinPlanNode) GetTableOID() uint32 { 54 | return math.MaxUint32 55 | } 56 | 57 | func (p *IndexJoinPlanNode) GetRightTableOID() uint32 { 58 | return p.rigthTableOID 59 | } 60 | 61 | func (p *IndexJoinPlanNode) getRightTableRows(c *catalog.Catalog) uint64 { 62 | tm := c.GetTableByOID(p.rigthTableOID) 63 | return tm.GetStatistics().Rows() 64 | } 65 | 66 | func (p *IndexJoinPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 67 | return p.GetLeftPlan().AccessRowCount(c) * 3 68 | } 69 | 70 | func (p *IndexJoinPlanNode) GetDebugStr() string { 71 | leftColIdx := p.onPredicate.GetChildAt(0).(*expression.ColumnValue).GetColIndex() 72 | leftColName := p.GetChildAt(0).OutputSchema().GetColumn(leftColIdx).GetColumnName() 73 | rightColIdx := p.onPredicate.GetChildAt(1).(*expression.ColumnValue).GetColIndex() 74 | rightColName := p.rightOutSchema.GetColumn(rightColIdx).GetColumnName() 75 | return "IndexJoinPlanNode [" + leftColName + " = " + rightColName + "]" 76 | } 77 | 78 | func (p *IndexJoinPlanNode) GetStatistics() *catalog.TableStatistics { 79 | return p.stats_ 80 | } 81 | 82 | func (p *IndexJoinPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 83 | return uint64(math.Min(float64(p.GetLeftPlan().EmitRowCount(c)), float64(p.getRightTableRows(c)))) 84 | } 85 | -------------------------------------------------------------------------------- /lib/execution/plans/insert.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package plans 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | ) 10 | 11 | /** 12 | * InsertPlanNode identifies a table that should be inserted into. 13 | * The values to be inserted are either embedded into the InsertPlanNode itself, i.e. a "raw insert", 14 | * or will come from the child of the InsertPlanNode. To simplify the assignment, InsertPlanNode has at most one child. 15 | */ 16 | type InsertPlanNode struct { 17 | *AbstractPlanNode 18 | rawValues [][]types.Value 19 | tableOID uint32 20 | } 21 | 22 | // NewInsertPlanNode creates a new insert plan node for inserting raw values 23 | func NewInsertPlanNode(rawValues [][]types.Value, oid uint32) Plan { 24 | return &InsertPlanNode{&AbstractPlanNode{nil, nil}, rawValues, oid} 25 | } 26 | 27 | // GetTableOID returns the identifier of the table that should be inserted into 28 | func (p *InsertPlanNode) GetTableOID() uint32 { 29 | return p.tableOID 30 | } 31 | 32 | // GetRawValues returns the raw values to be inserted 33 | func (p *InsertPlanNode) GetRawValues() [][]types.Value { 34 | return p.rawValues 35 | } 36 | 37 | func (p *InsertPlanNode) GetType() PlanType { 38 | return Insert 39 | } 40 | 41 | func (p *InsertPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 42 | return uint64(len(p.rawValues)) 43 | } 44 | 45 | func (p *InsertPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 46 | return uint64(len(p.rawValues)) 47 | } 48 | 49 | func (p *InsertPlanNode) GetStatistics() *catalog.TableStatistics { 50 | panic("not collable!") 51 | } 52 | 53 | func (p *InsertPlanNode) GetDebugStr() string { 54 | // TODO: (SDB) [OPT] not implemented yet (InsertPlanNode::GetDebugStr) 55 | panic("not implemented yet") 56 | } 57 | -------------------------------------------------------------------------------- /lib/execution/plans/limit.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package plans 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | ) 9 | 10 | type LimitPlanNode struct { 11 | *AbstractPlanNode 12 | limit uint32 13 | offset uint32 14 | stats_ *catalog.TableStatistics 15 | } 16 | 17 | func NewLimitPlanNode(child Plan, limit uint32, offset uint32) Plan { 18 | return &LimitPlanNode{&AbstractPlanNode{nil, []Plan{child}}, limit, offset, child.GetStatistics().GetDeepCopy()} 19 | } 20 | 21 | func (p *LimitPlanNode) GetLimit() uint32 { 22 | return p.limit 23 | } 24 | 25 | func (p *LimitPlanNode) GetOffset() uint32 { 26 | return p.offset 27 | } 28 | 29 | func (p *LimitPlanNode) GetType() PlanType { 30 | return Limit 31 | } 32 | 33 | func (p *LimitPlanNode) GetTableOID() uint32 { 34 | return p.children[0].GetTableOID() 35 | } 36 | 37 | func (p *LimitPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 38 | return p.children[0].AccessRowCount(c) 39 | } 40 | 41 | func (p *LimitPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 42 | return uint64(p.limit) 43 | } 44 | 45 | func (p *LimitPlanNode) GetDebugStr() string { 46 | // TODO: (SDB) [OPT] not implemented yet (LimitPlanNode::GetDebugStr) 47 | panic("not implemented yet") 48 | } 49 | 50 | func (p *LimitPlanNode) GetStatistics() *catalog.TableStatistics { 51 | return p.stats_ 52 | } 53 | -------------------------------------------------------------------------------- /lib/execution/plans/nested_loop_join.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/common" 6 | "math" 7 | ) 8 | 9 | type NestedLoopJoinPlanNode struct { 10 | *AbstractPlanNode 11 | stats_ *catalog.TableStatistics 12 | } 13 | 14 | func GenNestedLoopJoinStats(leftPlan Plan, rightPlan Plan) *catalog.TableStatistics { 15 | leftStats := leftPlan.GetStatistics().GetDeepCopy() 16 | leftStats.Multiply(float64(leftStats.Rows())) 17 | rightStats := rightPlan.GetStatistics().GetDeepCopy() 18 | rightStats.Multiply(float64(rightStats.Rows())) 19 | leftStats.Concat(rightStats) 20 | return leftStats 21 | } 22 | 23 | // used only for Cross Join 24 | func NewNestedLoopJoinPlanNode(children []Plan) *NestedLoopJoinPlanNode { 25 | return &NestedLoopJoinPlanNode{ 26 | &AbstractPlanNode{makeMergedOutputSchema(children[0].OutputSchema(), children[1].OutputSchema()), children}, 27 | GenNestedLoopJoinStats(children[0], children[1])} 28 | } 29 | 30 | func (p *NestedLoopJoinPlanNode) GetType() PlanType { return NestedLoopJoin } 31 | 32 | func (p *NestedLoopJoinPlanNode) GetLeftPlan() Plan { 33 | common.SH_Assert(len(p.GetChildren()) == 2, "nested loop joins should have exactly two children plans.") 34 | return p.GetChildAt(0) 35 | } 36 | 37 | func (p *NestedLoopJoinPlanNode) GetRightPlan() Plan { 38 | common.SH_Assert(len(p.GetChildren()) == 2, "nested loop joins should have exactly two children plans.") 39 | return p.GetChildAt(1) 40 | } 41 | 42 | // can not be used 43 | func (p *NestedLoopJoinPlanNode) GetTableOID() uint32 { 44 | return math.MaxUint32 45 | } 46 | 47 | func (p *NestedLoopJoinPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 48 | return p.GetLeftPlan().AccessRowCount(c) + 49 | (1 + p.GetLeftPlan().EmitRowCount(c)*p.GetRightPlan().AccessRowCount(c)) 50 | } 51 | 52 | func (p *NestedLoopJoinPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 53 | return p.GetLeftPlan().EmitRowCount(c) * p.GetRightPlan().EmitRowCount(c) 54 | } 55 | 56 | func (p *NestedLoopJoinPlanNode) GetStatistics() *catalog.TableStatistics { 57 | return p.stats_ 58 | } 59 | 60 | func (p *NestedLoopJoinPlanNode) GetDebugStr() string { 61 | return "NestedLoopJoinPlanNode" 62 | } 63 | -------------------------------------------------------------------------------- /lib/execution/plans/orderby.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/common" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 7 | ) 8 | 9 | // /** OrderbyType enumerates all the possible aggregation functions in our system. */ 10 | type OrderbyType int32 11 | 12 | /** The type of the sort order. */ 13 | const ( 14 | ASC OrderbyType = iota 15 | DESC 16 | ) 17 | 18 | /** 19 | * OrderbyPlanNode represents the ORDER BY clause of SQL. 20 | */ 21 | type OrderbyPlanNode struct { 22 | *AbstractPlanNode 23 | col_idxs_ []int 24 | orderby_types_ []OrderbyType 25 | stats_ *catalog.TableStatistics 26 | } 27 | 28 | /** 29 | * Creates a new OrderbyPlanNode. 30 | * @param output_schema the output format of this plan node. it is same with output schema of child 31 | * @param child the child plan to sort data over 32 | * @param col_idxs the specified columns idx at ORDER BY clause 33 | * @param order_types the order types of sorting with specifed columns 34 | */ 35 | func NewOrderbyPlanNode(child_schema *schema.Schema, child Plan, col_idxs []int, 36 | order_types []OrderbyType) *OrderbyPlanNode { 37 | return &OrderbyPlanNode{&AbstractPlanNode{child_schema, []Plan{child}}, col_idxs, order_types, child.GetStatistics().GetDeepCopy()} 38 | } 39 | 40 | func (p *OrderbyPlanNode) GetType() PlanType { return Orderby } 41 | 42 | /** @return the child of this aggregation plan node */ 43 | func (p *OrderbyPlanNode) GetChildPlan() Plan { 44 | common.SH_Assert(len(p.GetChildren()) == 1, "OrderBy expected to only have one child.") 45 | return p.GetChildAt(0) 46 | } 47 | 48 | func (p *OrderbyPlanNode) GetChildAt(childIndex uint32) Plan { 49 | return p.children[childIndex] 50 | } 51 | 52 | func (p *OrderbyPlanNode) GetChildren() []Plan { 53 | return p.children 54 | } 55 | 56 | /** @return the idx'th group by expression */ 57 | func (p *OrderbyPlanNode) GetColIdxAt(idx uint32) int { 58 | return p.col_idxs_[idx] 59 | } 60 | 61 | /** @return column indexes to deside sort order */ 62 | func (p *OrderbyPlanNode) GetColIdxs() []int { return p.col_idxs_ } 63 | 64 | /** @return the Order type ASC or DESC */ 65 | func (p *OrderbyPlanNode) GetOrderbyTypes() []OrderbyType { return p.orderby_types_ } 66 | 67 | func (p *OrderbyPlanNode) GetTableOID() uint32 { 68 | return p.children[0].GetTableOID() 69 | } 70 | 71 | func (p *OrderbyPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 72 | return p.children[0].AccessRowCount(c) 73 | } 74 | 75 | func (p *OrderbyPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 76 | return p.children[0].EmitRowCount(c) 77 | } 78 | 79 | func (p *OrderbyPlanNode) GetDebugStr() string { 80 | // TODO: (SDB) [OPT] not implemented yet (OrderbyPlanNode::GetDebugStr) 81 | panic("not implemented yet") 82 | } 83 | 84 | func (p *OrderbyPlanNode) GetStatistics() *catalog.TableStatistics { 85 | return p.stats_ 86 | } 87 | -------------------------------------------------------------------------------- /lib/execution/plans/plan.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package plans 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 9 | ) 10 | 11 | type PlanType int 12 | 13 | /** PlanType represents the types of plans that we have in our system. */ 14 | const ( 15 | SeqScan PlanType = iota 16 | Insert 17 | Delete 18 | Limit 19 | IndexPointScan 20 | IndexRangeScan 21 | NestedLoopJoin 22 | HashJoin 23 | IndexJoin 24 | Aggregation 25 | Orderby 26 | Projection 27 | Selection 28 | ) 29 | 30 | type Plan interface { 31 | OutputSchema() *schema.Schema 32 | GetChildAt(childIndex uint32) Plan 33 | GetChildren() []Plan 34 | GetType() PlanType 35 | GetTableOID() uint32 36 | AccessRowCount(*catalog.Catalog) uint64 37 | EmitRowCount(*catalog.Catalog) uint64 38 | GetStatistics() *catalog.TableStatistics 39 | // debugging utility func 40 | // returns a string representation of the plan tree rooted at this node 41 | GetDebugStr() string 42 | } 43 | 44 | /** 45 | * AbstractPlanNode represents all the possible types of plan nodes in our system. 46 | * Plan nodes are modeled as trees, so each plan node can have a variable number of children. 47 | * Per the Volcano model, the plan node receives the tuples of its children. 48 | * The ordering of the children may matter. 49 | */ 50 | type AbstractPlanNode struct { 51 | /** 52 | * The schema for the output of this plan node. In the volcano model, every plan node will spit out tuples, 53 | * and this tells you what schema this plan node's tuples will have. 54 | */ 55 | outputSchema *schema.Schema 56 | children []Plan 57 | } 58 | 59 | func (p *AbstractPlanNode) GetChildAt(childIndex uint32) Plan { 60 | return p.children[childIndex] 61 | } 62 | 63 | func (p *AbstractPlanNode) GetChildren() []Plan { 64 | return p.children 65 | } 66 | 67 | func (p *AbstractPlanNode) OutputSchema() *schema.Schema { 68 | return p.outputSchema 69 | } 70 | -------------------------------------------------------------------------------- /lib/execution/plans/plan_util.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/table/column" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | ) 10 | 11 | func makeMergedOutputSchema(left_schema *schema.Schema, right_schema *schema.Schema) *schema.Schema { 12 | var ret *schema.Schema 13 | columns := make([]*column.Column, 0) 14 | for _, col := range left_schema.GetColumns() { 15 | col_ := *col 16 | col_.SetIsLeft(true) 17 | columns = append(columns, &col_) 18 | } 19 | for _, col := range right_schema.GetColumns() { 20 | col_ := *col 21 | col_.SetIsLeft(false) 22 | columns = append(columns, &col_) 23 | } 24 | ret = schema.NewSchema(columns) 25 | return ret 26 | } 27 | 28 | func constructOnExpressionFromKeysInfo(leftKeys []expression.Expression, rightKeys []expression.Expression) expression.Expression { 29 | if len(leftKeys) != 1 || len(rightKeys) != 1 { 30 | panic("constructOnExpressionFromKeysInfo supports only one key for left and right now.") 31 | } 32 | 33 | return expression.NewComparison(leftKeys[0], rightKeys[0], expression.Equal, types.Boolean) 34 | } 35 | 36 | func PrintPlanTree(plan Plan, indent int) { 37 | for ii := 0; ii < indent; ii++ { 38 | fmt.Print(" ") 39 | } 40 | fmt.Print(plan.GetDebugStr()) 41 | fmt.Println("") 42 | 43 | for _, child := range plan.GetChildren() { 44 | PrintPlanTree(child, indent+2) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/execution/plans/point_scan_with_index.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package plans 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 10 | "math" 11 | ) 12 | 13 | /** 14 | * PointScanWithIndexPlanNode use hash index to filter rows matches predicate. 15 | */ 16 | type PointScanWithIndexPlanNode struct { 17 | *AbstractPlanNode 18 | predicate *expression.Comparison 19 | tableOID uint32 20 | stats_ *catalog.TableStatistics 21 | } 22 | 23 | func NewPointScanWithIndexPlanNode(c *catalog.Catalog, schema *schema.Schema, predicate *expression.Comparison, tableOID uint32) Plan { 24 | tm := c.GetTableByOID(tableOID) 25 | return &PointScanWithIndexPlanNode{&AbstractPlanNode{schema, nil}, predicate, tableOID, tm.GetStatistics().GetDeepCopy()} 26 | } 27 | 28 | func (p *PointScanWithIndexPlanNode) GetPredicate() *expression.Comparison { 29 | return p.predicate 30 | } 31 | 32 | func (p *PointScanWithIndexPlanNode) GetTableOID() uint32 { 33 | return p.tableOID 34 | } 35 | 36 | func (p *PointScanWithIndexPlanNode) GetType() PlanType { 37 | return IndexPointScan 38 | } 39 | 40 | func (p *PointScanWithIndexPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 41 | return p.EmitRowCount(c) 42 | } 43 | 44 | func (p *PointScanWithIndexPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 45 | tm := c.GetTableByOID(p.tableOID) 46 | return uint64(math.Ceil( 47 | float64(tm.GetStatistics().Rows()) / 48 | tm.GetStatistics().ReductionFactor(tm.Schema(), p.predicate))) 49 | } 50 | 51 | func (p *PointScanWithIndexPlanNode) GetDebugStr() string { 52 | // TODO: (SDB) [OPT] not implemented yet (PointScanWithIndexPlanNode::GetDebugStr) 53 | 54 | outColNames := "[" 55 | for _, col := range p.OutputSchema().GetColumns() { 56 | outColNames += col.GetColumnName() + ", " 57 | } 58 | return "PointScanWithIndexPlanNode " + outColNames + "]" 59 | } 60 | 61 | func (p *PointScanWithIndexPlanNode) GetStatistics() *catalog.TableStatistics { 62 | return p.stats_ 63 | } 64 | -------------------------------------------------------------------------------- /lib/execution/plans/projection.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 6 | "math" 7 | ) 8 | 9 | type ProjectionPlanNode struct { 10 | *AbstractPlanNode 11 | stats_ *catalog.TableStatistics 12 | } 13 | 14 | func NewProjectionPlanNode(child Plan, projectColumns *schema.Schema) Plan { 15 | return &ProjectionPlanNode{&AbstractPlanNode{projectColumns, []Plan{child}}, child.GetStatistics().GetDeepCopy()} 16 | } 17 | 18 | func (p *ProjectionPlanNode) GetType() PlanType { 19 | return Projection 20 | } 21 | 22 | func (p *ProjectionPlanNode) GetTableOID() uint32 { 23 | return math.MaxInt32 24 | } 25 | 26 | func (p *ProjectionPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 27 | return p.children[0].AccessRowCount(c) 28 | } 29 | 30 | func (p *ProjectionPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 31 | return p.children[0].EmitRowCount(c) 32 | } 33 | 34 | func (p *ProjectionPlanNode) GetDebugStr() string { 35 | projColNames := "[" 36 | for _, col := range p.outputSchema.GetColumns() { 37 | projColNames += col.GetColumnName() + ", " 38 | } 39 | return "ProjectionPlanNode " + projColNames + "]" 40 | } 41 | 42 | func (p *ProjectionPlanNode) GetStatistics() *catalog.TableStatistics { 43 | return p.stats_ 44 | } 45 | -------------------------------------------------------------------------------- /lib/execution/plans/range_scan_with_index.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package plans 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 10 | "github.com/ryogrid/SamehadaDB/lib/types" 11 | "math" 12 | "strconv" 13 | ) 14 | 15 | /** 16 | * RangeScanWithIndexPlanNode use hash index to filter rows matches predicate. 17 | */ 18 | type RangeScanWithIndexPlanNode struct { 19 | *AbstractPlanNode 20 | predicate expression.Expression 21 | tableOID uint32 22 | colIdx int32 // column idx which has index to be used 23 | startRange *types.Value 24 | endRange *types.Value 25 | stats_ *catalog.TableStatistics 26 | } 27 | 28 | func NewRangeScanWithIndexPlanNode(c *catalog.Catalog, schema *schema.Schema, tableOID uint32, colIdx int32, predicate expression.Expression, startRange *types.Value, endRange *types.Value) Plan { 29 | tm := c.GetTableByOID(tableOID) 30 | ret := &RangeScanWithIndexPlanNode{&AbstractPlanNode{schema, nil}, predicate, tableOID, colIdx, startRange, endRange, tm.GetStatistics().GetDeepCopy()} 31 | if startRange != nil && endRange != nil { 32 | // when caller is optimizer, both startRange and endRange are not nil 33 | // when not optimizer, this call is not needed 34 | ret.stats_ = ret.stats_.TransformBy(colIdx, startRange, endRange) 35 | } 36 | return ret 37 | } 38 | 39 | func (p *RangeScanWithIndexPlanNode) GetPredicate() expression.Expression { 40 | return p.predicate 41 | } 42 | 43 | func (p *RangeScanWithIndexPlanNode) GetTableOID() uint32 { 44 | return p.tableOID 45 | } 46 | 47 | func (p *RangeScanWithIndexPlanNode) GetColIdx() int32 { 48 | return p.colIdx 49 | } 50 | 51 | func (p *RangeScanWithIndexPlanNode) GetStartRange() *types.Value { 52 | return p.startRange 53 | } 54 | 55 | func (p *RangeScanWithIndexPlanNode) GetEndRange() *types.Value { 56 | return p.endRange 57 | } 58 | 59 | func (p *RangeScanWithIndexPlanNode) GetType() PlanType { 60 | return IndexRangeScan 61 | } 62 | 63 | func (p *RangeScanWithIndexPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 64 | return p.EmitRowCount(c) 65 | } 66 | 67 | func (p *RangeScanWithIndexPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 68 | return uint64(math.Ceil(c.GetTableByOID(p.tableOID).GetStatistics().EstimateCount(p.colIdx, p.startRange, p.endRange))) 69 | } 70 | 71 | func (p *RangeScanWithIndexPlanNode) GetDebugStr() string { 72 | outColNames := "[" 73 | for _, col := range p.OutputSchema().GetColumns() { 74 | outColNames += col.GetColumnName() + ", " 75 | } 76 | return "RangeScanWithIndexPlanNode " + outColNames + "] " + "type:" + strconv.Itoa(int(p.startRange.ValueType())) + " start:" + p.startRange.ToString() + " end:" + p.endRange.ToString() 77 | } 78 | 79 | func (p *RangeScanWithIndexPlanNode) GetStatistics() *catalog.TableStatistics { 80 | return p.stats_ 81 | } 82 | -------------------------------------------------------------------------------- /lib/execution/plans/selection.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 6 | "math" 7 | ) 8 | 9 | // do selection according to WHERE clause for Plan(Executor) which has no selection functionality 10 | 11 | type SelectionPlanNode struct { 12 | *AbstractPlanNode 13 | predicate expression.Expression 14 | stats_ *catalog.TableStatistics 15 | } 16 | 17 | func NewSelectionPlanNode(child Plan, predicate expression.Expression) Plan { 18 | childOutSchema := child.OutputSchema() 19 | return &SelectionPlanNode{&AbstractPlanNode{childOutSchema, []Plan{child}}, predicate, child.GetStatistics().GetDeepCopy()} 20 | } 21 | 22 | func (p *SelectionPlanNode) GetType() PlanType { 23 | return Selection 24 | } 25 | 26 | func (p *SelectionPlanNode) GetPredicate() expression.Expression { 27 | return p.predicate 28 | } 29 | 30 | func (p *SelectionPlanNode) GetTableOID() uint32 { 31 | return p.children[0].GetTableOID() 32 | } 33 | 34 | func (p *SelectionPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 35 | return p.children[0].AccessRowCount(c) 36 | } 37 | 38 | func (p *SelectionPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 39 | return uint64(math.Ceil(float64( 40 | p.children[0].EmitRowCount(c)) / 41 | p.children[0].GetStatistics().ReductionFactor(p.children[0].OutputSchema(), p.predicate))) 42 | } 43 | 44 | func (p *SelectionPlanNode) GetDebugStr() string { 45 | return "SelectionPlanNode [ " + expression.GetExpTreeStr(p.predicate) + "]" 46 | } 47 | 48 | func (p *SelectionPlanNode) GetStatistics() *catalog.TableStatistics { 49 | return p.stats_ 50 | } 51 | -------------------------------------------------------------------------------- /lib/execution/plans/seq_scan.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package plans 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/catalog" 8 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 10 | ) 11 | 12 | /** 13 | * SeqScanPlanNode identifies a table that should be scanned with an optional predicate. 14 | */ 15 | type SeqScanPlanNode struct { 16 | *AbstractPlanNode 17 | predicate expression.Expression 18 | tableOID uint32 19 | stats_ *catalog.TableStatistics 20 | } 21 | 22 | func NewSeqScanPlanNode(c *catalog.Catalog, schema *schema.Schema, predicate expression.Expression, tableOID uint32) Plan { 23 | tm := c.GetTableByOID(tableOID) 24 | return &SeqScanPlanNode{&AbstractPlanNode{schema, nil}, predicate, tableOID, tm.GetStatistics().GetDeepCopy()} 25 | } 26 | 27 | func (p *SeqScanPlanNode) GetPredicate() expression.Expression { 28 | return p.predicate 29 | } 30 | 31 | func (p *SeqScanPlanNode) GetTableOID() uint32 { 32 | return p.tableOID 33 | } 34 | 35 | func (p *SeqScanPlanNode) GetType() PlanType { 36 | return SeqScan 37 | } 38 | 39 | func (p *SeqScanPlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 40 | return c.GetTableByOID(p.GetTableOID()).GetStatistics().Rows() 41 | } 42 | 43 | func (p *SeqScanPlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 44 | // assumption: selection with predicate is not used 45 | return p.AccessRowCount(c) 46 | } 47 | 48 | func (p *SeqScanPlanNode) GetDebugStr() string { 49 | outColNames := "[" 50 | for _, col := range p.OutputSchema().GetColumns() { 51 | outColNames += col.GetColumnName() + ", " 52 | } 53 | 54 | return "SeqScanPlanNode " + outColNames + "]" 55 | } 56 | 57 | func (p *SeqScanPlanNode) GetStatistics() *catalog.TableStatistics { 58 | return p.stats_ 59 | } 60 | -------------------------------------------------------------------------------- /lib/execution/plans/update.go: -------------------------------------------------------------------------------- 1 | package plans 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/types" 6 | ) 7 | 8 | /** 9 | * UpdatePlanNode identifies a table and conditions specify record to be deleted. 10 | */ 11 | type UpdatePlanNode struct { 12 | *AbstractPlanNode 13 | rawValues []types.Value 14 | update_col_idxs []int 15 | stats_ *catalog.TableStatistics 16 | } 17 | 18 | // if you update all column, you can specify nil to update_col_idxs. then all data of existed tuple is replaced with rawValues 19 | // if you want update specifed columns only, you should specify columns with update_col_idxs and pass rawValues of all columns defined in schema. 20 | // but not update target column value can be dummy value! 21 | // func NewUpdatePlanNode(rawValues []types.Value, update_col_idxs []int, predicate expression.Expression, oid uint32) Plan { 22 | func NewUpdatePlanNode(rawValues []types.Value, update_col_idxs []int, child Plan) Plan { 23 | return &UpdatePlanNode{&AbstractPlanNode{nil, []Plan{child}}, rawValues, update_col_idxs, child.GetStatistics().GetDeepCopy()} 24 | } 25 | 26 | func (p *UpdatePlanNode) GetTableOID() uint32 { 27 | return p.children[0].GetTableOID() 28 | } 29 | 30 | func (p *UpdatePlanNode) GetType() PlanType { 31 | return Delete 32 | } 33 | 34 | // GetRawValues returns the raw values to be overwritten data 35 | func (p *UpdatePlanNode) GetRawValues() []types.Value { 36 | return p.rawValues 37 | } 38 | 39 | func (p *UpdatePlanNode) GetUpdateColIdxs() []int { 40 | return p.update_col_idxs 41 | } 42 | 43 | func (p *UpdatePlanNode) AccessRowCount(c *catalog.Catalog) uint64 { 44 | return p.children[0].AccessRowCount(c) 45 | } 46 | 47 | func (p *UpdatePlanNode) EmitRowCount(c *catalog.Catalog) uint64 { 48 | return p.children[0].EmitRowCount(c) 49 | } 50 | 51 | func (p *UpdatePlanNode) GetDebugStr() string { 52 | // TODO: (SDB) [OPT] not implemented yet (UpdatePlanNode::GetDebugStr) 53 | panic("not implemented yet") 54 | } 55 | 56 | func (p *UpdatePlanNode) GetStatistics() *catalog.TableStatistics { 57 | return p.stats_ 58 | } 59 | -------------------------------------------------------------------------------- /lib/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ryogrid/SamehadaDB/lib 2 | 3 | go 1.21 4 | 5 | toolchain go1.21.2 6 | 7 | //replace github.com/ryogrid/bltree-go-for-embedding => ../../bltree-go-for-embedding 8 | 9 | require ( 10 | github.com/deckarep/golang-set/v2 v2.3.0 11 | github.com/devlights/gomy v0.4.0 12 | github.com/dsnet/golib/memfile v1.0.0 13 | github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 14 | github.com/notEpsilon/go-pair v0.0.0-20221220200415-e91ef28c6c0b 15 | github.com/pingcap/parser v0.0.0-20200623164729-3a18f1e5dceb 16 | github.com/pingcap/tidb v1.1.0-beta.0.20200630082100-328b6d0a955c 17 | github.com/ryogrid/bltree-go-for-embedding v1.0.11 18 | github.com/spaolacci/murmur3 v1.1.0 19 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 20 | ) 21 | 22 | require ( 23 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect 24 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect 25 | github.com/go-ole/go-ole v1.2.4 // indirect 26 | github.com/golang/protobuf v1.3.4 // indirect 27 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect 28 | github.com/opentracing/opentracing-go v1.1.0 // indirect 29 | github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011 // indirect 30 | github.com/pingcap/log v0.0.0-20200511115504-543df19646ad // indirect 31 | github.com/pingcap/tipb v0.0.0-20200522051215-f31a15d98fce // indirect 32 | github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 // indirect 33 | github.com/shirou/gopsutil v2.19.10+incompatible // indirect 34 | github.com/sirupsen/logrus v1.6.0 // indirect 35 | go.uber.org/atomic v1.6.0 // indirect 36 | go.uber.org/multierr v1.5.0 // indirect 37 | go.uber.org/zap v1.15.0 // indirect 38 | golang.org/x/net v0.15.0 // indirect 39 | golang.org/x/sync v0.3.0 // indirect 40 | golang.org/x/sys v0.12.0 // indirect 41 | golang.org/x/text v0.13.0 // indirect 42 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 43 | ) 44 | -------------------------------------------------------------------------------- /lib/materialization/tmp_tuple.go: -------------------------------------------------------------------------------- 1 | package materialization 2 | 3 | import "github.com/ryogrid/SamehadaDB/lib/types" 4 | 5 | type TmpTuple struct { 6 | page_id types.PageID 7 | offset uint32 8 | } 9 | 10 | func NewTmpTuple(page_id types.PageID, offset uint32) *TmpTuple { 11 | return &TmpTuple{page_id, offset} 12 | } 13 | 14 | func (tt *TmpTuple) Equals(rhs *TmpTuple) bool { 15 | return tt.page_id == rhs.page_id && tt.offset == rhs.offset 16 | } 17 | 18 | func (tt *TmpTuple) GetPageId() types.PageID { return tt.page_id } 19 | func (tt *TmpTuple) GetOffset() uint32 { return tt.offset } 20 | -------------------------------------------------------------------------------- /lib/materialization/tmp_tuple_page.go: -------------------------------------------------------------------------------- 1 | package materialization 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 7 | "unsafe" 8 | 9 | "github.com/ryogrid/SamehadaDB/lib/storage/page" 10 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 11 | "github.com/ryogrid/SamehadaDB/lib/types" 12 | ) 13 | 14 | const offsetFreeSpace = uint32(16) 15 | 16 | /** 17 | * TmpTuplePage format: 18 | * 19 | * Sizes are in bytes. 20 | * | PageId (4) | LSN (4) | FreeSpace (4) | (free space) | TupleSize2 | TupleData2 | TupleSize1 | TupleData1 | 21 | * 22 | * We choose this format because DeserializeExpression expects to read Size followed by Data. 23 | */ 24 | type TmpTuplePage struct { 25 | page.Page 26 | } 27 | 28 | // CastPageAsTmpTuplePage casts the abstract Page struct into TmpTuplePage 29 | func CastPageAsTmpTuplePage(page *page.Page) *TmpTuplePage { 30 | if page == nil { 31 | return nil 32 | } 33 | return (*TmpTuplePage)(unsafe.Pointer(page)) 34 | } 35 | 36 | func (p *TmpTuplePage) Init(page_id types.PageID, page_size uint32) { 37 | p.SetPageId(page_id) 38 | p.SetFreeSpacePointer(page_size) 39 | } 40 | 41 | func (p *TmpTuplePage) GetTablePageId() types.PageID { 42 | return types.NewPageIDFromBytes(p.GetData()[:unsafe.Sizeof(*new(types.PageID))]) 43 | } 44 | 45 | func (p *TmpTuplePage) Insert(tuple_ *tuple.Tuple, out *TmpTuple) bool { 46 | free_offset := p.GetFreeSpacePointer() 47 | need_size := 4 + tuple_.Size() 48 | 49 | if free_offset-need_size < uint32(offsetFreeSpace+4) { 50 | return false 51 | } 52 | free_offset -= need_size 53 | p.SetFreeSpacePointer(free_offset) 54 | addr := p.GetNextPosToInsert() 55 | size := tuple_.Size() 56 | buf := new(bytes.Buffer) 57 | binary.Write(buf, binary.LittleEndian, size) 58 | copy(addr[0:], buf.Bytes()) 59 | data := tuple_.Data() 60 | copy(addr[int(unsafe.Sizeof(size)):], data[:tuple_.Size()]) 61 | *out = *NewTmpTuple(p.GetTablePageId(), p.GetOffset()) 62 | return true 63 | } 64 | 65 | func (p *TmpTuplePage) Get(tuple_ *tuple.Tuple, offset uint32) { 66 | tuple_.DeserializeFrom(p.GetData()[offset:]) 67 | } 68 | 69 | func (p *TmpTuplePage) SetPageId(page_id types.PageID) { 70 | copy(p.GetData()[0:], page_id.Serialize()) 71 | } 72 | func (p *TmpTuplePage) GetFreeSpacePointer() uint32 { 73 | return uint32(types.NewUInt32FromBytes(p.Data()[offsetFreeSpace:])) 74 | } 75 | func (p *TmpTuplePage) SetFreeSpacePointer(size uint32) { 76 | buf := new(bytes.Buffer) 77 | binary.Write(buf, binary.LittleEndian, size) 78 | sizeInBytes := buf.Bytes() 79 | copy(p.GetData()[offsetFreeSpace:], sizeInBytes) 80 | } 81 | func (p *TmpTuplePage) GetNextPosToInsert() []byte { return p.GetData()[p.GetFreeSpacePointer():] } 82 | func (p *TmpTuplePage) GetOffset() uint32 { return p.GetFreeSpacePointer() } 83 | 84 | func FetchTupleFromTmpTuplePage(bpm *buffer.BufferPoolManager, tuple_ *tuple.Tuple, tmp_tuple *TmpTuple) { 85 | tmp_page := CastPageAsTmpTuplePage(bpm.FetchPage(tmp_tuple.GetPageId())) 86 | if tmp_page == nil { 87 | panic("fail to fetch tmp page when doing hash join") 88 | } 89 | // tmp_page content is copied and accessed from currrent transaction only 90 | // so tuple locking is not needed 91 | tmp_page.Get(tuple_, tmp_tuple.GetOffset()) 92 | bpm.UnpinPage(tmp_tuple.GetPageId(), false) 93 | } 94 | -------------------------------------------------------------------------------- /lib/parser/agg_func_visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/pingcap/parser/ast" 7 | driver "github.com/pingcap/tidb/types/parser_driver" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | ) 10 | 11 | type AggFuncVisitor struct { 12 | ColumnName_ *string 13 | TableName_ *string 14 | } 15 | 16 | func (v *AggFuncVisitor) Enter(in ast.Node) (ast.Node, bool) { 17 | switch node := in.(type) { 18 | case *ast.ColumnName: 19 | tabname := node.String() 20 | if strings.Contains(tabname, ".") { 21 | tabname = strings.Split(tabname, ".")[0] 22 | v.TableName_ = &tabname 23 | } else { 24 | v.TableName_ = nil 25 | } 26 | colname := node.Name.String() 27 | v.ColumnName_ = &colname 28 | return in, true 29 | case *driver.ValueExpr: 30 | val := ValueExprToValue(node) 31 | if val.ValueType() == types.Integer { 32 | // val is 1 (Integer) means wildcard maybe... 33 | colname := "*" 34 | v.ColumnName_ = &colname 35 | return in, true 36 | } 37 | default: 38 | } 39 | return in, false 40 | } 41 | 42 | func (v *AggFuncVisitor) Leave(in ast.Node) (ast.Node, bool) { 43 | return in, true 44 | } 45 | -------------------------------------------------------------------------------- /lib/parser/assign_visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/pingcap/parser/ast" 5 | driver "github.com/pingcap/tidb/types/parser_driver" 6 | "github.com/ryogrid/SamehadaDB/lib/types" 7 | ) 8 | 9 | type AssignVisitor struct { 10 | Colname_ *string 11 | Value_ *types.Value 12 | } 13 | 14 | func (v *AssignVisitor) Enter(in ast.Node) (ast.Node, bool) { 15 | switch node := in.(type) { 16 | case *ast.ColumnName: 17 | colname := node.String() 18 | v.Colname_ = &colname 19 | return in, true 20 | case *driver.ValueExpr: 21 | v.Value_ = ValueExprToValue(node) 22 | return in, true 23 | default: 24 | } 25 | 26 | return in, false 27 | } 28 | 29 | func (v *AssignVisitor) Leave(in ast.Node) (ast.Node, bool) { 30 | return in, true 31 | } 32 | -------------------------------------------------------------------------------- /lib/parser/binary_op_visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/pingcap/parser/ast" 5 | "github.com/pingcap/parser/opcode" 6 | driver "github.com/pingcap/tidb/types/parser_driver" 7 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | ) 10 | 11 | type BinaryOpVisitor struct { 12 | QueryInfo_ *QueryInfo 13 | BinaryOpExpression_ *BinaryOpExpression 14 | } 15 | 16 | func (v *BinaryOpVisitor) Enter(in ast.Node) (ast.Node, bool) { 17 | switch node := in.(type) { 18 | case *ast.BinaryOperationExpr: 19 | l_visitor := &BinaryOpVisitor{v.QueryInfo_, new(BinaryOpExpression)} 20 | node.L.Accept(l_visitor) 21 | r_visitor := &BinaryOpVisitor{v.QueryInfo_, new(BinaryOpExpression)} 22 | node.R.Accept(r_visitor) 23 | 24 | logicType, compType := GetTypesForBOperationExpr(node.Op) 25 | v.BinaryOpExpression_.LogicalOperationType_ = logicType 26 | v.BinaryOpExpression_.ComparisonOperationType_ = compType 27 | 28 | if logicType == -1 && compType != -1 { 29 | // when expression is comparison case 30 | if l_visitor.BinaryOpExpression_.ComparisonOperationType_ == -1 && 31 | l_visitor.BinaryOpExpression_.LogicalOperationType_ == -1 { 32 | v.BinaryOpExpression_.Left_ = l_visitor.BinaryOpExpression_.Left_ 33 | } else { 34 | v.BinaryOpExpression_.Left_ = l_visitor.BinaryOpExpression_ 35 | } 36 | 37 | if r_visitor.BinaryOpExpression_.ComparisonOperationType_ == -1 && 38 | r_visitor.BinaryOpExpression_.LogicalOperationType_ == -1 { 39 | v.BinaryOpExpression_.Right_ = r_visitor.BinaryOpExpression_.Left_ 40 | } else { 41 | v.BinaryOpExpression_.Right_ = l_visitor.BinaryOpExpression_ 42 | } 43 | } else { 44 | v.BinaryOpExpression_.Left_ = l_visitor.BinaryOpExpression_ 45 | v.BinaryOpExpression_.Right_ = r_visitor.BinaryOpExpression_ 46 | } 47 | return in, true 48 | case *ast.IsNullExpr: 49 | cdv := &ChildDataVisitor{make([]interface{}, 0)} 50 | node.Accept(cdv) 51 | 52 | v.BinaryOpExpression_.LogicalOperationType_ = -1 53 | if node.Not { 54 | v.BinaryOpExpression_.ComparisonOperationType_ = expression.NotEqual 55 | } else { 56 | v.BinaryOpExpression_.ComparisonOperationType_ = expression.Equal 57 | } 58 | 59 | null_val := types.NewNull() 60 | v.BinaryOpExpression_.Left_ = cdv.ChildDatas_[0] 61 | v.BinaryOpExpression_.Right_ = &null_val 62 | return in, true 63 | case *ast.ColumnNameExpr: 64 | v.BinaryOpExpression_.LogicalOperationType_ = -1 65 | v.BinaryOpExpression_.ComparisonOperationType_ = -1 66 | left_val := node.Name.String() 67 | v.BinaryOpExpression_.Left_ = &left_val 68 | return in, true 69 | case *driver.ValueExpr: 70 | v.BinaryOpExpression_.LogicalOperationType_ = -1 71 | v.BinaryOpExpression_.ComparisonOperationType_ = -1 72 | v.BinaryOpExpression_.Left_ = ValueExprToValue(node) 73 | return in, true 74 | default: 75 | } 76 | 77 | return in, false 78 | } 79 | 80 | func (v *BinaryOpVisitor) Leave(in ast.Node) (ast.Node, bool) { 81 | return in, true 82 | } 83 | 84 | func GetTypesForBOperationExpr(opcode_ opcode.Op) (expression.LogicalOpType, expression.ComparisonType) { 85 | switch opcode_ { 86 | case opcode.EQ: 87 | return -1, expression.Equal 88 | case opcode.GT: 89 | return -1, expression.GreaterThan 90 | case opcode.GE: 91 | return -1, expression.GreaterThanOrEqual 92 | case opcode.LT: 93 | return -1, expression.LessThan 94 | case opcode.LE: 95 | return -1, expression.LessThanOrEqual 96 | case opcode.NE: 97 | return -1, expression.NotEqual 98 | case opcode.LogicAnd: 99 | return expression.AND, -1 100 | case opcode.LogicOr: 101 | return expression.OR, -1 102 | default: 103 | panic("unknown opcode") 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/parser/child_data_visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/pingcap/parser/ast" 5 | driver "github.com/pingcap/tidb/types/parser_driver" 6 | ) 7 | 8 | type ChildDataVisitor struct { 9 | ChildDatas_ []interface{} 10 | } 11 | 12 | func (v *ChildDataVisitor) Enter(in ast.Node) (ast.Node, bool) { 13 | switch node := in.(type) { 14 | case *ast.ColumnName: 15 | colname := node.Name.String() 16 | v.ChildDatas_ = append(v.ChildDatas_, &colname) 17 | return in, true 18 | case *driver.ValueExpr: 19 | val := ValueExprToValue(node) 20 | v.ChildDatas_ = append(v.ChildDatas_, val) 21 | return in, true 22 | default: 23 | } 24 | return in, false 25 | } 26 | 27 | func (v *ChildDataVisitor) Leave(in ast.Node) (ast.Node, bool) { 28 | return in, true 29 | } 30 | -------------------------------------------------------------------------------- /lib/parser/join_visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/pingcap/parser/ast" 5 | ) 6 | 7 | type JoinVisitor struct { 8 | QueryInfo_ *QueryInfo 9 | } 10 | 11 | func (v *JoinVisitor) Enter(in ast.Node) (ast.Node, bool) { 12 | switch node := in.(type) { 13 | case *ast.TableName: 14 | tblname := node.Name.String() 15 | v.QueryInfo_.JoinTables_ = append(v.QueryInfo_.JoinTables_, &tblname) 16 | return in, true 17 | case *ast.BinaryOperationExpr: 18 | bv := &BinaryOpVisitor{v.QueryInfo_, new(BinaryOpExpression)} 19 | node.Accept(bv) 20 | v.QueryInfo_.OnExpressions_ = bv.BinaryOpExpression_ 21 | return in, true 22 | default: 23 | } 24 | return in, false 25 | } 26 | 27 | func (v *JoinVisitor) Leave(in ast.Node) (ast.Node, bool) { 28 | return in, true 29 | } 30 | -------------------------------------------------------------------------------- /lib/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pingcap/parser" 6 | "github.com/pingcap/parser/ast" 7 | _ "github.com/pingcap/tidb/types/parser_driver" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | ) 10 | 11 | type QueryInfo struct { 12 | QueryType_ *QueryType 13 | SelectFields_ []*SelectFieldExpression // SELECT 14 | SetExpressions_ []*SetExpression // UPDATE 15 | NewTable_ *string // CREATE TABLE 16 | ColDefExpressions_ []*ColDefExpression // CREATE TABLE 17 | IndexDefExpressions_ []*IndexDefExpression // CREATE TABLE 18 | TargetCols_ []*string // INSERT 19 | Values_ []*types.Value // INSERT 20 | OnExpressions_ *BinaryOpExpression // SELECT (with JOIN) 21 | JoinTables_ []*string // SELECT 22 | WhereExpression_ *BinaryOpExpression // SELECT, UPDATE, DELETE 23 | LimitNum_ int32 // SELECT 24 | OffsetNum_ int32 // SELECT 25 | OrderByExpressions_ []*OrderByExpression // SELECT 26 | } 27 | 28 | func extractInfoFromAST(rootNode *ast.StmtNode) *QueryInfo { 29 | v := NewRootSQLVisitor() 30 | (*rootNode).Accept(v) 31 | return v.QueryInfo_ 32 | } 33 | 34 | func parse(sqlStr *string) (*ast.StmtNode, error) { 35 | p := parser.New() 36 | 37 | stmtNodes, _, err := p.Parse(*sqlStr, "", "") 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return &stmtNodes[0], nil 43 | } 44 | 45 | func ProcessSQLStr(sqlStr *string) (*QueryInfo, error) { 46 | astNode, err := parse(sqlStr) 47 | if err != nil { 48 | fmt.Printf("parse error: %v\n", err.Error()) 49 | return nil, err 50 | } 51 | 52 | return extractInfoFromAST(astNode), nil 53 | } 54 | 55 | // for utity func on develop phase 56 | func printTraversedNodes(rootNode *ast.StmtNode) { 57 | v := NewPrintNodesVisitor() 58 | (*rootNode).Accept(v) 59 | } 60 | 61 | // for utity func on develop phase 62 | func PrintParsedNodes(sqlStr *string) { 63 | astNode, err := parse(sqlStr) 64 | if err != nil { 65 | fmt.Printf("parse error: %v\n", err.Error()) 66 | return 67 | } 68 | 69 | printTraversedNodes(astNode) 70 | } 71 | -------------------------------------------------------------------------------- /lib/parser/parser_util.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | ptypes "github.com/pingcap/tidb/types" 5 | driver "github.com/pingcap/tidb/types/parser_driver" 6 | "github.com/ryogrid/SamehadaDB/lib/execution/expression" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | type QueryType int32 14 | 15 | const ( 16 | SELECT QueryType = iota 17 | CREATE_TABLE 18 | INSERT 19 | DELETE 20 | UPDATE 21 | ) 22 | 23 | func ValueExprToValue(expr *driver.ValueExpr) *types.Value { 24 | switch expr.Datum.Kind() { 25 | case ptypes.KindInt64, ptypes.KindUint64: 26 | val_str := expr.String() 27 | istr := strings.Split(val_str, " ")[1] 28 | ival, _ := strconv.Atoi(istr) 29 | ret := types.NewInteger(int32(ival)) 30 | return &ret 31 | case ptypes.KindMysqlDecimal: 32 | val_str := expr.String() 33 | fstr := strings.Split(val_str, " ")[1] 34 | fval, _ := strconv.ParseFloat(fstr, 32) 35 | ret := types.NewFloat(float32(fval)) 36 | return &ret 37 | default: // varchar 38 | val_str := expr.String() 39 | splited := strings.Split(val_str, " ") 40 | target_str := strings.Join(splited[1:], " ") 41 | ret := types.NewVarchar(target_str) 42 | return &ret 43 | } 44 | } 45 | 46 | func GetPredicateExprFromStr(schema_ *schema.Schema, pred *string) expression.Expression { 47 | sqlStr := "SELECT * FROM dummy WHERE " + *pred + ";" 48 | qi, _ := ProcessSQLStr(&sqlStr) 49 | return ConvParsedBinaryOpExprToExpIFOne(schema_, qi.WhereExpression_) 50 | } 51 | -------------------------------------------------------------------------------- /lib/parser/print_nodes_visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pingcap/parser/ast" 6 | "reflect" 7 | ) 8 | 9 | type PrintNodesVisitor struct { 10 | } 11 | 12 | func NewPrintNodesVisitor() *PrintNodesVisitor { 13 | return new(PrintNodesVisitor) 14 | } 15 | 16 | func (v *PrintNodesVisitor) Enter(in ast.Node) (ast.Node, bool) { 17 | refVal := reflect.ValueOf(in) 18 | fmt.Println(refVal.Type()) 19 | return in, false 20 | } 21 | 22 | func (v *PrintNodesVisitor) Leave(in ast.Node) (ast.Node, bool) { 23 | return in, true 24 | } 25 | -------------------------------------------------------------------------------- /lib/parser/select_fields_visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/pingcap/parser/ast" 5 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 6 | "strings" 7 | ) 8 | 9 | type SelectFieldsVisitor struct { 10 | QueryInfo_ *QueryInfo 11 | } 12 | 13 | func (v *SelectFieldsVisitor) Enter(in ast.Node) (ast.Node, bool) { 14 | switch node := in.(type) { 15 | case *ast.ColumnName: 16 | sfield := new(SelectFieldExpression) 17 | tabname := node.String() 18 | if strings.Contains(tabname, ".") { 19 | tabname = strings.Split(tabname, ".")[0] 20 | sfield.TableName_ = &tabname 21 | } else { 22 | sfield.TableName_ = nil 23 | } 24 | colname := node.Name.String() 25 | sfield.ColName_ = &colname 26 | v.QueryInfo_.SelectFields_ = append(v.QueryInfo_.SelectFields_, sfield) 27 | return in, true 28 | case *ast.SelectField: 29 | // when specifed wildcard 30 | if node.WildCard != nil { 31 | sfield := new(SelectFieldExpression) 32 | colname := "*" 33 | sfield.ColName_ = &colname 34 | v.QueryInfo_.SelectFields_ = append(v.QueryInfo_.SelectFields_, sfield) 35 | return in, true 36 | } 37 | case *ast.AggregateFuncExpr: 38 | av := new(AggFuncVisitor) 39 | node.Accept(av) 40 | var sfield *SelectFieldExpression = nil 41 | aggTypeStr := node.F 42 | switch aggTypeStr { 43 | case "count": 44 | sfield = &SelectFieldExpression{true, plans.COUNT_AGGREGATE, av.TableName_, av.ColumnName_} 45 | case "max": 46 | sfield = &SelectFieldExpression{true, plans.MAX_AGGREGATE, av.TableName_, av.ColumnName_} 47 | case "min": 48 | sfield = &SelectFieldExpression{true, plans.MIN_AGGREGATE, av.TableName_, av.ColumnName_} 49 | case "sum": 50 | sfield = &SelectFieldExpression{true, plans.SUM_AGGREGATE, av.TableName_, av.ColumnName_} 51 | } 52 | 53 | v.QueryInfo_.SelectFields_ = append(v.QueryInfo_.SelectFields_, sfield) 54 | return in, true 55 | default: 56 | } 57 | return in, false 58 | } 59 | 60 | func (v *SelectFieldsVisitor) Leave(in ast.Node) (ast.Node, bool) { 61 | return in, true 62 | } 63 | -------------------------------------------------------------------------------- /lib/parser/visitor.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/pingcap/parser/ast" 5 | ) 6 | 7 | type Visitor interface { 8 | Enter(n ast.Node) (node ast.Node, skipChildren bool) 9 | Leave(n ast.Node) (node ast.Node, ok bool) 10 | } 11 | -------------------------------------------------------------------------------- /lib/planner/optimizer/optimizer.go: -------------------------------------------------------------------------------- 1 | package optimizer 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/catalog" 5 | "github.com/ryogrid/SamehadaDB/lib/execution/executors" 6 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 7 | "github.com/ryogrid/SamehadaDB/lib/parser" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 9 | ) 10 | 11 | type Optimizer interface { 12 | Optimize(*parser.QueryInfo, *executors.ExecutorContext, *catalog.Catalog, *access.Transaction) (plans.Plan, error) 13 | } 14 | -------------------------------------------------------------------------------- /lib/planner/planner.go: -------------------------------------------------------------------------------- 1 | package planner 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/execution/plans" 5 | "github.com/ryogrid/SamehadaDB/lib/parser" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 7 | ) 8 | 9 | type Planner interface { 10 | MakePlan(*parser.QueryInfo, *access.Transaction) (error, plans.Plan) 11 | } 12 | -------------------------------------------------------------------------------- /lib/samehada/request_manager.go: -------------------------------------------------------------------------------- 1 | package samehada 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/common" 5 | "sync" 6 | ) 7 | 8 | type queryRequest struct { 9 | reqId *uint64 10 | queryStr *string 11 | callerCh *chan *reqResult 12 | } 13 | 14 | type RequestManager struct { 15 | sdb *SamehadaDB 16 | nextReqId uint64 17 | execQue []*queryRequest 18 | queMutex *sync.Mutex 19 | curExectingReqNum uint64 20 | inCh *chan *reqResult 21 | isExecutionActive bool 22 | } 23 | 24 | func NewRequestManager(sdb *SamehadaDB) *RequestManager { 25 | ch := make(chan *reqResult, 100) 26 | return &RequestManager{sdb, 0, make([]*queryRequest, 0), new(sync.Mutex), 0, &ch, true} 27 | } 28 | 29 | func (reqManager *RequestManager) AppendRequest(queryStr *string) *chan *reqResult { 30 | reqManager.queMutex.Lock() 31 | 32 | qr := new(queryRequest) 33 | tmpId := reqManager.nextReqId 34 | qr.reqId = &tmpId 35 | reqManager.nextReqId++ 36 | qr.queryStr = queryStr 37 | 38 | retCh := make(chan *reqResult) 39 | 40 | qr.callerCh = &retCh 41 | 42 | reqManager.execQue = append(reqManager.execQue, qr) 43 | reqManager.queMutex.Unlock() 44 | 45 | // wake up execution thread 46 | *reqManager.inCh <- nil 47 | 48 | return &retCh 49 | } 50 | 51 | // caller must having lock of queMutex 52 | func (reqManager *RequestManager) RetrieveRequest() *queryRequest { 53 | retVal := reqManager.execQue[0] 54 | reqManager.execQue = reqManager.execQue[1:] 55 | return retVal 56 | } 57 | 58 | func (reqManager *RequestManager) StartTh() { 59 | go reqManager.Run() 60 | } 61 | 62 | func (reqManager *RequestManager) StopTh() { 63 | reqManager.isExecutionActive = false 64 | *reqManager.inCh <- nil 65 | } 66 | 67 | // caller must having lock of queMutex 68 | func (reqManager *RequestManager) executeQuedTxns() { 69 | qr := reqManager.RetrieveRequest() 70 | go reqManager.sdb.ExecuteSQLForTxnTh(reqManager.inCh, qr) 71 | reqManager.curExectingReqNum++ 72 | } 73 | 74 | // caller must having lock of queMutex 75 | func (reqManager *RequestManager) handleAbortedByCCTxn(result *reqResult) { 76 | // insert aborted request to head of que 77 | reqManager.execQue = append([]*queryRequest{{result.reqId, result.query, result.callerCh}}, reqManager.execQue...) 78 | //fmt.Println("add que aborted req") 79 | } 80 | 81 | func (reqManager *RequestManager) Run() { 82 | for { 83 | recvVal := <-*reqManager.inCh 84 | if recvVal != nil { // receive result 85 | reqManager.queMutex.Lock() 86 | reqManager.curExectingReqNum-- 87 | 88 | if recvVal.err != nil { 89 | if recvVal.err == QueryAbortedErr { 90 | reqManager.handleAbortedByCCTxn(recvVal) 91 | reqManager.queMutex.Unlock() 92 | } else { 93 | reqManager.queMutex.Unlock() 94 | *recvVal.callerCh <- recvVal 95 | } 96 | } else { 97 | reqManager.queMutex.Unlock() 98 | *recvVal.callerCh <- recvVal 99 | } 100 | } 101 | 102 | // check stop signal_handle or new request 103 | if !reqManager.isExecutionActive { 104 | break 105 | } 106 | reqManager.queMutex.Lock() 107 | if len(reqManager.execQue) > 0 && reqManager.curExectingReqNum < common.MaxTxnThreadNum { 108 | reqManager.executeQuedTxns() 109 | } 110 | reqManager.queMutex.Unlock() 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/samehada/samehada_instance.go: -------------------------------------------------------------------------------- 1 | package samehada 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/common" 5 | "github.com/ryogrid/SamehadaDB/lib/concurrency" 6 | "github.com/ryogrid/SamehadaDB/lib/recovery" 7 | "github.com/ryogrid/SamehadaDB/lib/storage/access" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/disk" 10 | ) 11 | 12 | type ShutdownPattern int 13 | 14 | const ( 15 | ShutdownPatternRemoveFiles ShutdownPattern = iota 16 | ShutdownPatternCloseFiles 17 | ) 18 | 19 | type SamehadaInstance struct { 20 | disk_manager disk.DiskManager 21 | log_manager *recovery.LogManager 22 | bpm *buffer.BufferPoolManager 23 | lock_manager *access.LockManager 24 | transaction_manager *access.TransactionManager 25 | checkpoint_manger *concurrency.CheckpointManager 26 | } 27 | 28 | func NewSamehadaInstanceForTesting() *SamehadaInstance { 29 | ret := NewSamehadaInstance("test", common.BufferPoolMaxFrameNumForTest) 30 | ret.GetLogManager().DeactivateLogging() 31 | return ret 32 | } 33 | 34 | // reset program state except for variables on testcase function 35 | // and db/log file 36 | // bpoolSize: usable buffer size in frame(=page) num 37 | func NewSamehadaInstance(dbName string, bpoolSize int) *SamehadaInstance { 38 | var disk_manager disk.DiskManager 39 | if !common.EnableOnMemStorage || common.TempSuppressOnMemStorage { 40 | disk_manager = disk.NewDiskManagerImpl(dbName + ".db") 41 | } else { 42 | disk_manager = disk.NewVirtualDiskManagerImpl(dbName + ".db") 43 | } 44 | 45 | log_manager := recovery.NewLogManager(&disk_manager) 46 | log_manager.ActivateLogging() 47 | bpm := buffer.NewBufferPoolManager(uint32(bpoolSize), disk_manager, log_manager) 48 | lock_manager := access.NewLockManager(access.STRICT, access.SS2PL_MODE) 49 | transaction_manager := access.NewTransactionManager(lock_manager, log_manager) 50 | checkpoint_manager := concurrency.NewCheckpointManager(transaction_manager, log_manager, bpm) 51 | 52 | return &SamehadaInstance{disk_manager, log_manager, bpm, lock_manager, transaction_manager, checkpoint_manager} 53 | } 54 | 55 | func (si *SamehadaInstance) GetDiskManager() disk.DiskManager { 56 | return si.disk_manager 57 | } 58 | 59 | func (si *SamehadaInstance) GetLogManager() *recovery.LogManager { 60 | return si.log_manager 61 | } 62 | 63 | func (si *SamehadaInstance) GetBufferPoolManager() *buffer.BufferPoolManager { 64 | return si.bpm 65 | } 66 | 67 | func (si *SamehadaInstance) GetLockManager() *access.LockManager { 68 | return si.lock_manager 69 | } 70 | 71 | func (si *SamehadaInstance) GetTransactionManager() *access.TransactionManager { 72 | return si.transaction_manager 73 | } 74 | 75 | func (si *SamehadaInstance) GetCheckpointManager() *concurrency.CheckpointManager { 76 | return si.checkpoint_manger 77 | } 78 | 79 | // functionality is Flushing dirty pages, shutdown of DiskManager and action around DB/Log files 80 | func (si *SamehadaInstance) Shutdown(shutdownPat ShutdownPattern) { 81 | switch shutdownPat { 82 | case ShutdownPatternRemoveFiles: 83 | //close 84 | si.disk_manager.ShutDown() 85 | //remove 86 | si.disk_manager.RemoveDBFile() 87 | si.disk_manager.RemoveLogFile() 88 | case ShutdownPatternCloseFiles: 89 | si.log_manager.Flush() 90 | // TODO: (SDB) need to finalize BTreeIndex objects 91 | si.bpm.FlushAllDirtyPages() 92 | logRecord := recovery.NewLogRecordGracefulShutdown() 93 | si.log_manager.AppendLogRecord(logRecord) 94 | si.log_manager.Flush() 95 | // close only 96 | si.disk_manager.ShutDown() 97 | default: 98 | panic("invalid shutdown pattern") 99 | } 100 | } 101 | 102 | // for testing. this method does file closing only in contrast to Shutdown method 103 | func (si *SamehadaInstance) CloseFilesForTesting() { 104 | si.disk_manager.ShutDown() 105 | } 106 | -------------------------------------------------------------------------------- /lib/storage/access/table_heap_iterator.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package access 5 | 6 | import ( 7 | "fmt" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 9 | ) 10 | 11 | // TableHeapIterator is the access method for table heaps 12 | // 13 | // It iterates through a table heap when Next is called 14 | // The tuple1 that it is being pointed to can be accessed with the method Current 15 | type TableHeapIterator struct { 16 | tableHeap *TableHeap 17 | tuple *tuple.Tuple 18 | lock_manager *LockManager 19 | txn *Transaction 20 | } 21 | 22 | // NewTableHeapIterator creates a new table heap operator for the given table heap 23 | // It points to the first tuple1 of the table heap 24 | func NewTableHeapIterator(tableHeap *TableHeap, lock_manager *LockManager, txn *Transaction) *TableHeapIterator { 25 | return &TableHeapIterator{tableHeap, tableHeap.GetFirstTuple(txn), lock_manager, txn} 26 | } 27 | 28 | // Current points to the current tuple1 29 | func (it *TableHeapIterator) Current() *tuple.Tuple { 30 | return it.tuple 31 | } 32 | 33 | // End checks if the iterator is at the end 34 | func (it *TableHeapIterator) End() bool { 35 | return it.Current() == nil 36 | } 37 | 38 | // Next advances the iterator trying to find the next tuple1 39 | // The next tuple1 can be inside the same page of the current tuple1 40 | // or it can be in the next page 41 | func (it *TableHeapIterator) Next() *tuple.Tuple { 42 | start: 43 | bpm := it.tableHeap.bpm 44 | currentPage := CastPageAsTablePage(bpm.FetchPage(it.Current().GetRID().GetPageId())) 45 | currentPage.RLatch() 46 | 47 | nextTupleRID := currentPage.GetNextTupleRID(it.Current().GetRID(), false) 48 | if nextTupleRID == nil { 49 | // VARIANT: currentPage is always RLatched after loop 50 | for currentPage.GetNextPageId().IsValid() { 51 | nextPage := CastPageAsTablePage(bpm.FetchPage(currentPage.GetNextPageId())) 52 | if nextPage == nil { 53 | // TODO: (SDB) SHOULD BE FIXED: statics data update thread's call pass here rarely 54 | bpm.UnpinPage(currentPage.GetPageId(), false) 55 | currentPage.RUnlatch() 56 | it.tuple = nil 57 | return nil 58 | } 59 | bpm.UnpinPage(currentPage.GetPageId(), false) 60 | nextPage.RLatch() 61 | currentPage.RUnlatch() 62 | currentPage = nextPage 63 | nextTupleRID = currentPage.GetNextTupleRID(it.Current().GetRID(), true) 64 | 65 | if nextTupleRID != nil { 66 | break 67 | } 68 | } 69 | } 70 | 71 | finalizeCurrentPage := func() { 72 | bpm.UnpinPage(currentPage.GetPageId(), false) 73 | currentPage.RUnlatch() 74 | } 75 | 76 | var err error = nil 77 | if nextTupleRID != nil && nextTupleRID.GetPageId().IsValid() { 78 | it.tuple, err = currentPage.GetTuple(nextTupleRID, it.tableHeap.log_manager, it.lock_manager, it.txn) 79 | if it.tuple != nil && err == ErrSelfDeletedCase { 80 | fmt.Println("TableHeapIterator::Next ErrSelfDeletedCase!") 81 | finalizeCurrentPage() 82 | goto start 83 | } 84 | } else { 85 | it.tuple = nil 86 | } 87 | 88 | finalizeCurrentPage() 89 | return it.tuple 90 | } 91 | -------------------------------------------------------------------------------- /lib/storage/buffer/circular_list.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package buffer 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type node struct { 11 | key FrameID //interface{} 12 | value bool //interface{} 13 | next *node 14 | prev *node 15 | } 16 | 17 | type circularList struct { 18 | head *node 19 | tail *node 20 | size uint32 21 | capacity uint32 22 | supportMap map[FrameID]*node 23 | } 24 | 25 | func (c *circularList) hasKey(key FrameID) bool { 26 | _, ok := c.supportMap[key] 27 | return ok 28 | } 29 | 30 | func (c *circularList) insert(key FrameID, value bool) error { 31 | if c.size == c.capacity { 32 | panic("circularList::insert capacity is full") 33 | } 34 | 35 | newNode := &node{key, value, nil, nil} 36 | if c.size == 0 { 37 | newNode.next = newNode 38 | newNode.prev = newNode 39 | c.head = newNode 40 | c.tail = newNode 41 | c.size++ 42 | c.supportMap[key] = newNode 43 | return nil 44 | } 45 | 46 | node, ok := c.supportMap[key] 47 | if ok { 48 | node.value = value 49 | return nil 50 | } 51 | 52 | newNode.next = c.head 53 | newNode.prev = c.tail 54 | 55 | c.tail.next = newNode 56 | if c.head == c.tail { 57 | c.head.next = newNode 58 | } 59 | 60 | c.tail = newNode 61 | c.head.prev = c.tail 62 | 63 | c.size++ 64 | c.supportMap[key] = newNode 65 | 66 | return nil 67 | } 68 | 69 | func (c *circularList) remove(key FrameID) { 70 | node, ok := c.supportMap[key] 71 | if !ok { 72 | return 73 | } 74 | 75 | if c.size == 1 { 76 | c.head = nil 77 | c.tail = nil 78 | c.size-- 79 | delete(c.supportMap, key) 80 | return 81 | } 82 | 83 | if node == c.head { 84 | c.head = c.head.next 85 | } 86 | 87 | if node == c.tail { 88 | c.tail = c.tail.prev 89 | } 90 | 91 | node.next.prev = node.prev 92 | node.prev.next = node.next 93 | 94 | c.size-- 95 | delete(c.supportMap, key) 96 | } 97 | 98 | func (c *circularList) isFull() bool { 99 | return c.size == c.capacity 100 | } 101 | 102 | func (c *circularList) Print() { 103 | if c.size == 0 { 104 | fmt.Println("circularList is empty.") 105 | } 106 | ptr := c.head 107 | printStr := fmt.Sprintf("circularList size:%d supportMap len:%d |", c.size, len(c.supportMap)) 108 | for i := uint32(0); i < c.size; i++ { 109 | printStr += fmt.Sprintf("-%v,%v,%v,%v-", ptr.key, ptr.value, ptr.prev.key, ptr.next.key) 110 | ptr = ptr.next 111 | } 112 | fmt.Println(printStr) 113 | } 114 | 115 | func newCircularList(maxSize uint32) *circularList { 116 | return &circularList{nil, nil, 0, maxSize, make(map[FrameID]*node)} 117 | } 118 | -------------------------------------------------------------------------------- /lib/storage/buffer/clock_replacer.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package buffer 5 | 6 | import ( 7 | "fmt" 8 | "math" 9 | "sync" 10 | ) 11 | 12 | const DEALLOCATED_FRAME FrameID = math.MaxUint32 13 | 14 | // FrameID is the type for frame id 15 | type FrameID uint32 16 | 17 | /** 18 | * ClockReplacer implements the clock replacement policy, which approximates the Least Recently Used policy. 19 | */ 20 | type ClockReplacer struct { 21 | cList *circularList 22 | clockHand **node 23 | mutex *sync.Mutex 24 | } 25 | 26 | // Victim removes the victim frame as defined by the replacement policy 27 | func (c *ClockReplacer) Victim() *FrameID { 28 | if c.cList.size == 0 { 29 | //fmt.Println("ClockReplacer::Victim: page which can be cache out is not exist!") 30 | panic("Victim: page which can be cache out is not exist!") 31 | //return nil 32 | } 33 | 34 | var victimFrameID *FrameID 35 | currentNode := *c.clockHand 36 | for { 37 | if currentNode.value { 38 | currentNode.value = false 39 | c.clockHand = ¤tNode.next 40 | } else { 41 | frameID := currentNode.key 42 | victimFrameID = &frameID 43 | 44 | c.clockHand = ¤tNode.next 45 | 46 | c.cList.remove(currentNode.key) 47 | return victimFrameID 48 | } 49 | } 50 | } 51 | 52 | // Unpin unpins a frame, indicating that it can now be victimized 53 | func (c *ClockReplacer) Unpin(id FrameID) { 54 | if !c.cList.hasKey(id) { 55 | c.cList.insert(id, true) 56 | if c.cList.size == 1 { 57 | c.clockHand = &c.cList.head 58 | } 59 | } 60 | } 61 | 62 | // Pin pins a frame, indicating that it should not be victimized until it is unpinned 63 | func (c *ClockReplacer) Pin(id FrameID) { 64 | node, ok := c.cList.supportMap[id] 65 | if !ok { 66 | return 67 | } 68 | 69 | if (*c.clockHand) == node { 70 | c.clockHand = &(*c.clockHand).next 71 | } 72 | c.cList.remove(id) 73 | } 74 | 75 | func (c *ClockReplacer) isContain(id FrameID) bool { 76 | _, ok := c.cList.supportMap[id] 77 | return ok 78 | } 79 | 80 | // Size returns the size of the clock 81 | func (c *ClockReplacer) Size() uint32 { 82 | return c.cList.size 83 | } 84 | 85 | func (c *ClockReplacer) PrintList() { 86 | fmt.Printf("ClockReplacer::PrintList ") 87 | c.cList.Print() 88 | } 89 | 90 | // NewClockReplacer instantiates a new clock replacer 91 | func NewClockReplacer(poolSize uint32) *ClockReplacer { 92 | cList := newCircularList(poolSize) 93 | return &ClockReplacer{cList, &cList.head, new(sync.Mutex)} 94 | } 95 | -------------------------------------------------------------------------------- /lib/storage/buffer/clock_replacer_test.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package buffer 5 | 6 | import ( 7 | testingpkg "github.com/ryogrid/SamehadaDB/lib/testing/testing_assert" 8 | "testing" 9 | ) 10 | 11 | func TestClockReplacer(t *testing.T) { 12 | clockReplacer := NewClockReplacer(7) 13 | 14 | // Scenario: unpin six elements, i.e. add them to the replacer. 15 | clockReplacer.Unpin(1) 16 | clockReplacer.Unpin(2) 17 | clockReplacer.Unpin(3) 18 | clockReplacer.Unpin(4) 19 | clockReplacer.Unpin(5) 20 | clockReplacer.Unpin(6) 21 | clockReplacer.Unpin(1) 22 | testingpkg.Equals(t, uint32(6), clockReplacer.Size()) 23 | 24 | // Scenario: get three victims from the caccess. 25 | var value *FrameID 26 | value = clockReplacer.Victim() 27 | testingpkg.Equals(t, FrameID(1), *value) 28 | value = clockReplacer.Victim() 29 | testingpkg.Equals(t, FrameID(2), *value) 30 | value = clockReplacer.Victim() 31 | testingpkg.Equals(t, FrameID(3), *value) 32 | 33 | // Scenario: pin elements in the replacer. 34 | // Note that 3 has already been victimized, so pinning 3 should have no effect. 35 | clockReplacer.Pin(3) 36 | clockReplacer.Pin(4) 37 | testingpkg.Equals(t, uint32(2), clockReplacer.Size()) 38 | 39 | // Scenario: unpin 4. We expect that the reference bit of 4 will be set to 1. 40 | clockReplacer.Unpin(4) 41 | 42 | // Scenario: continue looking for victims. We expect these victims. 43 | value = clockReplacer.Victim() 44 | testingpkg.Equals(t, FrameID(5), *value) 45 | value = clockReplacer.Victim() 46 | testingpkg.Equals(t, FrameID(6), *value) 47 | value = clockReplacer.Victim() 48 | testingpkg.Equals(t, FrameID(4), *value) 49 | } 50 | -------------------------------------------------------------------------------- /lib/storage/disk/disk_manager.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package disk 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/types" 8 | ) 9 | 10 | /** 11 | * DiskManager takes care of the allocation and deallocation of pages within a database. It performs the reading and 12 | * writing of pages to and from disk, providing a logical file layer within the context of a database management system. 13 | */ 14 | type DiskManager interface { 15 | ReadPage(types.PageID, []byte) error 16 | WritePage(types.PageID, []byte) error 17 | AllocatePage() types.PageID 18 | DeallocatePage(types.PageID) 19 | GetNumWrites() uint64 20 | ShutDown() 21 | Size() int64 22 | RemoveDBFile() 23 | RemoveLogFile() 24 | WriteLog([]byte) error 25 | ReadLog([]byte, int32, *uint32) bool 26 | GetLogFileSize() int64 27 | GCLogFile() error 28 | } 29 | -------------------------------------------------------------------------------- /lib/storage/disk/disk_manager_test.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package disk 5 | 6 | import ( 7 | testingpkg "github.com/ryogrid/SamehadaDB/lib/testing/testing_assert" 8 | "testing" 9 | 10 | "github.com/ryogrid/SamehadaDB/lib/common" 11 | ) 12 | 13 | func zeroClear(buffer []byte) { 14 | for i := range buffer { 15 | buffer[i] = 0 16 | } 17 | } 18 | 19 | func TestReadWritePage(t *testing.T) { 20 | common.TempSuppressOnMemStorageMutex.Lock() 21 | common.TempSuppressOnMemStorage = true 22 | 23 | dm := NewDiskManagerTest() 24 | defer dm.ShutDown() 25 | 26 | data := make([]byte, common.PageSize) 27 | buffer := make([]byte, common.PageSize) 28 | 29 | copy(data, "A test string.") 30 | 31 | dm.ReadPage(0, buffer) // tolerate empty read 32 | dm.WritePage(0, data) 33 | err := dm.ReadPage(0, buffer) 34 | testingpkg.Equals(t, err, nil) 35 | testingpkg.Equals(t, int64(common.PageSize), dm.Size()) 36 | testingpkg.Equals(t, data, buffer) 37 | 38 | zeroClear(buffer) 39 | copy(data, "Another test string.") 40 | 41 | dm.WritePage(5, data) 42 | dm.ReadPage(5, buffer) 43 | testingpkg.Equals(t, data, buffer) 44 | 45 | // the size of disk is 6 * PageSize bytes because we have 6 pages 46 | testingpkg.Equals(t, int64(6*common.PageSize), dm.Size()) 47 | 48 | common.TempSuppressOnMemStorage = false 49 | common.TempSuppressOnMemStorageMutex.Unlock() 50 | } 51 | -------------------------------------------------------------------------------- /lib/storage/disk/testing.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package disk 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/common" 8 | "io/ioutil" 9 | "os" 10 | ) 11 | 12 | // DiskManagerTest is the disk implementation of DiskManager for testing purposes 13 | type DiskManagerTest struct { 14 | path string 15 | DiskManager 16 | } 17 | 18 | // NewDiskManagerTest returns a DiskManager instance for testing purposes 19 | func NewDiskManagerTest() DiskManager { 20 | // Retrieve a temporary path. 21 | f, err := ioutil.TempFile("", "samehada.") 22 | if err != nil { 23 | panic(err) 24 | } 25 | path := f.Name() 26 | f.Close() 27 | os.Remove(path) 28 | 29 | if !common.EnableOnMemStorage || common.TempSuppressOnMemStorage { 30 | diskManager := NewDiskManagerImpl(path) 31 | return &DiskManagerTest{path, diskManager} 32 | } else { 33 | diskManager := NewVirtualDiskManagerImpl(path) 34 | return &DiskManagerTest{path, diskManager} 35 | } 36 | } 37 | 38 | // ShutDown closes of the database file 39 | func (d *DiskManagerTest) ShutDown() { 40 | d.DiskManager.ShutDown() 41 | if !common.EnableOnMemStorage || common.TempSuppressOnMemStorage { 42 | os.Remove(d.path) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/storage/index/index.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/storage/page" 5 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 6 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 7 | ) 8 | 9 | /** 10 | * class IndexMetadata - Holds metadata of an index object 11 | * 12 | * The metadata object maintains the tuple schema and key attribute of an 13 | * index, since the external callers does not know the actual structure of 14 | * the index key, so it is the index's responsibility to maintain such a 15 | * mapping relation and does the conversion between tuple key and index key 16 | */ 17 | 18 | type IndexMetadata struct { 19 | name string 20 | table_name string 21 | // The mapping relation between key schema and tuple schema 22 | key_attrs []uint32 23 | // schema of the indexed key 24 | tuple_schema *schema.Schema 25 | } 26 | 27 | func NewIndexMetadata(index_name string, table_name string, tuple_schema *schema.Schema, 28 | key_attrs []uint32) *IndexMetadata { 29 | ret := new(IndexMetadata) 30 | ret.name = index_name 31 | ret.table_name = table_name 32 | ret.key_attrs = key_attrs 33 | ret.tuple_schema = tuple_schema 34 | return ret 35 | } 36 | 37 | func (im *IndexMetadata) GetName() *string { return &im.name } 38 | func (im *IndexMetadata) GetTableName() *string { return &im.table_name } 39 | 40 | // Returns a schema object pointer that represents the indexed key 41 | func (im *IndexMetadata) GetTupleSchema() *schema.Schema { return im.tuple_schema } 42 | 43 | // Return the number of columns inside index key (not in tuple key) 44 | // Note that this must be defined inside the cpp source file 45 | // because it uses the member of catalog::Schema which is not known here 46 | func (im *IndexMetadata) GetIndexColumnCount() uint32 { return uint32(len(im.key_attrs)) } 47 | 48 | // Returns the mapping relation between indexed columns and base table 49 | // columns 50 | func (im *IndexMetadata) GetKeyAttrs() []uint32 { return im.key_attrs } 51 | 52 | ///////////////////////////////////////////////////////////////////// 53 | // Index class definition 54 | ///////////////////////////////////////////////////////////////////// 55 | 56 | /** 57 | * class Index - Base class for derived indices of different types 58 | * 59 | * The index structure majorly maintains information on the schema of the 60 | * schema of the underlying table and the mapping relation between index key 61 | * and tuple key, and provides an abstracted way for the external world to 62 | * interact with the underlying index implementation without exposing 63 | * the actual implementation's interface. 64 | * 65 | * Index object also handles predicate scan, in addition to simple insert, 66 | * delete, predicate insert, point query, and full index scan. OnPredicate scan 67 | * only supports conjunction, and may or may not be optimized depending on 68 | * the type of expressions inside the predicate. 69 | */ 70 | 71 | type Index interface { 72 | // Return the metadata object associated with the index 73 | GetMetadata() *IndexMetadata 74 | GetIndexColumnCount() uint32 75 | GetName() *string 76 | GetTupleSchema() *schema.Schema 77 | GetKeyAttrs() []uint32 78 | /////////////////////////////////////////////////////////////////// 79 | // Point Modification 80 | /////////////////////////////////////////////////////////////////// 81 | // designed for secondary indexes. 82 | InsertEntry(*tuple.Tuple, page.RID, interface{}) 83 | // delete the index entry linked to given tuple 84 | DeleteEntry(*tuple.Tuple, page.RID, interface{}) 85 | // update entry. internally, delete first entry and insert seconde entry atomically 86 | UpdateEntry(*tuple.Tuple, page.RID, *tuple.Tuple, page.RID, interface{}) 87 | ScanKey(*tuple.Tuple, interface{}) []page.RID 88 | // pass start key and end key. nil is also ok. 89 | GetRangeScanIterator(*tuple.Tuple, *tuple.Tuple, interface{}) IndexRangeScanIterator 90 | } 91 | -------------------------------------------------------------------------------- /lib/storage/index/index_common/index_common.go: -------------------------------------------------------------------------------- 1 | package index_common 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "github.com/ryogrid/SamehadaDB/lib/types" 7 | ) 8 | 9 | const sizeEntryValue = uint32(8) 10 | 11 | type IndexEntry struct { 12 | Key types.Value 13 | Value uint64 14 | } 15 | 16 | func (ie IndexEntry) Serialize() []byte { 17 | keyInBytes := ie.Key.Serialize() 18 | valBuf := new(bytes.Buffer) 19 | binary.Write(valBuf, binary.LittleEndian, ie.Value) 20 | valInBytes := valBuf.Bytes() 21 | 22 | retBuf := new(bytes.Buffer) 23 | retBuf.Write(keyInBytes) 24 | retBuf.Write(valInBytes) 25 | return retBuf.Bytes() 26 | } 27 | 28 | func (ie IndexEntry) GetDataSize() uint32 { 29 | keyInBytes := ie.Key.Serialize() 30 | 31 | return uint32(len(keyInBytes)) + sizeEntryValue 32 | } 33 | 34 | func NewIndexEntryFromBytes(buf []byte, keyType types.TypeID) *IndexEntry { 35 | dataLen := len(buf) 36 | valPartOffset := dataLen - int(sizeEntryValue) 37 | key := types.NewValueFromBytes(buf[:valPartOffset], keyType) 38 | value := uint64(types.NewUInt64FromBytes(buf[valPartOffset:])) 39 | return &IndexEntry{*key, value} 40 | } 41 | -------------------------------------------------------------------------------- /lib/storage/index/index_constants/index_constants.go: -------------------------------------------------------------------------------- 1 | package index_constants 2 | 3 | type IndexKind int32 4 | 5 | const ( 6 | INDEX_KIND_INVALID IndexKind = iota 7 | INDEX_KIND_UNIQ_SKIP_LIST 8 | INDEX_KIND_SKIP_LIST 9 | INDEX_KIND_HASH 10 | INDEX_KIND_BTREE // B-link tree 11 | ) 12 | -------------------------------------------------------------------------------- /lib/storage/index/index_range_scan_iterator.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/storage/page" 5 | "github.com/ryogrid/SamehadaDB/lib/types" 6 | ) 7 | 8 | type IndexRangeScanIterator interface { 9 | Next() (bool, error, *types.Value, *page.RID) 10 | } 11 | -------------------------------------------------------------------------------- /lib/storage/index/linear_probe_hash_table_index.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/samehada/samehada_util" 5 | "github.com/ryogrid/SamehadaDB/lib/types" 6 | 7 | "github.com/ryogrid/SamehadaDB/lib/container/hash" 8 | "github.com/ryogrid/SamehadaDB/lib/storage/buffer" 9 | "github.com/ryogrid/SamehadaDB/lib/storage/page" 10 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 11 | "github.com/ryogrid/SamehadaDB/lib/storage/tuple" 12 | ) 13 | 14 | type LinearProbeHashTableIndex struct { 15 | // container 16 | container hash.LinearProbeHashTable 17 | metadata *IndexMetadata 18 | // idx of target column on table 19 | col_idx uint32 20 | } 21 | 22 | func NewLinearProbeHashTableIndex(metadata *IndexMetadata, buffer_pool_manager *buffer.BufferPoolManager, col_idx uint32, 23 | num_buckets int, headerPageId types.PageID) *LinearProbeHashTableIndex { 24 | ret := new(LinearProbeHashTableIndex) 25 | ret.metadata = metadata 26 | ret.container = *hash.NewLinearProbeHashTable(buffer_pool_manager, num_buckets, headerPageId) 27 | ret.col_idx = col_idx 28 | return ret 29 | } 30 | 31 | // Return the metadata object associated with the index 32 | func (htidx *LinearProbeHashTableIndex) GetMetadata() *IndexMetadata { return htidx.metadata } 33 | 34 | func (htidx *LinearProbeHashTableIndex) GetIndexColumnCount() uint32 { 35 | return htidx.metadata.GetIndexColumnCount() 36 | } 37 | func (htidx *LinearProbeHashTableIndex) GetName() *string { return htidx.metadata.GetName() } 38 | func (htidx *LinearProbeHashTableIndex) GetTupleSchema() *schema.Schema { 39 | return htidx.metadata.GetTupleSchema() 40 | } 41 | func (htidx *LinearProbeHashTableIndex) GetKeyAttrs() []uint32 { return htidx.metadata.GetKeyAttrs() } 42 | 43 | func (htidx *LinearProbeHashTableIndex) InsertEntry(key *tuple.Tuple, rid page.RID, transaction interface{}) { 44 | tupleSchema_ := htidx.GetTupleSchema() 45 | keyDataInBytes := key.GetValueInBytes(tupleSchema_, htidx.col_idx) 46 | 47 | htidx.container.Insert(keyDataInBytes, samehada_util.PackRIDtoUint64(&rid)) 48 | } 49 | 50 | func (htidx *LinearProbeHashTableIndex) DeleteEntry(key *tuple.Tuple, rid page.RID, transaction interface{}) { 51 | tupleSchema_ := htidx.GetTupleSchema() 52 | keyDataInBytes := key.GetValueInBytes(tupleSchema_, htidx.col_idx) 53 | 54 | htidx.container.Remove(keyDataInBytes, samehada_util.PackRIDtoUint64(&rid)) 55 | } 56 | 57 | func (htidx *LinearProbeHashTableIndex) ScanKey(key *tuple.Tuple, transaction interface{}) []page.RID { 58 | tupleSchema_ := htidx.GetTupleSchema() 59 | keyDataInBytes := key.GetValueInBytes(tupleSchema_, htidx.col_idx) 60 | 61 | packed_values := htidx.container.GetValue(keyDataInBytes) 62 | var ret_arr []page.RID 63 | for _, packed_val := range packed_values { 64 | ret_arr = append(ret_arr, samehada_util.UnpackUint64toRID(packed_val)) 65 | } 66 | return ret_arr 67 | } 68 | 69 | func (htidx *LinearProbeHashTableIndex) UpdateEntry(oldKey *tuple.Tuple, oldRID page.RID, newKey *tuple.Tuple, newRID page.RID, transaction interface{}) { 70 | panic("not implemented yet") 71 | } 72 | 73 | func (htidx *LinearProbeHashTableIndex) GetRangeScanIterator(startkey *tuple.Tuple, endKey *tuple.Tuple, txn interface{}) IndexRangeScanIterator { 74 | return nil 75 | } 76 | 77 | func (htidx *LinearProbeHashTableIndex) GetHeaderPageId() types.PageID { 78 | return htidx.container.GetHeaderPageId() 79 | } 80 | -------------------------------------------------------------------------------- /lib/storage/page/hash_table_block_page.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package page 5 | 6 | import "github.com/ryogrid/SamehadaDB/lib/common" 7 | 8 | type HashTablePair struct { 9 | key uint64 //uint32 10 | value uint64 11 | } 12 | 13 | const sizeOfHashTablePair = 16 //12 14 | const BlockArraySize = 4 * common.PageSize / (4*sizeOfHashTablePair + 1) 15 | 16 | /** 17 | * Store indexed key and value together within block page. Supports 18 | * non-unique keys. 19 | * 20 | * Block page format (keys are stored in order): 21 | * ---------------------------------------------------------------- 22 | * | KEY(1) + VALUE(1) | KEY(2) + VALUE(2) | ... | KEY(n) + VALUE(n) 23 | * ---------------------------------------------------------------- 24 | * 25 | * Here '+' means concatenation. 26 | * 27 | */ 28 | type HashTableBlockPage struct { 29 | occuppied [(BlockArraySize-1)/8 + 1]byte // 43 bytes (344 bits) 30 | readable [(BlockArraySize-1)/8 + 1]byte // 43 bytes (344 bits) 31 | array [BlockArraySize]HashTablePair // 334 * 12 bytes 32 | } 33 | 34 | // Gets the key at an index in the block 35 | func (page *HashTableBlockPage) KeyAt(index uint64) uint64 { 36 | return page.array[index].key 37 | } 38 | 39 | // Gets the value at an index in the block 40 | func (page *HashTableBlockPage) ValueAt(index uint64) uint64 { 41 | return page.array[index].value 42 | } 43 | 44 | // Attempts to insert a key and value into an index in the baccess. 45 | func (page *HashTableBlockPage) Insert(index uint64, key uint64, value uint64) bool { 46 | if page.IsOccupied(index) && page.IsReadable(index) { 47 | return false 48 | } 49 | 50 | page.array[index] = HashTablePair{key, value} 51 | page.occuppied[index/8] |= 1 << (index % 8) 52 | page.readable[index/8] |= 1 << (index % 8) 53 | return true 54 | } 55 | 56 | func (page *HashTableBlockPage) Remove(index uint64) { 57 | if !page.IsReadable(index) { 58 | return 59 | } 60 | 61 | page.readable[index/8] &= ^(1 << (index % 8)) 62 | } 63 | 64 | // Returns whether or not an index is occuppied (valid key/value pair) 65 | func (page *HashTableBlockPage) IsOccupied(index uint64) bool { 66 | return (page.occuppied[index/8] & (1 << (index % 8))) != 0 67 | } 68 | 69 | // Returns whether or not an index is readable (valid key/value pair) 70 | func (page *HashTableBlockPage) IsReadable(index uint64) bool { 71 | return (page.readable[index/8] & (1 << (index % 8))) != 0 72 | } 73 | -------------------------------------------------------------------------------- /lib/storage/page/hash_table_header_page.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package page 5 | 6 | import "github.com/ryogrid/SamehadaDB/lib/types" 7 | 8 | /** 9 | * 10 | * Header Page for linear probing hash table. 11 | * 12 | * Header format (size in byte, 12 bytes in total): 13 | * ---------------------------------------------------------------------- 14 | * | PageId(4) | NextBlockIndex(4) | Size (4) | BlockPageIds (4) x 1020 15 | * ---------------------------------------------------------------------- 16 | * all Page content size: 12 + 8 * 1020 = 4096 17 | */ 18 | type HashTableHeaderPage struct { 19 | pageId types.PageID 20 | //lsn int // log sequence number 21 | nextIndex uint64 // the next index to add a new entry to blockPageIds 22 | size int // the number of key/value pairs the hash table can hold 23 | blockPageIds [1020]types.PageID 24 | } 25 | 26 | func (page *HashTableHeaderPage) GetBlockPageId(index uint64) types.PageID { 27 | return page.blockPageIds[index] 28 | } 29 | 30 | func (page *HashTableHeaderPage) GetPageId() types.PageID { 31 | return page.pageId 32 | } 33 | 34 | func (page *HashTableHeaderPage) SetPageId(pageId types.PageID) { 35 | page.pageId = pageId 36 | } 37 | 38 | func (page *HashTableHeaderPage) AddBlockPageId(pageId types.PageID) { 39 | page.blockPageIds[page.nextIndex] = pageId 40 | page.nextIndex++ 41 | } 42 | 43 | func (page *HashTableHeaderPage) NumBlocks() uint64 { 44 | return page.nextIndex 45 | } 46 | 47 | func (page *HashTableHeaderPage) SetSize(size int) { 48 | page.size = size 49 | } 50 | 51 | func (page *HashTableHeaderPage) GetSize() int { 52 | return page.size 53 | } 54 | -------------------------------------------------------------------------------- /lib/storage/page/page_test.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package page 5 | 6 | import ( 7 | testingpkg "github.com/ryogrid/SamehadaDB/lib/testing/testing_assert" 8 | "testing" 9 | 10 | "github.com/ryogrid/SamehadaDB/lib/common" 11 | "github.com/ryogrid/SamehadaDB/lib/types" 12 | ) 13 | 14 | func TestNewPage(t *testing.T) { 15 | p := New(types.PageID(0), false, &[common.PageSize]byte{}) 16 | 17 | testingpkg.Equals(t, types.PageID(0), p.GetPageId()) 18 | testingpkg.Equals(t, int32(1), p.PinCount()) 19 | p.IncPinCount() 20 | testingpkg.Equals(t, int32(2), p.PinCount()) 21 | p.DecPinCount() 22 | p.DecPinCount() 23 | testingpkg.Equals(t, int32(0), p.PinCount()) 24 | testingpkg.Equals(t, false, p.IsDirty()) 25 | p.SetIsDirty(true) 26 | testingpkg.Equals(t, true, p.IsDirty()) 27 | p.Copy(0, []byte{'H', 'E', 'L', 'L', 'O'}) 28 | testingpkg.Equals(t, [common.PageSize]byte{'H', 'E', 'L', 'L', 'O'}, *p.Data()) 29 | } 30 | 31 | func TestEmptyPage(t *testing.T) { 32 | var pageData [common.PageSize]byte 33 | p := NewEmpty(types.PageID(0), &pageData) 34 | 35 | testingpkg.Equals(t, types.PageID(0), p.GetPageId()) 36 | testingpkg.Equals(t, int32(1), p.PinCount()) 37 | testingpkg.Equals(t, false, p.IsDirty()) 38 | testingpkg.Equals(t, [common.PageSize]byte{}, *p.Data()) 39 | } 40 | -------------------------------------------------------------------------------- /lib/storage/page/rid.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package page 5 | 6 | import ( 7 | "fmt" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | ) 10 | 11 | // RID is the record identifier for the given page identifier and slot number 12 | type RID struct { 13 | PageId types.PageID 14 | SlotNum uint32 15 | } 16 | 17 | // Set sets the recod identifier 18 | func (r *RID) Set(pageId types.PageID, slot uint32) { 19 | r.PageId = pageId 20 | r.SlotNum = slot 21 | } 22 | 23 | // GetPageId gets the page id 24 | func (r *RID) GetPageId() types.PageID { 25 | return r.PageId 26 | } 27 | 28 | // GetSlotNum gets the slot number 29 | func (r *RID) GetSlotNum() uint32 { 30 | return r.SlotNum 31 | } 32 | 33 | func (r *RID) GetAsStr() string { 34 | return fmt.Sprintf("%v", *r) 35 | } 36 | -------------------------------------------------------------------------------- /lib/storage/page/rid_test.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package page 5 | 6 | import ( 7 | testingpkg "github.com/ryogrid/SamehadaDB/lib/testing/testing_assert" 8 | "testing" 9 | 10 | "github.com/ryogrid/SamehadaDB/lib/types" 11 | ) 12 | 13 | func TestRID(t *testing.T) { 14 | rid := RID{} 15 | rid.Set(types.PageID(0), uint32(0)) 16 | testingpkg.Equals(t, types.PageID(0), rid.GetPageId()) 17 | testingpkg.Equals(t, uint32(0), rid.GetSlotNum()) 18 | } 19 | -------------------------------------------------------------------------------- /lib/storage/table/column/column.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package column 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/index/index_constants" 8 | "github.com/ryogrid/SamehadaDB/lib/types" 9 | "strings" 10 | ) 11 | 12 | type Column struct { 13 | // note: columnName field includes table name. e.g. "table1.column1" 14 | // and GetColumnName() returns it as it is. 15 | columnName string 16 | columnType types.TypeID 17 | fixedLength uint32 // For a non-inlined column, this is the size of a pointer. Otherwise, the size of the fixed length column 18 | variableLength uint32 // For an inlined column, 0. Otherwise, the length of the variable length column 19 | columnOffset uint32 // Column offset in the tuple 20 | hasIndex bool // whether the column has index data 21 | indexKind index_constants.IndexKind 22 | indexHeaderPageId types.PageID 23 | isLeft bool // when temporal schema, this is used for join 24 | // should be pointer of subtype of expression.Expression 25 | // this member is used and needed at temporarily created table (schema) on query execution 26 | expr_ interface{} 27 | } 28 | 29 | // indexHeaderPageID should be types.PageID(-1) if there is no special reason 30 | // the ID is set when CreateTable is called 31 | // expr argument should be pointer of subtype of expression.Expression 32 | func NewColumn(name string, columnType types.TypeID, hasIndex bool, indexKind index_constants.IndexKind, indexHeaderPageID types.PageID, expr interface{}) *Column { 33 | // note: alphabets on column name is stored in lowercase 34 | 35 | if columnType != types.Varchar { 36 | return &Column{strings.ToLower(name), columnType, columnType.Size(), 0, 0, hasIndex, indexKind, indexHeaderPageID, true, expr} 37 | } 38 | 39 | return &Column{strings.ToLower(name), types.Varchar, 4, 255, 0, hasIndex, indexKind, indexHeaderPageID, true, expr} 40 | } 41 | 42 | func (c *Column) IsInlined() bool { 43 | return c.columnType != types.Varchar 44 | } 45 | 46 | func (c *Column) GetType() types.TypeID { 47 | return c.columnType 48 | } 49 | 50 | func (c *Column) GetOffset() uint32 { 51 | return c.columnOffset 52 | } 53 | 54 | func (c *Column) SetOffset(offset uint32) { 55 | c.columnOffset = offset 56 | } 57 | 58 | func (c *Column) FixedLength() uint32 { 59 | return c.fixedLength 60 | } 61 | 62 | func (c *Column) SetFixedLength(fixedLength uint32) { 63 | c.fixedLength = fixedLength 64 | } 65 | 66 | func (c *Column) VariableLength() uint32 { 67 | return c.variableLength 68 | } 69 | 70 | func (c *Column) SetVariableLength(variableLength uint32) { 71 | c.variableLength = variableLength 72 | } 73 | 74 | func (c *Column) GetColumnName() string { 75 | return c.columnName 76 | } 77 | 78 | func (c *Column) SetColumnName(colName string) { 79 | c.columnName = colName 80 | } 81 | 82 | func (c *Column) HasIndex() bool { 83 | return c.hasIndex 84 | } 85 | 86 | func (c *Column) SetHasIndex(hasIndex bool) { 87 | c.hasIndex = hasIndex 88 | } 89 | 90 | func (c *Column) IndexKind() index_constants.IndexKind { 91 | return c.indexKind 92 | } 93 | 94 | func (c *Column) SetIndexKind(kind index_constants.IndexKind) { 95 | c.indexKind = kind 96 | } 97 | 98 | func (c *Column) IndexHeaderPageId() types.PageID { 99 | return c.indexHeaderPageId 100 | } 101 | 102 | func (c *Column) SetIndexHeaderPageId(pageId types.PageID) { 103 | c.indexHeaderPageId = pageId 104 | } 105 | 106 | func (c *Column) IsLeft() bool { 107 | return c.isLeft 108 | } 109 | 110 | func (c *Column) SetIsLeft(isLeft bool) { 111 | c.isLeft = isLeft 112 | } 113 | 114 | // returned value should be used with type validation at expression.Expression 115 | func (c *Column) GetExpr() interface{} { 116 | return c.expr_ 117 | } 118 | 119 | func (c *Column) SetExpr(expr interface{}) { 120 | c.expr_ = expr 121 | } 122 | -------------------------------------------------------------------------------- /lib/storage/table/schema/schema.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package schema 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/table/column" 8 | "math" 9 | "strings" 10 | ) 11 | 12 | type Schema struct { 13 | length uint32 // Fixed-length column size, i.e. the number of bytes used by one tuple 14 | columns []*column.Column // All the columns in the schema, inlined and uninlined. 15 | tupleIsInlined bool // True if all the columns are inlined, false otherwise 16 | uninlinedColumns []uint32 // Indices of all uninlined columns 17 | } 18 | 19 | func NewSchema(columns []*column.Column) *Schema { 20 | schema := &Schema{} 21 | schema.tupleIsInlined = true 22 | 23 | var currentOffset uint32 24 | currentOffset = 0 25 | for i := uint32(0); i < uint32(len(columns)); i++ { 26 | column := *columns[i] 27 | 28 | if !column.IsInlined() { 29 | schema.tupleIsInlined = false 30 | schema.uninlinedColumns = append(schema.uninlinedColumns, i) 31 | } 32 | 33 | column.SetOffset(currentOffset) 34 | currentOffset += column.FixedLength() 35 | 36 | schema.columns = append(schema.columns, &column) 37 | } 38 | schema.length = currentOffset 39 | return schema 40 | } 41 | 42 | func (s *Schema) GetColumn(colIndex uint32) *column.Column { 43 | return s.columns[colIndex] 44 | } 45 | 46 | func (s *Schema) GetUnlinedColumns() []uint32 { 47 | return s.uninlinedColumns 48 | } 49 | 50 | func (s *Schema) GetColumnCount() uint32 { 51 | return uint32(len(s.columns)) 52 | } 53 | 54 | func (s *Schema) Length() uint32 { 55 | return s.length 56 | } 57 | 58 | func (s *Schema) GetColIndex(columnName string) uint32 { 59 | // note: alphabets on column name is stored in lowercase 60 | columnName_ := strings.ToLower(columnName) 61 | 62 | for i := uint32(0); i < s.GetColumnCount(); i++ { 63 | if strings.Contains(columnName_, ".") && s.columns[i].GetColumnName() == columnName_ { 64 | return i 65 | } else if !strings.Contains(columnName_, ".") { 66 | if s.columns[i].GetColumnName() == columnName_ { 67 | return i 68 | } else if strings.Contains(s.columns[i].GetColumnName(), ".") && strings.Split(s.columns[i].GetColumnName(), ".")[1] == columnName_ { 69 | return i 70 | } 71 | } 72 | } 73 | 74 | return math.MaxUint32 75 | } 76 | 77 | func (s *Schema) GetColumns() []*column.Column { 78 | return s.columns 79 | } 80 | 81 | func (s *Schema) IsHaveColumn(columnName *string) bool { 82 | for _, col := range s.columns { 83 | if strings.Contains(*columnName, ".") && col.GetColumnName() == *columnName { 84 | return true 85 | } 86 | //} else if !strings.Contains(*columnName, ".") && strings.Split(col.GetColumnName(), ".")[1] == *columnName { 87 | // return true 88 | //} 89 | } 90 | return false 91 | } 92 | 93 | func CopySchema(from *Schema, attrs []uint32) *Schema { 94 | var cols_obj []column.Column 95 | var cols_p []*column.Column 96 | for _, col := range from.columns { 97 | cols_obj = append(cols_obj, *col) 98 | } 99 | for _, col := range cols_obj { 100 | cols_p = append(cols_p, &col) 101 | } 102 | 103 | ret := new(Schema) 104 | ret.columns = cols_p 105 | return ret 106 | } 107 | -------------------------------------------------------------------------------- /lib/storage/tuple/tuple_test.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package tuple 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/storage/index/index_constants" 8 | testingpkg "github.com/ryogrid/SamehadaDB/lib/testing/testing_assert" 9 | "testing" 10 | 11 | "github.com/ryogrid/SamehadaDB/lib/storage/table/column" 12 | "github.com/ryogrid/SamehadaDB/lib/storage/table/schema" 13 | "github.com/ryogrid/SamehadaDB/lib/types" 14 | ) 15 | 16 | func TestTuple(t *testing.T) { 17 | columnA := column.NewColumn("a", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 18 | columnB := column.NewColumn("b", types.Varchar, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 19 | columnC := column.NewColumn("c", types.Integer, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 20 | columnD := column.NewColumn("d", types.Varchar, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 21 | columnE := column.NewColumn("e", types.Varchar, false, index_constants.INDEX_KIND_INVALID, types.PageID(-1), nil) 22 | 23 | schema := schema.NewSchema([]*column.Column{columnA, columnB, columnC, columnD, columnE}) 24 | 25 | row := make([]types.Value, 0) 26 | 27 | expA, expB, expC, expD, expE := int32(99), "Hello World", int32(100), "áé&@#+\\çç", "blablablablabalbalalabalbalbalablablabalbalaba" 28 | row = append(row, types.NewInteger(expA)) 29 | row = append(row, types.NewVarchar(expB)) 30 | row = append(row, types.NewInteger(expC)) 31 | row = append(row, types.NewVarchar(expD)) 32 | row = append(row, types.NewVarchar(expE)) 33 | tuple := NewTupleFromSchema(row, schema) 34 | 35 | testingpkg.Equals(t, expA, tuple.GetValue(schema, 0).ToInteger()) 36 | testingpkg.Equals(t, expB, tuple.GetValue(schema, 1).ToVarchar()) 37 | testingpkg.Equals(t, expC, tuple.GetValue(schema, 2).ToInteger()) 38 | testingpkg.Equals(t, expD, tuple.GetValue(schema, 3).ToVarchar()) 39 | testingpkg.Equals(t, expE, tuple.GetValue(schema, 4).ToVarchar()) 40 | 41 | // added info of isNull(bool, 1byte) * 5 to 96(hos no info of isNull) 42 | testingpkg.Equals(t, uint32(101), tuple.Size()) 43 | } 44 | -------------------------------------------------------------------------------- /lib/testing/testing_assert/assert.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package testing_assert 5 | 6 | import ( 7 | "path/filepath" 8 | "reflect" 9 | "runtime" 10 | "testing" 11 | ) 12 | 13 | // Assert fails the test if the condition is false. 14 | func Assert(tb testing.TB, condition bool, msg string, v ...interface{}) { 15 | if !condition { 16 | _, file, line, _ := runtime.Caller(1) 17 | tb.Errorf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...) 18 | tb.FailNow() 19 | } 20 | } 21 | 22 | func SimpleAssert(tb testing.TB, condition bool) { 23 | if !condition { 24 | _, file, line, _ := runtime.Caller(1) 25 | tb.Errorf("\033[31m%s:%d: \033[39m\n\n", filepath.Base(file), line) 26 | tb.FailNow() 27 | } 28 | } 29 | 30 | // Assert fails the test if the condition is false. 31 | func AssertFalse(tb testing.TB, condition bool, msg string, v ...interface{}) { 32 | if condition { 33 | _, file, line, _ := runtime.Caller(1) 34 | tb.Errorf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...) 35 | tb.FailNow() 36 | } 37 | } 38 | 39 | // Ok fails the test if an err is not nil. 40 | func Ok(tb testing.TB, err error) { 41 | if err != nil { 42 | _, file, line, _ := runtime.Caller(1) 43 | tb.Errorf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error()) 44 | tb.FailNow() 45 | } 46 | } 47 | 48 | // Nok fails the test if an err is nil. 49 | func Nok(tb testing.TB, err error) { 50 | if err == nil { 51 | _, file, line, _ := runtime.Caller(1) 52 | tb.Errorf("\033[31m%s:%d: err should not be nil\n\n", filepath.Base(file), line) 53 | tb.FailNow() 54 | } 55 | } 56 | 57 | // Equals fails the test if exp is not equal to act. 58 | func Equals(tb testing.TB, exp, act interface{}) { 59 | if !reflect.DeepEqual(exp, act) { 60 | _, file, line, _ := runtime.Caller(1) 61 | tb.Errorf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) 62 | tb.FailNow() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/testing/testing_util/testing_utils.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package testing_util 5 | 6 | import ( 7 | "github.com/ryogrid/SamehadaDB/lib/types" 8 | ) 9 | 10 | func GetValue(data interface{}) (value types.Value) { 11 | switch v := data.(type) { 12 | case int: 13 | value = types.NewInteger(int32(v)) 14 | case int32: 15 | value = types.NewInteger(v) 16 | case float32: 17 | value = types.NewFloat(float32(v)) 18 | case string: 19 | value = types.NewVarchar(v) 20 | case bool: 21 | value = types.NewBoolean(v) 22 | case *types.Value: 23 | val := data.(*types.Value) 24 | return *val 25 | } 26 | return 27 | } 28 | 29 | func GetValueType(data interface{}) (value types.TypeID) { 30 | switch data.(type) { 31 | case int, int32: 32 | return types.Integer 33 | case float32: 34 | return types.Float 35 | case string: 36 | return types.Varchar 37 | case bool: 38 | return types.Boolean 39 | case *types.Value: 40 | val := data.(*types.Value) 41 | return val.ValueType() 42 | } 43 | panic("not implemented") 44 | } 45 | -------------------------------------------------------------------------------- /lib/types/column_type_id.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package types 5 | 6 | type TypeID int 7 | 8 | // Every possible SQL type GetPageId 9 | const ( 10 | Invalid TypeID = iota 11 | Boolean 12 | Tinyint 13 | Smallint 14 | Integer 15 | BigInt 16 | Decimal 17 | Float 18 | Varchar 19 | Timestamp 20 | Null 21 | ) 22 | 23 | func (t TypeID) Size() uint32 { 24 | switch t { 25 | case Integer: 26 | return 1 + 4 27 | case Float: 28 | return 1 + 4 29 | case Boolean: 30 | return 1 + 1 31 | } 32 | return 0 33 | } 34 | -------------------------------------------------------------------------------- /lib/types/lsn.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package types 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "sync/atomic" 10 | "unsafe" 11 | ) 12 | 13 | // LSN is the type of the log identifier 14 | type LSN int32 15 | 16 | const SizeOfLSN = 4 17 | 18 | // Serialize casts it to []byte 19 | func (lsn LSN) Serialize() []byte { 20 | buf := new(bytes.Buffer) 21 | binary.Write(buf, binary.LittleEndian, lsn) 22 | return buf.Bytes() 23 | } 24 | 25 | // NewLSNFromBytes creates a LSN from []byte 26 | func NewLSNFromBytes(data []byte) (ret LSN) { 27 | binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &ret) 28 | return ret 29 | } 30 | 31 | func (lsn LSN) AtomicAdd(val int32) { 32 | p := (*int32)(unsafe.Pointer(&lsn)) 33 | atomic.AddInt32(p, val) 34 | } 35 | -------------------------------------------------------------------------------- /lib/types/page_id.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package types 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "github.com/ryogrid/SamehadaDB/lib/errors" 10 | ) 11 | 12 | // PageID is the type of the page identifier 13 | type PageID int32 14 | 15 | const DeallocatedPageErr = errors.Error("dellocated Page ID is passed.") 16 | 17 | // InvalidPageID represents an invalid page GetPageId 18 | const InvalidPageID = PageID(-1) 19 | 20 | // IsValid checks if id is valid 21 | func (id PageID) IsValid() bool { 22 | return id != InvalidPageID || id >= 0 23 | } 24 | 25 | // Serialize casts it to []byte 26 | func (id PageID) Serialize() []byte { 27 | buf := new(bytes.Buffer) 28 | binary.Write(buf, binary.LittleEndian, id) 29 | return buf.Bytes() 30 | } 31 | 32 | // NewPageIDFromBytes creates a page id from []byte 33 | func NewPageIDFromBytes(data []byte) (ret PageID) { 34 | binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &ret) 35 | return ret 36 | } 37 | -------------------------------------------------------------------------------- /lib/types/txn_id.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package types 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "sync/atomic" 10 | "unsafe" 11 | ) 12 | 13 | // TxnID is the type of the transaction identifier 14 | type TxnID int32 15 | 16 | // Serialize casts it to []byte 17 | func (id TxnID) Serialize() []byte { 18 | buf := new(bytes.Buffer) 19 | binary.Write(buf, binary.LittleEndian, id) 20 | return buf.Bytes() 21 | } 22 | 23 | // NewPageIDFromBytes creates a page id from []byte 24 | func NewTxnIDFromBytes(data []byte) (ret TxnID) { 25 | binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &ret) 26 | return ret 27 | } 28 | 29 | func (id *TxnID) AtomicAdd(val int32) { 30 | p := (*int32)(unsafe.Pointer(id)) 31 | atomic.AddInt32(p, val) 32 | } 33 | -------------------------------------------------------------------------------- /lib/types/uint32.go: -------------------------------------------------------------------------------- 1 | // this code is from https://github.com/brunocalza/go-bustub 2 | // there is license and copyright notice in licenses/go-bustub dir 3 | 4 | package types 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | ) 10 | 11 | type UInt16 uint16 12 | type UInt32 uint32 13 | type UInt64 uint64 14 | type Int32 int32 15 | type Bool bool 16 | 17 | // Serialize casts it to []byte 18 | func (id UInt16) Serialize() []byte { 19 | buf := new(bytes.Buffer) 20 | binary.Write(buf, binary.BigEndian, id) 21 | return buf.Bytes() 22 | } 23 | 24 | func NewUInt16FromBytes(data []byte) (ret_ UInt16) { 25 | var ret UInt16 26 | binary.Read(bytes.NewBuffer(data), binary.BigEndian, &ret) 27 | return ret 28 | } 29 | 30 | // Serialize casts it to []byte 31 | func (id UInt32) Serialize() []byte { 32 | buf := new(bytes.Buffer) 33 | binary.Write(buf, binary.LittleEndian, id) 34 | return buf.Bytes() 35 | } 36 | 37 | // Serialize casts it to []byte 38 | func (id UInt64) Serialize() []byte { 39 | buf := new(bytes.Buffer) 40 | binary.Write(buf, binary.LittleEndian, id) 41 | return buf.Bytes() 42 | } 43 | 44 | func NewUInt32FromBytes(data []byte) (ret_ UInt32) { 45 | var ret UInt32 46 | binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &ret) 47 | return ret 48 | } 49 | 50 | func NewUInt64FromBytes(data []byte) (ret_ UInt64) { 51 | var ret UInt64 52 | binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &ret) 53 | return ret 54 | } 55 | 56 | // Serialize casts it to []byte 57 | func (id Int32) Serialize() []byte { 58 | buf := new(bytes.Buffer) 59 | binary.Write(buf, binary.LittleEndian, id) 60 | return buf.Bytes() 61 | } 62 | 63 | func NewInt32FromBytes(data []byte) (ret_ Int32) { 64 | var ret Int32 65 | binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &ret) 66 | return ret 67 | } 68 | 69 | // Serialize casts it to []byte 70 | func (flag Bool) Serialize() []byte { 71 | buf := new(bytes.Buffer) 72 | binary.Write(buf, binary.LittleEndian, flag) 73 | return buf.Bytes() 74 | } 75 | 76 | func NewBoolFromBytes(data []byte) (ret_ Bool) { 77 | var ret Bool 78 | binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &ret) 79 | return ret 80 | } 81 | -------------------------------------------------------------------------------- /licenses/BusTub/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 CMU Database Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /licenses/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 SamehadaDB team members 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /licenses/go-bustub/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bruno Calza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /licenses/goostub/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Qitian Zeng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /samehada_logo2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryogrid/SamehadaDB/443040120976a2935ba89adcc197a644c7b0236b/samehada_logo2.webp -------------------------------------------------------------------------------- /server/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ryogrid/SamehadaDB/server 2 | 3 | replace github.com/ryogrid/SamehadaDB/lib => ../lib 4 | 5 | go 1.21 6 | 7 | toolchain go1.21.2 8 | 9 | //replace github.com/ryogrid/bltree-go-for-embedding => ../../bltree-go-for-embedding 10 | 11 | require ( 12 | github.com/ant0ine/go-json-rest v3.3.2+incompatible 13 | github.com/ryogrid/SamehadaDB/lib v0.0.0-20240725021953-263ea0c5c011 14 | github.com/vmihailenco/msgpack/v5 v5.4.1 15 | ) 16 | 17 | require ( 18 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect 19 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect 20 | github.com/deckarep/golang-set/v2 v2.3.0 // indirect 21 | github.com/devlights/gomy v0.4.0 // indirect 22 | github.com/dsnet/golib/memfile v1.0.0 // indirect 23 | github.com/go-ole/go-ole v1.2.4 // indirect 24 | github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect 25 | github.com/golang/protobuf v1.3.4 // indirect 26 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect 27 | github.com/notEpsilon/go-pair v0.0.0-20221220200415-e91ef28c6c0b // indirect 28 | github.com/opentracing/opentracing-go v1.1.0 // indirect 29 | github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011 // indirect 30 | github.com/pingcap/log v0.0.0-20200511115504-543df19646ad // indirect 31 | github.com/pingcap/parser v0.0.0-20200623164729-3a18f1e5dceb // indirect 32 | github.com/pingcap/tidb v1.1.0-beta.0.20200630082100-328b6d0a955c // indirect 33 | github.com/pingcap/tipb v0.0.0-20200522051215-f31a15d98fce // indirect 34 | github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 // indirect 35 | github.com/ryogrid/bltree-go-for-embedding v1.0.11 // indirect 36 | github.com/shirou/gopsutil v2.19.10+incompatible // indirect 37 | github.com/sirupsen/logrus v1.6.0 // indirect 38 | github.com/spaolacci/murmur3 v1.1.0 // indirect 39 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 40 | go.uber.org/atomic v1.6.0 // indirect 41 | go.uber.org/multierr v1.5.0 // indirect 42 | go.uber.org/zap v1.15.0 // indirect 43 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 44 | golang.org/x/sys v0.12.0 // indirect 45 | golang.org/x/text v0.13.0 // indirect 46 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 47 | ) 48 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ant0ine/go-json-rest/rest" 6 | "github.com/ryogrid/SamehadaDB/lib/samehada" 7 | "github.com/ryogrid/SamehadaDB/server/signal_handle" 8 | "github.com/vmihailenco/msgpack/v5" 9 | "log" 10 | "net/http" 11 | "os" 12 | ) 13 | 14 | type QueryInput struct { 15 | Query string 16 | } 17 | 18 | type Row struct { 19 | C []interface{} 20 | } 21 | 22 | type QueryOutput struct { 23 | Result []Row 24 | Error string 25 | } 26 | 27 | var db = samehada.NewSamehadaDB("default", 5000) //5MB 28 | var IsStopped = false 29 | 30 | func postQuery(w rest.ResponseWriter, req *rest.Request) { 31 | if signal_handle.IsStopped { 32 | rest.Error(w, "Server is stopped", http.StatusGone) 33 | return 34 | } 35 | 36 | input := QueryInput{} 37 | err := req.DecodeJsonPayload(&input) 38 | 39 | if err != nil { 40 | fmt.Println(err) 41 | rest.Error(w, err.Error(), http.StatusBadRequest) 42 | return 43 | } 44 | 45 | if input.Query == "" { 46 | rest.Error(w, "Query is required", 400) 47 | return 48 | } 49 | 50 | err2, results := db.ExecuteSQL(input.Query) 51 | if err2 != nil { 52 | rest.Error(w, err2.Error(), http.StatusBadRequest) 53 | return 54 | } 55 | 56 | rows := make([]Row, 0) 57 | for _, row := range results { 58 | rows = append(rows, Row{row}) 59 | } 60 | 61 | w.WriteJson(&QueryOutput{ 62 | rows, "SUCCESS", 63 | }) 64 | } 65 | 66 | func postQueryMsgPack(w rest.ResponseWriter, req *rest.Request) { 67 | if signal_handle.IsStopped { 68 | http.Error(w.(http.ResponseWriter), "Server is stopped", http.StatusGone) 69 | return 70 | } 71 | 72 | input := QueryInput{} 73 | err := req.DecodeJsonPayload(&input) 74 | 75 | if err != nil { 76 | fmt.Println(err) 77 | http.Error(w.(http.ResponseWriter), err.Error(), http.StatusBadRequest) 78 | return 79 | } 80 | 81 | if input.Query == "" { 82 | http.Error(w.(http.ResponseWriter), "Query is required", 400) 83 | return 84 | } 85 | 86 | err2, results := db.ExecuteSQL(input.Query) 87 | if err2 != nil { 88 | http.Error(w.(http.ResponseWriter), err2.Error(), http.StatusBadRequest) 89 | return 90 | } 91 | 92 | rows := make([]Row, 0) 93 | for _, row := range results { 94 | rows = append(rows, Row{row}) 95 | } 96 | 97 | ret := QueryOutput{rows, "SUCCESS"} 98 | b, err := msgpack.Marshal(&ret) 99 | if err != nil { 100 | panic(err) 101 | } 102 | 103 | //var decoded []Row 104 | //err = msgpack.Unmarshal(b, &decoded) 105 | //if err != nil { 106 | // panic(err) 107 | //} 108 | //fmt.Println(decoded) 109 | 110 | w.Header().Set("Content-Type", "application/octent-stream") 111 | w.(http.ResponseWriter).Write(b) 112 | } 113 | 114 | func launchDBAndListen() { 115 | api := rest.NewApi() 116 | 117 | // the Middleware stack 118 | api.Use(rest.DefaultDevStack...) 119 | api.Use(&rest.JsonpMiddleware{ 120 | CallbackNameKey: "cb", 121 | }) 122 | api.Use(&rest.CorsMiddleware{ 123 | RejectNonCorsRequests: false, 124 | OriginValidator: func(origin string, request *rest.Request) bool { 125 | return true 126 | }, 127 | AllowedMethods: []string{"POST"}, 128 | AllowedHeaders: []string{"Accept", "content-type"}, 129 | AccessControlAllowCredentials: true, 130 | AccessControlMaxAge: 3600, 131 | }) 132 | 133 | router, err := rest.MakeRouter( 134 | &rest.Route{"POST", "/Query", postQuery}, 135 | &rest.Route{"POST", "/QueryMsgPack", postQueryMsgPack}, 136 | ) 137 | if err != nil { 138 | log.Fatal(err) 139 | } 140 | api.SetApp(router) 141 | 142 | log.Printf("Server started") 143 | log.Fatal(http.ListenAndServe( 144 | "0.0.0.0:19999", 145 | api.MakeHandler(), 146 | )) 147 | } 148 | 149 | func main() { 150 | exitNotifyCh := make(chan bool, 1) 151 | 152 | // start signal handler thread 153 | go signal_handle.SignalHandlerTh(db, &exitNotifyCh) 154 | 155 | // start server 156 | go launchDBAndListen() 157 | 158 | // wait shutdown operation finished notification 159 | <-exitNotifyCh 160 | 161 | fmt.Println("Server is stopped gracefully") 162 | // exit process 163 | os.Exit(0) 164 | } 165 | -------------------------------------------------------------------------------- /server/signal_handle/signal_handler.go: -------------------------------------------------------------------------------- 1 | package signal_handle 2 | 3 | import ( 4 | "github.com/ryogrid/SamehadaDB/lib/samehada" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | ) 9 | 10 | var IsStopped = false 11 | 12 | func SignalHandlerTh(db *samehada.SamehadaDB, exitNotifyCh *chan bool) { 13 | sigChan := make(chan os.Signal, 1) 14 | // receive SIGINT only 15 | signal.Ignore() 16 | signal.Notify(sigChan, syscall.SIGINT) 17 | 18 | // block until receive SIGINT 19 | <-sigChan 20 | 21 | // ---- after receive SIGINT --- 22 | 23 | // stop handle request 24 | IsStopped = true 25 | 26 | // shutdown SamehadaDB object 27 | db.Shutdown() 28 | 29 | // notify that shutdown operation finished to main thread 30 | *exitNotifyCh <- true 31 | } 32 | -------------------------------------------------------------------------------- /todo-comment-lister.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import os, sys, io 3 | 4 | additonalFilter = None 5 | 6 | def listTodoComments(): 7 | for root, dirs, files in os.walk(top='./'): 8 | for file in files: 9 | filePath = os.path.join(root, file) 10 | filePathToPrint = filePath.replace('\\', '/') 11 | isPathPrinted = False 12 | if filePath.endswith('.go'): 13 | with open(filePath, mode='r', encoding='utf-8') as f: 14 | lineNum = 1 15 | for line in f: 16 | try: 17 | if line.find('TODO') > -1 and line.find('(SDB)') > -1: 18 | if additonalFilter and line.find(additonalFilter) == -1: 19 | continue 20 | 21 | if not isPathPrinted: 22 | print(filePathToPrint) 23 | isPathPrinted = True 24 | stripedLine = line.strip() 25 | print(' ' + '{:<5}'.format(str(lineNum) + ':') + ' ' + stripedLine) 26 | finally: 27 | lineNum += 1 28 | 29 | def main(): 30 | global additonalFilter 31 | 32 | sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') 33 | sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') 34 | 35 | # additonal filtering option: -f "" 36 | if len(sys.argv) == 3 and sys.argv[1] == '-f': 37 | additonalFilter = sys.argv[2] 38 | 39 | listTodoComments() 40 | 41 | main() --------------------------------------------------------------------------------