├── .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 |
4 |
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 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_TestKeyDuplicateSkipListPrallelTxnStrideVarchar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_TestSkipListBench8_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_TestSkipListParallelSimpleInteger3Stride.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
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 |
13 |
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 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_TestSkipList.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_TestSkipList_LongRun_only.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_TestSkipList__short_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_blink_tree.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_executors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_executors__short_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_index_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_lib.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_test_recovery.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
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()
--------------------------------------------------------------------------------