├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── auto-comment.yml │ ├── codeql-analysis.yml │ ├── go.yml │ └── linelint.yml ├── .gitignore ├── .gitlab └── merge_request_templates │ └── mr.md ├── .linelint.yml ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── backend ├── assets │ ├── image-20220314214316673.png │ ├── image-20220314215924425.png │ ├── image-20220314220921338.png │ ├── image-20220315221559157.png │ ├── image-20220318083633693.png │ ├── image-20220318183833245.png │ ├── image-20220319002738908.png │ └── image-20220319113026919.png ├── balancer.go ├── balancer_test.go ├── connection_pool.go ├── direct_connection.go ├── direct_connection_cn.md ├── direct_connection_en.md ├── direct_connection_test.go ├── interface.go ├── mock_connection_pool.go ├── mock_polled_connect.go ├── pooled_connection.go ├── slice.go └── slice_test.go ├── cc ├── proxy │ ├── proxy.go │ └── proxy_api.go ├── server.go └── service │ └── service.go ├── cmd ├── gaea-cc │ └── main.go └── gaea │ └── main.go ├── core ├── errors │ └── errors.go └── version.go ├── docs ├── architecture.md ├── assets │ ├── 1-4-1-read-only-CPU.jpg │ ├── 1-4-1-read-only-P99.jpg │ ├── 1-4-1-read-only-QPS.jpg │ ├── 1-4-1-read-only-TPS.jpg │ ├── 1-4-2-read-write-CPU.jpg │ ├── 1-4-2-read-write-P99.jpg │ ├── 1-4-2-read-write-QPS.jpg │ ├── 1-4-2-read-write-TPS.jpg │ ├── 1-4-3-write-only-CPU.jpg │ ├── 1-4-3-write-only-P99.jpg │ ├── 1-4-3-write-only-QPS.jpg │ ├── 1-4-3-write-only-TPS.jpg │ ├── 1-4-4-point-select-CPU.jpg │ ├── 1-4-4-point-select-P99.jpg │ ├── 1-4-4-point-select-QPS.jpg │ ├── 1-4-4-point-select-TPS.jpg │ ├── 2-4-1-read-only-CPU.jpg │ ├── 2-4-1-read-only-P99.jpg │ ├── 2-4-1-read-only-QPS.jpg │ ├── 2-4-1-read-only-TPS.jpg │ ├── 2-4-2-read-write-CPU.jpg │ ├── 2-4-2-read-write-P99.jpg │ ├── 2-4-2-read-write-QPS.jpg │ ├── 2-4-2-read-write-TPS.jpg │ ├── 2-4-3-write-only-CPU.jpg │ ├── 2-4-3-write-only-P99.jpg │ ├── 2-4-3-write-only-QPS.jpg │ ├── 2-4-3-write-only-TPS.jpg │ ├── 2-4-4-point-select-CPU.jpg │ ├── 2-4-4-point-select-P99.jpg │ ├── 2-4-4-point-select-QPS.jpg │ ├── 2-4-4-point-select-TPS.jpg │ ├── architecture.png │ ├── deployment.png │ ├── feishu_talk.jpeg │ ├── gaea_dingtalk.png │ ├── master-slave.jpg │ ├── master-slaves.jpg │ └── singlemaster.jpg ├── compatibility.md ├── concepts.md ├── config-reloading.md ├── configuration.md ├── connection-pool.md ├── faq.md ├── gaea-cc.md ├── grafana.md ├── multi-tenant.md ├── performance-test-report.md ├── prepare.md ├── quickstart.md ├── sequence-id.md ├── shard-example.md ├── shard.md └── template │ ├── gaea_namespace.json │ └── gaea_proxy.json ├── etc ├── file │ └── namespace │ │ ├── test_namespace_1.json │ │ ├── test_namespace_2.json │ │ └── test_namespace_shard.json ├── gaea.ini └── gaea_cc.ini ├── gen_ldflags.sh ├── gen_version.sh ├── genver.sh ├── go.mod ├── go.sum ├── hack ├── e2e-mysql5.sh ├── e2e-mysql8.sh ├── ginkgo-run-mysql5.sh ├── ginkgo-run-mysql8.sh ├── update-EOF.sh ├── update_e2e.sh ├── verify-EOF.sh └── verify-spelling.sh ├── log ├── logger.go ├── logger_test.go ├── xlog │ ├── console.go │ ├── console_test.go │ ├── file.go │ ├── interface.go │ ├── logger.go │ ├── logger_test.go │ ├── util.go │ └── util_test.go └── zap │ ├── encoder.go │ ├── encoder_test.go │ ├── logger.go │ ├── logger_test.go │ ├── syncer.go │ └── syncer_test.go ├── misc ├── git │ ├── hooks │ │ ├── gofmt │ │ ├── goimports │ │ ├── golint │ │ └── govet │ └── pre-commit ├── gofmt-all └── golint-all ├── models ├── README.md ├── assets │ ├── image-20220124113553383.png │ ├── image-20220124142003588.png │ ├── image-20220124143836664.png │ ├── image-20220124145641452.png │ ├── image-20220124154131750.png │ ├── image-20220124182118946.png │ ├── image-20220124182833234.png │ ├── image-20220124182944970.png │ ├── image-20220124183045456.png │ ├── image-20220124183141813.png │ ├── image-20220124183228814.png │ ├── image-20220124183310893.png │ ├── image-20220126175410955.png │ ├── image-20220127114256168.png │ └── image-20220127142531692.png ├── cc.go ├── connection.md ├── encode.go ├── encode_test.go ├── etcd │ ├── etcd.go │ └── etcd_test.go ├── etcdv3 │ ├── README.md │ ├── assets │ │ ├── image-20220113163203011.png │ │ ├── image-20220113164918661.png │ │ ├── image-20220117035130712.png │ │ ├── image-20220117040619272.png │ │ ├── image-20220117040820166.png │ │ ├── image-20220117041532732.png │ │ ├── image-20220117041953692.png │ │ ├── image-20220117042408542.png │ │ ├── image-20220117043339429.png │ │ ├── image-20220117112954091.png │ │ └── image-20220117143235293.png │ ├── etcdv3.go │ └── etcdv3_test.go ├── file │ └── file.go ├── namespace.go ├── namespace_test.go ├── numkey.go ├── proxy.go ├── proxy_monitor_metric.go ├── sequence.go ├── shard.go ├── shard_rule.go ├── slice.go ├── store.go ├── store_test.go └── user.go ├── mysql ├── charset.go ├── charset_tidb.go ├── charset_tidb_test.go ├── conn.go ├── conn_test.go ├── constants.go ├── encoding.go ├── encoding_test.go ├── error.go ├── error_code.go ├── error_name.go ├── error_state.go ├── error_test.go ├── field.go ├── field_test.go ├── result.go ├── result_pool.go ├── result_pool_test.go ├── resultset_sort.go ├── resultset_sort_test.go ├── sql_fingerprint.go ├── sql_fingerprint_test.go ├── type.go ├── type_test.go ├── util.go ├── util_test.go ├── variables.go └── variables_test.go ├── parser ├── Makefile ├── README.md ├── analyzer.go ├── analyzer_test.go ├── ast │ ├── ast.go │ ├── base.go │ ├── ddl.go │ ├── ddl_test.go │ ├── dml.go │ ├── dml_test.go │ ├── expressions.go │ ├── expressions_test.go │ ├── flag.go │ ├── flag_test.go │ ├── format_test.go │ ├── functions.go │ ├── functions_test.go │ ├── misc.go │ ├── misc_test.go │ ├── stats.go │ ├── util.go │ └── util_test.go ├── auth │ ├── auth.go │ └── auth_test.go ├── comments.go ├── comments_test.go ├── format │ ├── format.go │ └── format_test.go ├── goyacc │ ├── Makefile │ ├── go.mod │ ├── go.sum │ └── main.go ├── lexer.go ├── lexer_test.go ├── misc.go ├── model │ ├── ddl.go │ ├── flags.go │ ├── model.go │ └── model_test.go ├── opcode │ ├── opcode.go │ └── opcode_test.go ├── parser.go ├── parser.y ├── parser_example_test.go ├── parser_test.go ├── stmtctx │ └── stmtctx.go ├── terror │ ├── terror.go │ └── terror_test.go ├── tidb-types │ ├── binary_literal.go │ ├── binary_literal_test.go │ ├── compare.go │ ├── compare_test.go │ ├── convert.go │ ├── convert_test.go │ ├── datum.go │ ├── datum_eval.go │ ├── datum_test.go │ ├── enum.go │ ├── enum_test.go │ ├── errors.go │ ├── etc.go │ ├── etc_test.go │ ├── eval_type.go │ ├── export_test.go │ ├── field_type.go │ ├── field_type_test.go │ ├── fsp.go │ ├── fsp_test.go │ ├── helper.go │ ├── helper_test.go │ ├── json │ │ ├── binary.go │ │ ├── binary_functions.go │ │ ├── binary_test.go │ │ ├── constants.go │ │ ├── path_expr.go │ │ └── path_expr_test.go │ ├── mydecimal.go │ ├── mydecimal_test.go │ ├── mytime.go │ ├── mytime_test.go │ ├── overflow.go │ ├── overflow_test.go │ ├── parser_driver │ │ ├── value_expr.go │ │ └── value_expr_test.go │ ├── set.go │ ├── set_test.go │ └── time.go ├── types │ ├── etc.go │ ├── eval_type.go │ └── field_type.go ├── yy_parser.go └── yy_parser_test.go ├── proxy ├── plan │ ├── decorator_between_expr.go │ ├── decorator_binary_operation_expr.go │ ├── decorator_column_name.go │ ├── decorator_limit.go │ ├── decorator_pattern_in_expr.go │ ├── decorator_table_name.go │ ├── merge_result.go │ ├── merge_result_test.go │ ├── plan.go │ ├── plan_delete.go │ ├── plan_delete_test.go │ ├── plan_exec_test.go │ ├── plan_explain.go │ ├── plan_explain_test.go │ ├── plan_insert.go │ ├── plan_insert_test.go │ ├── plan_select.go │ ├── plan_select_subquery.go │ ├── plan_select_test.go │ ├── plan_test.go │ ├── plan_tidb_test.go │ ├── plan_unshard.go │ ├── plan_unshard_test.go │ ├── plan_update.go │ ├── plan_update_test.go │ ├── route_result.go │ ├── util.go │ └── util_test.go ├── router │ ├── list_test.go │ ├── numkey.go │ ├── router.go │ ├── rule.go │ ├── rule_test.go │ ├── shard.go │ ├── shard_mycat.go │ ├── shard_mycat_test.go │ └── shard_test.go ├── sequence │ ├── mysql.go │ └── sequence.go └── server │ ├── admin.go │ ├── client_conn.go │ ├── client_conn_test.go │ ├── executor.go │ ├── executor_handle.go │ ├── executor_stmt.go │ ├── executor_stmt_test.go │ ├── executor_test.go │ ├── manager.go │ ├── manager_test.go │ ├── namespace.go │ ├── namespace_test.go │ ├── server.go │ ├── session.go │ ├── session_test.go │ └── var.go ├── stats ├── counter.go ├── counter_test.go ├── counters.go ├── counters_test.go ├── duration.go ├── duration_test.go ├── export.go ├── export_test.go ├── histogram.go ├── histogram_test.go ├── kebab_case_converter.go ├── kebab_case_converter_test.go ├── multidimensional.go ├── multidimensional_test.go ├── prometheus │ ├── collectors.go │ ├── prometheus_backend.go │ └── prometheus_backend_test.go ├── rates.go ├── rates_test.go ├── ring.go ├── snake_case_converter.go ├── snake_case_converter_test.go ├── timings.go ├── timings_test.go ├── util.go └── variable_interface.go ├── tests ├── docker │ ├── mysql5 │ │ ├── Dockerfile │ │ ├── install_dependencies.sh │ │ ├── my3319.cnf │ │ ├── my3329.cnf │ │ ├── my3339.cnf │ │ ├── my3349.cnf │ │ └── my3379.cnf │ └── mysql8 │ │ ├── Dockerfile │ │ ├── install_dependencies_mysql8.sh │ │ ├── my3319.cnf │ │ ├── my3329.cnf │ │ ├── my3339.cnf │ │ ├── my3349.cnf │ │ └── my3379.cnf └── e2e │ ├── cmd │ ├── gaea.ini │ └── gaea_cc.ini │ ├── config │ ├── cluster.go │ ├── manager.go │ ├── ns │ │ ├── default.template │ │ ├── kingshard_hash.template │ │ ├── kingshard_mod.template │ │ ├── mycat_long.template │ │ ├── mycat_mod.template │ │ ├── mycat_murmur.template │ │ ├── mycat_string.template │ │ ├── shard.template │ │ ├── unshard.template │ │ └── unshard_dml.template │ └── plan.go │ ├── dml │ ├── case │ │ ├── delete.json │ │ ├── insert.json │ │ ├── replace.json │ │ ├── select.json │ │ ├── sql.json │ │ └── update.json │ ├── dml_set.go │ └── dml_set_invalid.go │ ├── e2e_test.go │ ├── function │ ├── auth_plugin.go │ ├── bad_conn.go │ ├── basic_sqls.go │ ├── client_limit.go │ ├── collation_set.go │ ├── keep_session.go │ ├── loadbalance.go │ ├── masterhint.go │ ├── multi_query.go │ ├── prepare_stmt.go │ ├── qps_limit.go │ ├── readwritesep.go │ ├── session_variables.go │ ├── set_session_read_only.go │ ├── show_type.go │ ├── slave_fuse.go │ └── table_fuse.go │ ├── shard │ ├── case │ │ ├── dml │ │ │ ├── kingdate_month.json │ │ │ ├── kingdate_year.json │ │ │ ├── kinghash.json │ │ │ ├── kingmod.json │ │ │ ├── kingrange.json │ │ │ ├── mycatlong.json │ │ │ ├── mycatmod.json │ │ │ ├── mycatmod_different_table.json │ │ │ ├── mycatmurmur.json │ │ │ └── mycatstring.json │ │ └── join │ │ │ ├── 0-gaea-prepare.sql │ │ │ ├── 0-slice0-kingshard.sql │ │ │ ├── 0-slice0-mycat.sql │ │ │ ├── 0-slice1-kingshard.sql │ │ │ ├── 0-slice1-mycat.sql │ │ │ ├── 0-test-prepare.sql │ │ │ ├── kingshard │ │ │ ├── equal.sql │ │ │ ├── show.sql │ │ │ ├── skip.sql │ │ │ ├── unequal.sql │ │ │ └── unsupport.sql │ │ │ └── mycat │ │ │ ├── long │ │ │ ├── equal.sql │ │ │ ├── show.sql │ │ │ ├── skip.sql │ │ │ ├── unequal.sql │ │ │ └── unsupport.sql │ │ │ ├── mod │ │ │ ├── equal.sql │ │ │ ├── show.sql │ │ │ ├── skip.sql │ │ │ ├── unequal.sql │ │ │ └── unsupport.sql │ │ │ ├── murmur │ │ │ ├── equal.sql │ │ │ ├── show.sql │ │ │ ├── skip.sql │ │ │ ├── unequal.sql │ │ │ └── unsupport.sql │ │ │ └── string │ │ │ ├── equal.sql │ │ │ ├── show.sql │ │ │ ├── skip.sql │ │ │ ├── unequal.sql │ │ │ └── unsupport.sql │ ├── kingshard_hash.go │ ├── kingshard_mod.go │ ├── mycat_long.go │ ├── mycat_mod.go │ ├── mycat_murmur.go │ ├── mycat_string.go │ └── shard.go │ ├── unshard │ ├── case │ │ ├── dml │ │ │ ├── delete.json │ │ │ ├── insert.json │ │ │ ├── replace.json │ │ │ ├── select.json │ │ │ ├── sql.json │ │ │ └── update.json │ │ └── join │ │ │ ├── 0-prepare.sql │ │ │ ├── 1-clean.sql │ │ │ ├── skip.sql │ │ │ ├── test_join.sql │ │ │ ├── test_join_unsharding.sql │ │ │ ├── test_select_global_old.sql │ │ │ ├── test_show.sql │ │ │ ├── test_simple.sql │ │ │ └── test_subquery_global.sql │ ├── dml.go │ └── unshard.go │ └── util │ ├── action.go │ ├── constant.go │ ├── db.go │ ├── expect.go │ └── log.go └── util ├── array.go ├── array_test.go ├── bucketpool ├── bucketpool.go └── bucketpool_test.go ├── cache ├── lru_cache.go ├── lru_cache_test.go └── perf_test.go ├── crypto ├── xaes_ecb.go └── xaes_test.go ├── hack ├── hack.go └── hack_test.go ├── ip.go ├── ip_test.go ├── math └── compare.go ├── mocks └── pipeTest │ ├── README.md │ ├── assets │ ├── PipeTest测试函数抽换缓存时序图.png │ ├── PipeTest测试函数抽换缓存时序图.txt │ ├── pipeTest测试临时函数介入时序图.png │ ├── pipeTest测试临时函数介入时序图.txt │ ├── pipeTest的对象图.png │ ├── pipeTest的对象图.txt │ ├── pipeTest的类图.png │ ├── pipeTest的类图.txt │ ├── pipeTest连续测试时序图.png │ ├── pipeTest连续测试时序图.txt │ ├── 单一连线方向重置.png │ └── 单一连线方向重置.txt │ ├── pipeTest.go │ └── pipeTest_test.go ├── murmur.go ├── padding.go ├── request_context.go ├── requests ├── api.go └── api_test.go ├── resource_pool.go ├── resource_pool_test.go ├── string.go ├── string_test.go ├── sync2 ├── atomic.go ├── atomic_test.go ├── doc.go ├── semaphore.go └── semaphore_flaky_test.go ├── testleak ├── add-leaktest.sh ├── check-leaktest.sh ├── fake.go └── leaktest.go ├── time_wheel.go ├── time_wheel_test.go ├── timer ├── randticker.go ├── randticker_flaky_test.go ├── timer.go └── timer_flaky_test.go ├── types.go ├── util.go ├── version.go └── version_test.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/auto-comment.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | name: Auto Comment 21 | 22 | on: 23 | issues: 24 | types: [opened] # 触发条件:当问题被创建时 25 | pull_request: 26 | types: [opened] # 触发条件:当拉取请求被创建时 27 | 28 | permissions: 29 | contents: write 30 | issues: write 31 | pull-requests: write 32 | 33 | jobs: 34 | comment: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - name: Comment on issue 38 | uses: actions/github-script@v4 39 | with: 40 | script: | 41 | const issueOpened = "Thank you for raising an issue. We will try and get back to you as soon as possible. Please make sure you have given us as much context as possible."; 42 | const pullRequestOpened = "Thank you for raising your pull request. Please make sure you have followed our contributing guidelines. We will review it as soon as possible."; 43 | 44 | if (context.payload.action === 'opened') { 45 | const issueComment = context.payload.issue ? issueOpened : pullRequestOpened; 46 | await github.issues.createComment({ 47 | ...context.repo, 48 | issue_number: context.payload.issue ? context.payload.issue.number : context.payload.pull_request.number, 49 | body: issueComment 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/linelint.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | name: Linelint 21 | 22 | on: 23 | push: 24 | pull_request: 25 | 26 | jobs: 27 | linelint: 28 | runs-on: ubuntu-latest 29 | name: Check if all files end in newline 30 | 31 | steps: 32 | - name: Checkout code 33 | uses: actions/checkout@v3 34 | 35 | - name: Run Linelint 36 | uses: fernandrone/linelint@0.0.4 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | y.output 3 | bin 4 | logs 5 | .coverage.* 6 | .vscode 7 | .idea 8 | tests/e2e/cmd/logs 9 | tests/e2e/cmd/gaea 10 | tests/e2e/cmd/gaea-cc 11 | .DS_Store 12 | .integrate_coverage.func 13 | .integrate_coverage.html 14 | .integrate_coverage.out 15 | .coverage.* 16 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/mr.md: -------------------------------------------------------------------------------- 1 | ### What problem does this PR solve? 2 | 3 | Issue Number: None 4 | 5 | Problem Summary: 6 | 7 | ### What is changed and how it works? 8 | 9 | ### Check List 10 | 11 | - [ ] Unit test 12 | - [ ] Integration test 13 | - [ ] Manual test (add detailed scripts or steps below) 14 | - [ ] No code 15 | 16 | Side effects 17 | 18 | - [ ] Breaking backward compatibility 19 | - [ ] Config file changes 20 | 21 | Documentation 22 | 23 | - [ ] Affects user behaviors 24 | - [ ] Contains syntax changes 25 | - [ ] Contains variable changes 26 | - [ ] Changes MySQL compatibility 27 | 28 | ### Release note 29 | 30 | ```release-note 31 | None 32 | ``` 33 | -------------------------------------------------------------------------------- /.linelint.yml: -------------------------------------------------------------------------------- 1 | # 'true' will fix files 2 | autofix: false 3 | 4 | # list of paths to ignore, uses gitignore syntaxes (executes before any rule) 5 | ignore: 6 | - tests/e2e/shard/case/join/mycat/long/unsupport.sql 7 | - tests/e2e/shard/case/join/mycat/mod/unsupport.sql 8 | - tests/e2e/shard/case/join/mycat/murmur/unsupport.sql 9 | - tests/e2e/shard/case/join/mycat/string/unsupport.sql 10 | rules: 11 | # checks if file ends in a newline character 12 | end-of-file: 13 | # set to true to enable this rule 14 | enable: true 15 | 16 | # set to true to disable autofix (if enabled globally) 17 | disable-autofix: false 18 | 19 | # if true also checks if file ends in a single newline character 20 | single-new-line: true 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | env: 3 | - TZ=Asia/Shanghai 4 | 5 | sudo: required 6 | 7 | language: go 8 | 9 | go: 10 | - 1.13.4 11 | 12 | script: 13 | - export GO111MODULE=on 14 | - make 15 | 16 | deploy: 17 | provider: releases 18 | file: 19 | - bin/gaea 20 | - bin/gaea-cc 21 | overwrite: true 22 | skip_cleanup: true 23 | api_key: $GITHUB_TOKEN 24 | on: 25 | tags: true #发布tag时才进行发包 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ROOT:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 2 | GOOS ?= linux 3 | GOARCH ?= $(shell go env GOARCH) 4 | GOENV := CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) 5 | GO := $(GOENV) go 6 | GAEA_OUT:=$(ROOT)/bin/gaea 7 | GAEA_CC_OUT:=$(ROOT)/bin/gaea-cc 8 | PKG:=$(shell go list -m) 9 | 10 | .PHONY: all build gaea gaea-cc parser clean test build_with_coverage 11 | all: build test 12 | 13 | build: parser gaea gaea-cc 14 | 15 | gaea: 16 | $(GO) build -o $(GAEA_OUT) $(shell bash gen_ldflags.sh $(GAEA_OUT) $(PKG)/core $(PKG)/cmd/gaea) 17 | 18 | gaea-cc: 19 | $(GO) build -o $(GAEA_CC_OUT) $(shell bash gen_ldflags.sh $(GAEA_CC_OUT) $(PKG)/core $(PKG)/cmd/gaea-cc) 20 | 21 | parser: 22 | cd parser && make && cd .. 23 | 24 | clean: 25 | @rm -rf bin 26 | @rm -f .coverage.out .coverage.html 27 | 28 | ALL_CHECKS = EOF spelling 29 | check: $(addprefix check-,$(ALL_CHECKS)) 30 | 31 | check-%: 32 | ./hack/verify-$*.sh 33 | 34 | test: 35 | go test -gcflags="all=-l -N" -coverprofile=.coverage.out `go list ./...` -short 36 | go tool cover -func=.coverage.out -o .coverage.func 37 | tail -1 .coverage.func 38 | go tool cover -html=.coverage.out -o .coverage.html 39 | 40 | e2e-test: gaea gaea-cc 41 | cp bin/gaea bin/gaea-cc tests/e2e/cmd/ 42 | ./hack/e2e-mysql5.sh 43 | ./hack/ginkgo-run-mysql5.sh 44 | 45 | e2e-test-mysql8: gaea gaea-cc 46 | cp bin/gaea bin/gaea-cc tests/e2e/cmd/ 47 | ./hack/e2e-mysql8.sh 48 | ./hack/ginkgo-run-mysql8.sh 49 | 50 | integrate_test: 51 | go test -timeout 30m -coverprofile=.integrate_coverage.out ./... -run ^TestIntegration$ 52 | go tool cover -func=.integrate_coverage.out -o .integrate_coverage.func 53 | tail -1 .integrate_coverage.func 54 | go tool cover -html=.integrate_coverage.out -o .integrate_coverage.html 55 | 56 | build_with_coverage: 57 | go test -c cmd/gaea/main.go cmd/gaea/main_test.go -coverpkg ./... -covermode=count -o bin/gaea 58 | -------------------------------------------------------------------------------- /backend/assets/image-20220314214316673.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220314214316673.png -------------------------------------------------------------------------------- /backend/assets/image-20220314215924425.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220314215924425.png -------------------------------------------------------------------------------- /backend/assets/image-20220314220921338.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220314220921338.png -------------------------------------------------------------------------------- /backend/assets/image-20220315221559157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220315221559157.png -------------------------------------------------------------------------------- /backend/assets/image-20220318083633693.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220318083633693.png -------------------------------------------------------------------------------- /backend/assets/image-20220318183833245.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220318183833245.png -------------------------------------------------------------------------------- /backend/assets/image-20220319002738908.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220319002738908.png -------------------------------------------------------------------------------- /backend/assets/image-20220319113026919.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/backend/assets/image-20220319113026919.png -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # 架构设计 2 | 3 | ## 模块划分 4 | 5 | gaea包含四个模块,分别是gaea-proxy、gaea-cc、gaea-agent、gaea-web。gaea-proxy为在线代理,负责承接sql流量,gaea-cc是中控模块,负责gaea-proxy的配置管理及一些后台任务,gaea-agent部署在mysql所在的机器上,负责实例创建、管理、回收等工作,gaea-web是gaea的一个管理界面,使gaea整体使用更加方便。 6 | 7 | ## 架构图 8 | 9 | ![gaea架构图](assets/architecture.png) 10 | -------------------------------------------------------------------------------- /docs/assets/1-4-1-read-only-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-1-read-only-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-1-read-only-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-1-read-only-P99.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-1-read-only-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-1-read-only-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-1-read-only-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-1-read-only-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-2-read-write-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-2-read-write-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-2-read-write-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-2-read-write-P99.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-2-read-write-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-2-read-write-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-2-read-write-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-2-read-write-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-3-write-only-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-3-write-only-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-3-write-only-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-3-write-only-P99.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-3-write-only-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-3-write-only-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-3-write-only-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-3-write-only-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-4-point-select-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-4-point-select-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-4-point-select-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-4-point-select-P99.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-4-point-select-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-4-point-select-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/1-4-4-point-select-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/1-4-4-point-select-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-1-read-only-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-1-read-only-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-1-read-only-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-1-read-only-P99.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-1-read-only-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-1-read-only-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-1-read-only-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-1-read-only-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-2-read-write-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-2-read-write-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-2-read-write-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-2-read-write-P99.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-2-read-write-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-2-read-write-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-2-read-write-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-2-read-write-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-3-write-only-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-3-write-only-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-3-write-only-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-3-write-only-P99.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-3-write-only-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-3-write-only-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-3-write-only-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-3-write-only-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-4-point-select-CPU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-4-point-select-CPU.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-4-point-select-P99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-4-point-select-P99.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-4-point-select-QPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-4-point-select-QPS.jpg -------------------------------------------------------------------------------- /docs/assets/2-4-4-point-select-TPS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/2-4-4-point-select-TPS.jpg -------------------------------------------------------------------------------- /docs/assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/architecture.png -------------------------------------------------------------------------------- /docs/assets/deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/deployment.png -------------------------------------------------------------------------------- /docs/assets/feishu_talk.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/feishu_talk.jpeg -------------------------------------------------------------------------------- /docs/assets/gaea_dingtalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/gaea_dingtalk.png -------------------------------------------------------------------------------- /docs/assets/master-slave.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/master-slave.jpg -------------------------------------------------------------------------------- /docs/assets/master-slaves.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/master-slaves.jpg -------------------------------------------------------------------------------- /docs/assets/singlemaster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/docs/assets/singlemaster.jpg -------------------------------------------------------------------------------- /docs/compatibility.md: -------------------------------------------------------------------------------- 1 | # Gaea兼容范围 2 | 3 | ## 协议兼容性 4 | 5 | Gaea支持text协议和binary协议. 6 | 7 | ## SQL兼容性 8 | 9 | Gaea对分表和非分表的兼容性有所不同. 非分表理论上支持所有DML语句, 部分ADMIN语句. 10 | 11 | 对分表情况, Gaea本身的定位是**轻量级, 高性能**, 因此采用轻量的分表实现方式, 对一条SQL的执行, 只做字段改写和结果聚合, 不做SQL语义上的改写和多条SQL结果集的拼接计算. 12 | 13 | **以下支持/不支持操作均指分表情况.** 14 | 15 | ### SELECT 16 | 17 | 明确支持以下操作: 18 | 19 | - JOIN操作支持一个父表和多个关联子表, 以及全局表. 20 | - 聚合函数支持SUM, MAX, MIN, COUNT, 且必须出现在最外层. 21 | - WHERE语句的条件支持AND, OR, 操作符支持=, >, >=, <, <=, <=>, IN, NOT IN, LIKE, NOT LIKE. 22 | - 支持GROUP BY. 23 | 24 | 明确不支持以下操作: 25 | 26 | - 不支持跨分片JOIN. JOIN中非分片键相关的条件, 只改写表名, 不计算路由, 走默认的广播路由. 27 | - JOIN USING不支持指定表名或DB名. 28 | - 表别名不允许与表名重复. 29 | - select animals.id from animals, test1.xm_order_extend as animals; 30 | - 这句SQL在MySQL中被认为是正确的, 但是gaea会明确拒绝这种操作. 31 | 32 | ### INSERT 33 | 34 | 明确不支持以下操作: 35 | 36 | - 不明确指定列名的INSERT 37 | - 跨分片批量INSERT 38 | - INSERT INTO SELECT 39 | 40 | ### UPDATE 41 | 42 | 明确不支持以下操作: 43 | 44 | - UPDATE多个表 45 | 46 | 47 | ## 事务兼容性 48 | 49 | - Gaea目前未实现分布式事务, 只支持单分片事务, 使用跨分片事务会报错. 50 | - 不支持SAVEPOINT, RELEASE SAVEPOINT, ROLLBACK TO SAVEPOINT **TODO** 51 | -------------------------------------------------------------------------------- /docs/concepts.md: -------------------------------------------------------------------------------- 1 | # 基本概念 2 | 3 | * cluster 4 | 5 | 集群, 按照业务重要程度划分集群, 一个集群内可包含多个gaea-proxy实例, 通过指定gaea-proxy启动时依赖的配置文件中的cluster_name 6 | 确定该proxy所属集群。集群内的proxy实例只为该集群内的namespace提供服务, 起到物理隔离的作用。 7 | 一套集群可为多个namespace提供服务。 8 | 9 | * namespace 10 | 11 | 命名空间,每一个业务系统对应一个namespace,一个namespace对应多个database,业务方可以自由切换。 12 | 每个namespace理论上只会属于一个集群。 13 | 通过gaea-cc配置管理接口, 指定namespace所属集群。 14 | 15 | * slice 16 | 17 | 分片,逻辑上的分组,一个分片包含mysql一主多从。 18 | 19 | * shard 20 | 21 | 分表规则,确定一个表如何分表,包括分表的类型、分布的分片位置。 22 | 23 | * proxy 24 | 25 | 指代理本身,承接线上流量。 26 | 27 | * gaea_cc 28 | 29 | 代理控制模块,主要负责配置下发、实例监控等。 30 | 31 | * gaea_agent 32 | 33 | 部署在mysql实例所在机器,负责实例部署、管理、执行插件等功能。 34 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## 权限说明 4 | 5 | 部分语句默认的请求到后端从库或主库的情况: 6 | - 普通 Select: 7 | - 读写分离用户(RWFlag=2,RWSplit=1):从库 8 | - 只写用户(RWFlag=2,RWSplit=0):主库 9 | - 只读用户(RWFlag=1,RWSplit=1):从库 10 | - Select 带 Master Hint: 11 | - 读写分离用户(RWFlag=2,RWSplit=1):主库 12 | - 只写用户(RWFlag=2,RWSplit=0):主库 13 | - 只读用户(RWFlag=1,RWSplit=1):从库(V2.0 以下版本会请求到主库,MiProxy 会打到从库) 14 | - Select... For Update(lock in share mode):(同上) 15 | - 读写分离用户(RWFlag=2,RWSplit=1):主库 16 | - 只写用户(RWFlag=2,RWSplit=0):主库 17 | - 只读用户(RWFlag=1,RWSplit=1):从库 18 | - Update/Insert/Delete: 19 | - 读写分离用户(RWFlag=2,RWSplit=1):主库 20 | - 只写用户(RWFlag=2,RWSplit=0):主库 21 | - 只读用户(RWFlag=1,RWSplit=1):报错 22 | - 开启事务 23 | - 读写分离用户(RWFlag=2,RWSplit=1):主库(与旧版相同) 24 | - 只写用户(RWFlag=2,RWSplit=0):主库(与旧版相同) 25 | - 只读用户(RWFlag=1,RWSplit=1):主库(与旧版相同) 26 | 27 | - 当从库宕机时:会重新从主库获取连接,并请求到主库(无论权限如何) 28 | - 当 Select 处于显式开启的事务中,也都会请求到主库(包括只读用户,此处与 MiProxy 不一致,MiProxy 只读用户开启事务也会请求到从库) 29 | 30 | ## 配置热加载 31 | 目前 Gaea namespace 配置支持热加载,Gaea 本身的配置文件只支持日志的热加载,执行热加载的方式为 32 | ```bash 33 | # 使用 signal 重新加载 34 | kill -SIGUSER1 ${gaea_pid} 35 | # 使用 API 热加载 36 | curl -X PUT 'http://127.0.0.1:13307/api/proxy/proxyconfig/reload' \ 37 | -H 'Authorization: Basic YWRtaW46YWRtaW4=' 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/multi-tenant.md: -------------------------------------------------------------------------------- 1 | # 多租户的设计与实现 2 | 3 | ## 背景 4 | 5 | gaea多租户是为了实现一套gaea集群,可以接入多个业务系统的不同数据库,方便部署、运维。gaea多租户为软多租户,一个租户称为一个namespace,多个namespace之间存在于一套gaea proxy集群内,所以是一种软隔离。我们也可以为一些重要等级业务系统单独部署一套gaea集群,甚至一套业务系统对应一套gaea集群实现物理隔离。 6 | 7 | ## 接入方式 8 | 9 | mysql的授权方式为用户名+密码+ip+数据库,接入gaea的情况下,授权方式为用户名+密码确定唯一一个namespace,ip则在白名单IP/IP段起作用,如果未配置白名单IP,则默认对所有IP生效。所以,不同的业务系统,用户名可以有相同的,但是用户名+密码要保证是唯一的,密码我们内部是根据一定的规则随机生成的,并且会校验是否重复。 10 | 11 | ## 实现原理 12 | 13 | ### 主要结构 14 | 15 | 在授权阶段还未能确定对应的namespace,所以user和namespace的配置是分别加载的,授权的实现主要依赖于UserManager结构体,其定义如下 16 | 17 | ```golang 18 | type UserManager struct { 19 | users map[string][]string // key: user name, value: user password, same user may have different password, so array of passwords is needed 20 | userNamespaces map[string]string // key: UserName+Password, value: name of namespace 21 | } 22 | ``` 23 | 24 | ### 配置加载过程 25 | 26 | 在系统初始化阶段,会依次加载对应user、namespace配置到一个全局Manager内,其中user部分用以授权检查,整个配置通过滚动数组的方式实现了无锁热加载,具体实现可以参照[gaea配置热加载实现原理](config-reloading.md)一章。 27 | 28 | ### 校验过程 29 | 30 | 其中users同一个用户名对应一个string数组,用以处理同一用户名不同密码的情形,而userNamespaces则用以通过用户名+密码快速获取对应的namespace名称。在验证阶段,首先通过CheckUser检查用户名是否存在,不存在则直接授权失败。然后,通过CheckPassword,依次对比确定是否可以找到对应密码,如果找不到,则最终授权失败;如果找到,则授权检查通过并记录对应的会话信息。 31 | 32 | ## 结语 33 | 34 | gaea的多租户确实为部署、运维带来了不少方便,后续也会考虑支持kubernetes的部署、调度多租户等,但是当下的多租户结构不会发生太大变化。 35 | -------------------------------------------------------------------------------- /docs/prepare.md: -------------------------------------------------------------------------------- 1 | # prepare的设计与实现 2 | 3 | ## 背景 4 | 5 | 应用端使用prepare主要考虑通过固定sql模板,在执行sql时只传输参数,减少数据包传输大小,提升sql执行效率。对于非分库分表的情况,我们可以直接通过转发execute(对应后端连接可能是prepare+execute+close)的方式进行支持。但是对于分库分表的情形,需要计算路由,重写sql,支持起来会非常麻烦。商城目前的分库分表中间件是mycat,而mycat是支持prepare的,而gaea的prepare方案也是参照mycat,即将prepare statements的执行转换为sql的执行,然后在应答阶段,根据文本的应答内容构造二进制应答内容,返回给客户端,从而统一了分库分表的处理逻辑。 6 | 7 | ## prepare 8 | 9 | gaea在接到preprae请求后,首先计算参数个数、参数偏移位置和stmt-id。然后根据以上数据,构造statement对象并保存在SessionExecutor的stmts内,stmts为一个map,key为stmt-id,value即为构造的statement对象。 10 | 11 | prepare阶段主要是计算、保存execute需要使用的变量信息,prepare应答数据内也会包含这些变量信息。 12 | 13 | ## execute 14 | 15 | execute请求时会携带prepare应答返回的stmt-id,服务端根据stmt-id从SessionExecutor的stmts中查询对应的statement信息。根据statement信息的参数个数、偏移和execute上传的参数值,进行关联绑定,然后rewrite一条同等含义的sql。同时,为了安全性考虑,也会进行特殊字符过滤,防止比如sql注入的发生。 16 | 17 | 生成sql之后,无论是分表还是非分表,我们都可以调用handleQuery进行统一的处理,避免了因为要支持prepare,而存在两套计算分库、分表路由的逻辑。 18 | 19 | 处理完成之后,需要进行文本应答协议到二进制应答协议的转换,相关实现在BuildBinaryResultset内。 20 | 21 | execute执行完成之后,执行ResetParams,重新初始化send_long_data对应的args字段,病返回应答。 22 | 23 | ## send_long_data 24 | 25 | send_long_data不是必须的,但是如果execute有多个参数,且不止一个参数长度比较大,一次execute可能达到mysql max-payload-length,但是如果分多次,每次只发送一个,这样就绕过了max-payload-length,send_long_data就是基于这样的背景产生的。 26 | 27 | 客户端发送send_long_data报文,会携带stmt-id、param-id(参数位置),我们根据stmt-id参数可以检索prepare阶段存储的stmt信息,根据param-id和对应上送的数据,可以建立一个k-v映射,存储在Stmt.args(interface slice)。在上述execute执行阶段,也会根据参数位置从Stmt.args查询对应位置的参数值,进行关联绑定。 28 | 29 | send_long_data不需要应答。 30 | 31 | ## close 32 | 33 | close的处理逻辑比较简单,服务端收到close请求后,删除prepare阶段stmt-id及其数据的对应关系。 34 | 35 | ## 总结 36 | 37 | gaea对于prepare的处理初衷还是考虑协议的兼容和简化处理逻辑,对于client->proxy->mysql这样接口来说,client->proxy是prepare协议,proxy->mysql是文本协议,所以整体来看在gaea环境下使用prepare性能提升有限,还是建议直接使用sql。 38 | 39 | ## 参考资料 40 | 41 | [mysql prepare statements 官方文档](https://dev.mysql.com/doc/internals/en/prepared-statements.html) 42 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # 快速入门 2 | 3 | ## 编译安装 4 | 5 | gaea基于go开发,基于go modules进行版本管理,并依赖goyacc、gofmt等工具。 6 | 7 | * go >= 1.11 8 | 9 | ```bash 10 | # 如果你已配置GOPATH,同时GO111MODULE设置为auto,请克隆Gaea到GOPATH外的目录 11 | git clone git@github.com:XiaoMi/Gaea.git 12 | 13 | # 如果拉取依赖速度慢,可以配置GOPROXY 14 | # export GOPROXY=https://athens.azurefd.net 15 | 16 | # 编译二进制包 17 | cd Gaea && make 18 | ``` 19 | 20 | ## 执行 21 | 22 | 编译之后在bin目录会有gaea、gaea-cc两个可执行文件。etc目录下为配置文件,如果想快速体验gaea功能,可以采用file配置方式,然后在etc/file/namespace下添加对应租户的json文件,该目录下目前有两个示例,可以直接修改使用。 23 | ./bin/gaea --help显示如下,其中-config是指定配置文件位置,默认为./etc/gaea.ini,具体配置见[配置说明](configuration.md)。 24 | 25 | ```bash 26 | Usage of ./bin/gaea: 27 | -config string 28 | gaea config file (default "etc/gaea.ini") 29 | ``` 30 | -------------------------------------------------------------------------------- /etc/file/namespace/test_namespace_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test_namespace_1", 3 | "online": true, 4 | "read_only": false, 5 | "allowed_dbs": { 6 | "sbtest1": true 7 | }, 8 | "slow_sql_time": "1000", 9 | "black_sql": [ 10 | "" 11 | ], 12 | "allowed_ip": null, 13 | "slices": [ 14 | { 15 | "name": "slice-0", 16 | "user_name": "test1", 17 | "password": "test1", 18 | "master": "127.0.0.1:3308", 19 | "slaves": null, 20 | "statistic_slaves": null, 21 | "capacity": 12, 22 | "max_capacity": 24, 23 | "idle_timeout": 60, 24 | "init_connect": "" 25 | } 26 | ], 27 | "shard_rules": null, 28 | "users": [ 29 | { 30 | "user_name": "front_user1", 31 | "password": "front_password1", 32 | "namespace": "test_namespace_1", 33 | "rw_flag": 2, 34 | "rw_split": 1, 35 | "other_property": 0 36 | } 37 | ], 38 | "default_slice": "slice-0", 39 | "global_sequences": null, 40 | "max_sql_execute_time": 0 41 | } 42 | -------------------------------------------------------------------------------- /etc/file/namespace/test_namespace_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test_namespace_2", 3 | "online": true, 4 | "read_only": false, 5 | "allowed_dbs": { 6 | "sbtest1": true 7 | }, 8 | "slow_sql_time": "1000", 9 | "black_sql": [ 10 | "" 11 | ], 12 | "allowed_ip": null, 13 | "slices": [ 14 | { 15 | "name": "slice-0", 16 | "user_name": "test2", 17 | "password": "test2", 18 | "master": "127.0.0.1:3309", 19 | "slaves": null, 20 | "statistic_slaves": null, 21 | "capacity": 12, 22 | "max_capacity": 24, 23 | "idle_timeout": 60, 24 | "init_connect": "" 25 | } 26 | ], 27 | "shard_rules": null, 28 | "users": [ 29 | { 30 | "user_name": "front_user2", 31 | "password": "front_password2", 32 | "namespace": "test_namespace_2", 33 | "rw_flag": 2, 34 | "rw_split": 1, 35 | "other_property": 0 36 | } 37 | ], 38 | "default_slice": "slice-0", 39 | "global_sequences": null, 40 | "max_sql_execute_time": 0 41 | } 42 | -------------------------------------------------------------------------------- /etc/gaea.ini: -------------------------------------------------------------------------------- 1 | ; config type, etcd/file/etcdv3, you can test gaea with file type, you shoud use etcd/etcdv3 in production 2 | ; 请指定设定方式为 file 或 etcd 或 etcdv3 3 | ;config_type=file 4 | config_type=etcdv3 5 | ;file config path, 具体配置放到file_config_path的namespace目录下,该下级目录为固定目录 6 | ;file_config_path=./etc/file 7 | ;file_config_path=/etc/ 8 | 9 | ;coordinator addr 10 | coordinator_addr=http://127.0.0.1:2379 11 | ;etcd user config 12 | username=root 13 | password=root 14 | 15 | ;environ 16 | environ=local 17 | ;service name 18 | service_name=gaea_proxy 19 | ;gaea_proxy cluster name 20 | cluster_name=gaea_default_cluster 21 | 22 | ;log config 23 | log_path=./logs 24 | log_level=Notice 25 | log_filename=gaea 26 | log_output=file 27 | ; 日志保留天数 28 | log_keep_days=3 29 | ; 日志保留数量 30 | log_keep_counts=3 31 | 32 | ;admin addr 33 | admin_addr=0.0.0.0:13307 34 | ; basic auth 35 | admin_user=test 36 | admin_password=test 37 | 38 | ;proxy addr 39 | proto_type=tcp4 40 | proxy_addr=0.0.0.0:13306 41 | proxy_charset=utf8 42 | ;slow sql time, when execute time is higher than this, log it, unit: ms 43 | slow_sql_time=100 44 | ;close session after session timeout, unit: seconds 45 | session_timeout=3600 46 | 47 | ;stats conf 48 | stats_enabled=true 49 | ;stats interval 50 | stats_interval=10 51 | 52 | ;encrypt key 53 | encrypt_key=1234abcd5678efg* 54 | 55 | ;server_version 56 | server_version=5.6.20-gaea 57 | 58 | ;auth plugin mysql_native_password or caching_sha2_password or '' 59 | auth_plugin=mysql_native_password 60 | -------------------------------------------------------------------------------- /etc/gaea_cc.ini: -------------------------------------------------------------------------------- 1 | addr=0.0.0.0:23306 2 | ; basic auth of gaea-cc 3 | admin_username=admin 4 | admin_password=admin 5 | 6 | ; basic auth of gaea-proxy's admin service 7 | proxy_username=test 8 | proxy_password=test 9 | 10 | ;Debug, Trace, Notice, Warn, Fatal, 建议测试采用debug级别,上线采用Notice级别 11 | log_level=Notice 12 | log_path =./logs 13 | log_filename=gaea_cc 14 | log_output=file 15 | log_keep_days=3 16 | 17 | ;coordinator 目前支持 etcd 和 etcdv3,coodinator config, 根据 etcd 的 API VERSION 版本进行配置 18 | coordinator_type=etcd 19 | coordinator_addr=http://127.0.0.1:2379 20 | username=root 21 | password=root 22 | 23 | ;指定一个的默认gaea集群名称 24 | default_cluster=gaea_default_cluster 25 | 26 | ;encrypt key 27 | encrypt_key=1234abcd5678efg* 28 | -------------------------------------------------------------------------------- /gen_ldflags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERBOSE=${VERBOSE:-"0"} 4 | V="" 5 | if [[ "${VERBOSE}" == "1" ]];then 6 | V="-x" 7 | set -x 8 | fi 9 | 10 | ROOT="$(pwd)" 11 | 12 | OUT=${1:?"output path"} 13 | VERSION_PACKAGE=${2:?"version go package"} # istio.io/istio/pkg/version 14 | BUILDPATH=${3:?"path to build"} 15 | 16 | set -e 17 | 18 | GOOS=${GOOS:-linux} 19 | GOARCH=${GOARCH:-amd64} 20 | GOBINARY=${GOBINARY:-go} 21 | GOPKG="$GOPATH/pkg" 22 | BUILDINFO=${BUILDINFO:-""} 23 | STATIC=${STATIC:-1} 24 | LDFLAGS="-extldflags -static" 25 | GOBUILDFLAGS=${GOBUILDFLAGS:-""} 26 | GCFLAGS=${GCFLAGS:-} 27 | export CGO_ENABLED=0 28 | 29 | if [[ "${STATIC}" != "1" ]];then 30 | LDFLAGS="" 31 | fi 32 | 33 | # gather buildinfo if not already provided 34 | # For a release build BUILDINFO should be produced 35 | # at the beginning of the build and used throughout 36 | if [[ -z ${BUILDINFO} ]];then 37 | BUILDINFO=$(mktemp) 38 | ${ROOT}/gen_version.sh > ${BUILDINFO} 39 | fi 40 | 41 | # BUILD LD_VERSIONFLAGS 42 | LD_VERSIONFLAGS="" 43 | while read line; do 44 | read SYMBOL VALUE < <(echo $line) 45 | LD_VERSIONFLAGS=${LD_VERSIONFLAGS}" -X ${VERSION_PACKAGE}.${SYMBOL}=${VALUE}" 46 | done < "${BUILDINFO}" 47 | 48 | echo -pkgdir=${GOPKG}/${GOOS}_${GOARCH} -ldflags "\"${LDFLAGS} ${LD_VERSIONFLAGS}\"" ${BUILDPATH} 49 | -------------------------------------------------------------------------------- /gen_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ROOT="$(pwd)" 4 | 5 | if BUILD_GIT_REVISION=$(git rev-parse HEAD 2> /dev/null); then if ! git diff-index --quiet HEAD; then BUILD_GIT_REVISION=${BUILD_GIT_REVISION}"-dirty" 6 | fi 7 | else 8 | BUILD_GIT_REVISION=unknown 9 | fi 10 | 11 | # Check for local changes 12 | if git diff-index --quiet HEAD --; then 13 | tree_status="Clean" 14 | else 15 | tree_status="Modified" 16 | fi 17 | 18 | # Check for git branch and git dirty 19 | BRANCH=$(git rev-parse --abbrev-ref HEAD) 20 | GIT_DIRTY=$(git diff --no-ext-diff 2> /dev/null | wc -l) 21 | 22 | # XXX This needs to be updated to accommodate tags added after building, rather than prior to builds 23 | RELEASE_TAG=$(git describe) 24 | 25 | # security wanted VERSION='unknown' 26 | VERSION="${BUILD_GIT_REVISION}" 27 | if [[ -n "${RELEASE_TAG}" ]]; then 28 | VERSION="${RELEASE_TAG}" 29 | fi 30 | 31 | # used by core/version 32 | echo buildVersion "${VERSION}" 33 | echo buildGitRevision "${BUILD_GIT_REVISION}" 34 | echo buildUser "$(whoami)" 35 | echo buildHost "$(hostname -f)" 36 | echo buildStatus "${tree_status}" 37 | echo buildTime "$(date +%Y-%m-%d--%T)" 38 | echo buildBranch "${BRANCH}" 39 | echo buildGitDirty "${GIT_DIRTY}" 40 | -------------------------------------------------------------------------------- /genver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version=`git log --date=iso --pretty=format:"%cd @%h" -1` 4 | if [ $? -ne 0 ]; then 5 | version="not a git repo" 6 | fi 7 | 8 | compile=`date +"%F %T %z"`" by "`go version` 9 | 10 | cat << EOF | gofmt > core/version.go 11 | package core 12 | 13 | const ( 14 | // Version means gaea version 15 | Version = "$version" 16 | // Compile means gaea compole info 17 | Compile = "$compile" 18 | ) 19 | EOF 20 | -------------------------------------------------------------------------------- /hack/ginkgo-run-mysql5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ginkgo --v --progress --trace --flake-attempts=1 --skip "^.*only mysql8:.*$" ./tests/e2e/ 3 | -------------------------------------------------------------------------------- /hack/ginkgo-run-mysql8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ginkgo --v --progress --trace --flake-attempts=1 --skip '^.*only mysql5:.*$' --skip '^.*shard join support test in.*$' --skip 'test dml set variables' --skip 'simple sql test' --skip='Unshard DML Support Test' ./tests/e2e/ 3 | -------------------------------------------------------------------------------- /hack/update-EOF.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | ROOTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)" 8 | 9 | cd $ROOTDIR 10 | 11 | FILELIST=($(find . -type f -not \( -path './bin/*' \ 12 | -o -path './etc/*' \ 13 | -o -path './.git/*' \ 14 | -o -path '*.png' \ 15 | -o -path './.idea/*' \ 16 | -o -path './.DS_Store' \ 17 | -o -path './*/.DS_Store' \ 18 | -o -path './docs/*' \ 19 | -o -path './logs/*' \ 20 | -o -path './parser/goyacc' \ 21 | \))) 22 | 23 | for f in ${FILELIST[@]}; do 24 | c=$(tail -c 1 "$f" | wc -l) 25 | if [ "$c" -eq 0 ]; then 26 | echo "find file $f do not end with newline, fixing it" 27 | printf "\n" >> $f 28 | fi 29 | done 30 | -------------------------------------------------------------------------------- /hack/update_e2e.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | MASTER_USER=$1 8 | MASTER_PASSWORD=$2 9 | ROOTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)" 10 | 11 | cd $ROOTDIR 12 | echo $ROOTDIR 13 | 14 | cp ./bin/gaea ./tests/e2e/cmd/ 15 | sed -i "s#^master_user.*#master_user: $MASTER_USER#" ./tests/e2e/config/config.yaml 16 | sed -i "s#^master_password.*#master_user: $MASTER_PASSWORD#" ./tests/e2e/config/config.yaml 17 | sed -i "s#^slave_user.*#slave_user: $MASTER_USER#" ./tests/e2e/config/config.yaml 18 | sed -i "s#^slave_password.*#slave_user: $MASTER_PASSWORD#" ./tests/e2e/config/config.yaml 19 | -------------------------------------------------------------------------------- /hack/verify-EOF.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | ROOTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)" 8 | 9 | cd $ROOTDIR 10 | 11 | FILELIST=($(find . -type f -not \( -path './bin/*' \ 12 | -o -path './etc/*' \ 13 | -o -path './.git/*' \ 14 | -o -path '*.png' \ 15 | -o -path './.idea/*' \ 16 | -o -path './.DS_Store' \ 17 | -o -path './*/.DS_Store' \ 18 | -o -path './docs/*' \ 19 | -o -path './logs/*' \ 20 | -o -path './parser/goyacc' \ 21 | -o -path './tests/e2e/cmd/*' \ 22 | \))) 23 | 24 | NUM=0 25 | declare FAILED_FILE 26 | 27 | for f in ${FILELIST[@]}; do 28 | c=$(tail -c 1 "$f" | wc -l) 29 | if [ "$c" -eq 0 ]; then 30 | FAILED_FILE+=($f) 31 | NUM=$((NUM + 1)) 32 | fi 33 | done 34 | 35 | if [ $NUM -ne 0 ]; then 36 | echo "error: following files do not end with newline, please run hack/update-EOF.sh to fix them" 37 | printf '%s\n' "${FAILED_FILE[@]}" 38 | exit 1 39 | else 40 | echo "all files pass checking." 41 | fi 42 | -------------------------------------------------------------------------------- /hack/verify-spelling.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd) 8 | cd $ROOT 9 | OUTPUT_BIN=$ROOT/bin/output/ 10 | mkdir -p $OUTPUT_BIN 11 | 12 | # ensure_misspell 13 | echo "Installing misspell..." 14 | GOBIN=$OUTPUT_BIN go install github.com/client9/misspell/cmd/misspell@v0.3.4 15 | 16 | 17 | ignore_words=( 18 | "importas" 19 | "etc" 20 | ) 21 | 22 | ret=0 23 | git ls-files | xargs ${OUTPUT_BIN}/misspell -i ${ignore_words[@]} -error -o stderr || ret=$? 24 | if [ $ret -eq 0 ]; then 25 | echo "Spellings all good!" 26 | else 27 | echo "Found some typos, please fix them!" 28 | exit 1 29 | fi 30 | -------------------------------------------------------------------------------- /log/zap/encoder_test.go: -------------------------------------------------------------------------------- 1 | package zap 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | "time" 8 | 9 | "go.uber.org/zap/buffer" 10 | "go.uber.org/zap/zapcore" 11 | ) 12 | 13 | func TestZapEncoder_EncodeEntry(t *testing.T) { 14 | zapBufferPool = buffer.NewPool() 15 | e := &ZapEncoder{} 16 | 17 | // 创建一个模拟的时间和日志条目 18 | now := time.Now() 19 | entry := zapcore.Entry{ 20 | Time: now, 21 | Level: zapcore.InfoLevel, 22 | Message: "test message", 23 | } 24 | 25 | // 调用 EncodeEntry 方法并获取结果缓冲区 26 | buf, err := e.EncodeEntry(entry, nil) 27 | if err != nil { 28 | t.Fatalf("Unexpected error: %v", err) 29 | } 30 | 31 | // 检查缓冲区的内容是否符合预期 32 | expectedOutput := fmt.Sprintf("[%s] [%s] %s\n", now.Format("2006-01-02 15:04:05.000"), strings.ToUpper("info"), "test message") 33 | if buf.String() != expectedOutput { 34 | t.Errorf("Expected output:\n%s\ngot:\n%s", expectedOutput, buf.String()) 35 | } 36 | } 37 | 38 | func TestZapEncoder_Clone(t *testing.T) { 39 | e := &ZapEncoder{} 40 | clone := e.Clone() 41 | if clone == nil || clone != e { 42 | t.Error("Clone method did not return a same instance") 43 | } 44 | } 45 | 46 | func BenchmarkZapEncoder_EncodeEntry(b *testing.B) { 47 | zapBufferPool = buffer.NewPool() 48 | e := &ZapEncoder{} 49 | 50 | // 创建一个模拟的时间和日志条目 51 | now := time.Now() 52 | entry := zapcore.Entry{ 53 | Time: now, 54 | Level: zapcore.InfoLevel, 55 | Message: "benchmark test message", 56 | } 57 | 58 | b.ResetTimer() 59 | for i := 0; i < b.N; i++ { 60 | _, _ = e.EncodeEntry(entry, nil) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /log/zap/syncer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package zap 16 | 17 | import ( 18 | "bufio" 19 | "context" 20 | "io" 21 | "sync" 22 | "time" 23 | ) 24 | 25 | const writerBuffSize = 1024 * 1024 26 | 27 | type LogAsyncWriter struct { 28 | sync.Mutex 29 | writer io.WriteCloser 30 | buf *bufio.Writer 31 | timer *time.Ticker 32 | quit context.CancelFunc 33 | ctx context.Context 34 | } 35 | 36 | func NewAsyncWriter(w io.WriteCloser) *LogAsyncWriter { 37 | buf := bufio.NewWriterSize(w, writerBuffSize) 38 | syncer := &LogAsyncWriter{ 39 | writer: w, 40 | buf: buf, 41 | } 42 | go syncer.intervalFlush() 43 | return syncer 44 | } 45 | 46 | func (l *LogAsyncWriter) Write(data []byte) (int, error) { 47 | l.Lock() 48 | defer l.Unlock() 49 | return l.buf.Write(data) 50 | } 51 | 52 | func (l *LogAsyncWriter) Sync() error { 53 | l.Lock() 54 | defer l.Unlock() 55 | return l.buf.Flush() 56 | } 57 | 58 | func (l *LogAsyncWriter) intervalFlush() { 59 | l.ctx, l.quit = context.WithCancel(context.Background()) 60 | l.timer = time.NewTicker(time.Millisecond * 100) 61 | for { 62 | select { 63 | case <-l.timer.C: 64 | l.Sync() 65 | case <-l.ctx.Done(): 66 | return 67 | } 68 | } 69 | } 70 | 71 | func (l *LogAsyncWriter) Close() error { 72 | defer l.writer.Close() 73 | l.timer.Stop() 74 | l.quit() 75 | return l.Sync() 76 | } 77 | -------------------------------------------------------------------------------- /misc/git/hooks/gofmt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # git gofmt pre-commit hook 17 | # 18 | # To use, store as .git/hooks/pre-commit inside your repository and make sure 19 | # it has execute permissions. 20 | # 21 | # This script does not handle file names that contain spaces. 22 | gofiles=$(git diff --cached --name-only --diff-filter=d | grep '.go$') 23 | 24 | [ -z "$gofiles" ] && exit 0 25 | unformatted=$(gofmt -s -l $gofiles 2>&1) 26 | [ -z "$unformatted" ] && exit 0 27 | 28 | # Some files are not gofmt'd. Print command to fix them and fail. 29 | 30 | # Deduplicate files first in case a file has multiple errors. 31 | files=$( 32 | # Split the "gofmt" output on newlines only. 33 | OLDIFS=$IFS 34 | IFS=' 35 | ' 36 | for line in $unformatted; do 37 | # Strip everything after the first ':', including it. 38 | # Example output for $line: 39 | # go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go:241:60: expected ';', found 'IDENT' wg 40 | echo ${line/:*/} 41 | done | 42 | # Remove duplicates. 43 | sort -u 44 | IFS=$OLDIFS 45 | ) 46 | 47 | echo >&2 48 | echo >&2 "Go files must be formatted with gofmt. Please run:" 49 | echo >&2 50 | echo >&2 -n " gofmt -s -w" 51 | 52 | for f in $files; do 53 | # Print " \" after the "gofmt" above and each filename (except for the last one). 54 | echo >&2 " \\" 55 | echo >&2 -n " $PWD/$f" 56 | done 57 | echo >&2 58 | 59 | echo >&2 60 | echo >&2 "If gofmt fails and outputs errors, you have to fix them manually." 61 | echo >&2 62 | 63 | exit 1 64 | -------------------------------------------------------------------------------- /misc/git/hooks/goimports: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # git goimports pre-commit hook 17 | # 18 | # To use, store as .git/hooks/pre-commit inside your repository and make sure 19 | # it has execute permissions. 20 | # 21 | # This script does not handle file names that contain spaces. 22 | gofiles=$(git diff --cached --name-only --diff-filter=d | grep '.go$') 23 | 24 | [ -z "$gofiles" ] && exit 0 25 | unformatted=$(goimports -l=true $gofiles 2>&1 | awk -F: '{print $1}') 26 | [ -z "$unformatted" ] && exit 0 27 | 28 | # Some files are not goimports'd. Print message and fail. 29 | 30 | echo >&2 "Go files must be formatted with goimports. Please run:" 31 | echo >&2 32 | echo -n >&2 " goimports -w" 33 | for fn in $unformatted; do 34 | echo -n >&2 " $PWD/$fn" 35 | done 36 | echo 37 | 38 | exit 1 39 | -------------------------------------------------------------------------------- /misc/git/hooks/govet: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2017 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # git go vet pre-commit hook 17 | # 18 | # To use, store as .git/hooks/pre-commit inside your repository and make sure 19 | # it has execute permissions. 20 | 21 | if [ -z "$GOPATH" ]; then 22 | echo "ERROR: pre-commit hook for go vet: \$GOPATH is empty. Please run 'source dev.env' to set the correct \$GOPATH." 23 | exit 1 24 | fi 25 | 26 | # This script does not handle file names that contain spaces. 27 | gofiles=$(git diff --cached --name-only --diff-filter=d | grep '.go$') 28 | 29 | # If any checks are found to be useless, they can be disabled here. 30 | # See the output of "go tool vet" for a list of flags. 31 | vetflags="-all=true" 32 | 33 | errors= 34 | 35 | # Run on one file at a time because a single invocation of "go tool vet" 36 | # with multiple files requires the files to all be in one package. 37 | for gofile in $gofiles 38 | do 39 | if ! go tool vet $vetflags $gofile 2>&1; then 40 | errors=YES 41 | fi 42 | done 43 | 44 | [ -z "$errors" ] && exit 0 45 | 46 | echo 47 | echo "Please fix the go vet warnings above. To disable certain checks, change vetflags in misc/git/hooks/govet." 48 | exit 1 49 | -------------------------------------------------------------------------------- /misc/git/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Runs any hooks in misc/git/hooks, and exits if any of them fail. 4 | set -e 5 | 6 | # This is necessary because the Emacs extensions don't set GIT_DIR. 7 | if [ -z "$GIT_DIR" ]; then 8 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 9 | GIT_DIR="${DIR}/.." 10 | fi 11 | 12 | # This is necessary because the Atom packages don't set GOPATH 13 | if [ -z "$GOPATH" ]; then 14 | GOPATH=$( cd "$( dirname "${BASH_SOURCE[0]}" )/../../../../../.." && pwd ) 15 | export GOPATH 16 | fi 17 | 18 | for hook in $GIT_DIR/../misc/git/hooks/*; do 19 | $hook 20 | done 21 | -------------------------------------------------------------------------------- /misc/gofmt-all: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . ! -path "./vendor/*" -name '*.go' -exec gofmt -s -w {} \; 4 | -------------------------------------------------------------------------------- /misc/golint-all: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . ! -path "./vendor/*" -name '*.go' -exec golint {} \; 4 | -------------------------------------------------------------------------------- /models/README.md: -------------------------------------------------------------------------------- 1 | # 中间件 Gaea Model 目录内容 2 | 3 | 4 | 5 | ## 1 功能列表 6 | 7 | Model 目录包含以下功能 8 | 9 | - 处理设定值的读写逻辑 10 | 11 | 12 | 13 | ## 2 相关文档 14 | 15 | 以下为相关文档 16 | 17 | - [指定设定值的读写方式 (file etcd etcdv3)](./connection.md) 18 | -------------------------------------------------------------------------------- /models/assets/image-20220124113553383.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124113553383.png -------------------------------------------------------------------------------- /models/assets/image-20220124142003588.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124142003588.png -------------------------------------------------------------------------------- /models/assets/image-20220124143836664.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124143836664.png -------------------------------------------------------------------------------- /models/assets/image-20220124145641452.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124145641452.png -------------------------------------------------------------------------------- /models/assets/image-20220124154131750.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124154131750.png -------------------------------------------------------------------------------- /models/assets/image-20220124182118946.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124182118946.png -------------------------------------------------------------------------------- /models/assets/image-20220124182833234.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124182833234.png -------------------------------------------------------------------------------- /models/assets/image-20220124182944970.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124182944970.png -------------------------------------------------------------------------------- /models/assets/image-20220124183045456.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124183045456.png -------------------------------------------------------------------------------- /models/assets/image-20220124183141813.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124183141813.png -------------------------------------------------------------------------------- /models/assets/image-20220124183228814.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124183228814.png -------------------------------------------------------------------------------- /models/assets/image-20220124183310893.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220124183310893.png -------------------------------------------------------------------------------- /models/assets/image-20220126175410955.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220126175410955.png -------------------------------------------------------------------------------- /models/assets/image-20220127114256168.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220127114256168.png -------------------------------------------------------------------------------- /models/assets/image-20220127142531692.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/assets/image-20220127142531692.png -------------------------------------------------------------------------------- /models/encode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package models 16 | 17 | import ( 18 | "encoding/json" 19 | 20 | "github.com/XiaoMi/Gaea/log" 21 | ) 22 | 23 | // JSONEncode return json encoding of v 24 | func JSONEncode(v interface{}) []byte { 25 | b, err := json.MarshalIndent(v, "", " ") 26 | if err != nil { 27 | //TODO panic 28 | log.Fatal("encode to json failed, %v", err) 29 | return nil 30 | } 31 | return b 32 | } 33 | 34 | // JSONDecode parses the JSON-encoded data and stores the result in the value pointed to by v 35 | func JSONDecode(v interface{}, data []byte) error { 36 | return json.Unmarshal(data, v) 37 | } 38 | -------------------------------------------------------------------------------- /models/encode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package models 15 | 16 | import ( 17 | "testing" 18 | ) 19 | 20 | type TestStruct struct { 21 | Host string `json:"host"` 22 | Port int `json:"port"` 23 | } 24 | 25 | var a = ` 26 | { 27 | "host": "127.0.0.1", 28 | "port": 8888 29 | } 30 | ` 31 | 32 | func TestJSONEncode(t *testing.T) { 33 | ts := &TestStruct{Host: "127.0.0.1", Port: 8888} 34 | JSONEncode(ts) 35 | } 36 | 37 | func TestJSONDecode(t *testing.T) { 38 | var b TestStruct 39 | if err := JSONDecode(&b, []byte(a)); err != nil { 40 | t.Fatalf("test jsonDecode error, %v", err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /models/etcd/etcd_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package etcdclient 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/coreos/etcd/client" 21 | ) 22 | 23 | func Test_isErrNoNode(t *testing.T) { 24 | err := client.Error{} 25 | err.Code = client.ErrorCodeKeyNotFound 26 | if !IsErrNoNode(err) { 27 | t.Fatalf("test isErrNoNode failed, %v", err) 28 | } 29 | err.Code = client.ErrorCodeNotFile 30 | if IsErrNoNode(err) { 31 | t.Fatalf("test isErrNoNode failed, %v", err) 32 | } 33 | } 34 | 35 | func Test_isErrNodeExists(t *testing.T) { 36 | err := client.Error{} 37 | err.Code = client.ErrorCodeNodeExist 38 | if !isErrNodeExists(err) { 39 | t.Fatalf("test isErrNodeExists failed, %v", err) 40 | } 41 | err.Code = client.ErrorCodeNotFile 42 | if isErrNodeExists(err) { 43 | t.Fatalf("test isErrNodeExists failed, %v", err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220113163203011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220113163203011.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220113164918661.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220113164918661.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117035130712.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117035130712.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117040619272.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117040619272.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117040820166.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117040820166.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117041532732.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117041532732.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117041953692.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117041953692.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117042408542.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117042408542.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117043339429.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117043339429.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117112954091.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117112954091.png -------------------------------------------------------------------------------- /models/etcdv3/assets/image-20220117143235293.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/models/etcdv3/assets/image-20220117143235293.png -------------------------------------------------------------------------------- /models/proxy_monitor_metric.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package models 16 | 17 | // ProxyMonitorMetric proxy register information 18 | type ProxyMonitorMetric struct { 19 | Token string `json:"token"` //目前同AdminAddr 20 | StartTime string `json:"start_time"` 21 | 22 | IP string `json:"ip"` 23 | AdminPort string `json:"admin_port"` 24 | ProxyPort string `json:"proxy_port"` 25 | 26 | Pid int `json:"pid"` 27 | Pwd string `json:"pwd"` 28 | Sys string `json:"sys"` 29 | } 30 | 31 | // Encode encode jsosn 32 | func (p *ProxyMonitorMetric) Encode() []byte { 33 | return JSONEncode(p) 34 | } 35 | -------------------------------------------------------------------------------- /models/sequence.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package models 16 | 17 | // GlobalSequence means config of global sequences with different types 18 | type GlobalSequence struct { 19 | DB string `json:"db"` 20 | Table string `json:"table"` 21 | Type string `json:"type"` // 全局序列号类型,目前只兼容mycat的数据库方式 22 | SliceName string `json:"slice_name"` // 对应sequence表所在的分片,默认都在0号片 23 | PKName string `json:"pk_name"` // 全局序列号字段名称 24 | MaxLimit int64 `json:"max_limit"` // 全局序列号上限设置 25 | } 26 | 27 | // Encode means encode for easy use 28 | func (p *GlobalSequence) Encode() []byte { 29 | return JSONEncode(p) 30 | } 31 | -------------------------------------------------------------------------------- /models/store_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package models 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func newTestStore() *Store { 22 | c := NewClient(ConfigEtcd, "127.0.0.1:2381", "test", "test", "") 23 | return NewStore(c) 24 | } 25 | 26 | func TestNewStore(t *testing.T) { 27 | c := NewClient(ConfigEtcd, "127.0.0.1:2381", "test", "test", "") 28 | store := NewStore(c) 29 | defer store.Close() 30 | if store == nil { 31 | t.Fatalf("test NewStore failed") 32 | } 33 | } 34 | 35 | func TestNamespaceBase(t *testing.T) { 36 | store := newTestStore() 37 | defer store.Close() 38 | base := store.NamespaceBase() 39 | if base != "/gaea/namespace" { 40 | t.Fatalf("test NamespaceBase failed, %v", base) 41 | } 42 | } 43 | 44 | func TestNamespacePath(t *testing.T) { 45 | store := newTestStore() 46 | defer store.Close() 47 | path := store.NamespacePath("test") 48 | if path != "/gaea/namespace/test" { 49 | t.Fatalf("test NamespacePath failed, %v", path) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /mysql/result_pool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // 借鉴 zap 优秀的设计 16 | 17 | package mysql 18 | 19 | import "sync" 20 | 21 | type resultPool struct { 22 | p1 *sync.Pool 23 | p2 *sync.Pool 24 | } 25 | 26 | var ResultPool = &resultPool{ 27 | // 有 result 的对象池 28 | p1: &sync.Pool{ 29 | New: func() interface{} { 30 | return new(Result) 31 | }, 32 | }, 33 | // 没有 result 的对象池 34 | p2: &sync.Pool{ 35 | New: func() interface{} { 36 | return new(Result) 37 | }, 38 | }, 39 | } 40 | 41 | func (rp *resultPool) Get() *Result { 42 | r := rp.p1.Get().(*Result) 43 | r.pool = rp 44 | r.Reset() 45 | if r.Resultset == nil { 46 | r.Resultset = &Resultset{} 47 | } 48 | return r 49 | } 50 | 51 | func (rp *resultPool) GetWithoutResultSet() *Result { 52 | r := rp.p2.Get().(*Result) 53 | r.pool = rp 54 | r.Reset() 55 | r.Resultset = nil 56 | return r 57 | } 58 | 59 | func (rp *resultPool) Put(r *Result) { 60 | if r.Resultset != nil { 61 | rp.p1.Put(r) 62 | } else { 63 | rp.p2.Put(r) 64 | } 65 | } 66 | 67 | func (r *Result) Reset() { 68 | r.Status = 0 69 | r.InsertID = 0 70 | r.AffectedRows = 0 71 | r.Warnings = 0 72 | r.Info = "" 73 | if r.Resultset != nil { 74 | r.Resultset.Fields = r.Resultset.Fields[:0] 75 | r.Resultset.Values = r.Resultset.Values[:0] 76 | r.Resultset.RowDatas = r.Resultset.RowDatas[:0] 77 | r.Resultset.FieldNames = make(map[string]int) 78 | } 79 | } 80 | 81 | func (r *Result) Free() { 82 | if r.pool != nil { 83 | r.pool.Put(r) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /mysql/result_pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mysql 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | // test result set 24 | func TestGetResultSet(t *testing.T) { 25 | rs := ResultPool.Get() 26 | assert.Equal(t, rs.Resultset == nil, false) 27 | rs.Free() 28 | rs = ResultPool.GetWithoutResultSet() 29 | assert.Equal(t, rs.Resultset == nil, true) 30 | rs.Free() 31 | rss := make([]*Result, 0) 32 | for i := 0; i < 10; i++ { 33 | rs = ResultPool.Get() 34 | assert.Equal(t, rs.Resultset == nil, false) 35 | rss = append(rss, rs) 36 | } 37 | for i := 0; i < 10; i++ { 38 | rs = ResultPool.GetWithoutResultSet() 39 | assert.Equal(t, rs.Resultset == nil, true) 40 | rss = append(rss, rs) 41 | } 42 | for _, rs := range rss { 43 | rs.Free() 44 | } 45 | for i := 0; i < 10; i++ { 46 | rs = ResultPool.Get() 47 | assert.Equal(t, rs.Resultset == nil, false) 48 | rs.Free() 49 | } 50 | for i := 0; i < 10; i++ { 51 | rs = ResultPool.GetWithoutResultSet() 52 | assert.Equal(t, rs.Resultset == nil, true) 53 | rs.Free() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mysql/type_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package mysql 15 | 16 | import ( 17 | "github.com/pingcap/check" 18 | ) 19 | 20 | var _ = check.Suite(&testTypeSuite{}) 21 | 22 | type testTypeSuite struct{} 23 | 24 | func (s *testTypeSuite) TestFlags(c *check.C) { 25 | c.Assert(HasNotNullFlag(NotNullFlag), check.IsTrue) 26 | c.Assert(HasUniKeyFlag(UniqueKeyFlag), check.IsTrue) 27 | c.Assert(HasNotNullFlag(NotNullFlag), check.IsTrue) 28 | c.Assert(HasNoDefaultValueFlag(NoDefaultValueFlag), check.IsTrue) 29 | c.Assert(HasAutoIncrementFlag(AutoIncrementFlag), check.IsTrue) 30 | c.Assert(HasUnsignedFlag(UnsignedFlag), check.IsTrue) 31 | c.Assert(HasZerofillFlag(ZerofillFlag), check.IsTrue) 32 | c.Assert(HasBinaryFlag(BinaryFlag), check.IsTrue) 33 | c.Assert(HasPriKeyFlag(PriKeyFlag), check.IsTrue) 34 | c.Assert(HasMultipleKeyFlag(MultipleKeyFlag), check.IsTrue) 35 | c.Assert(HasTimestampFlag(TimestampFlag), check.IsTrue) 36 | c.Assert(HasOnUpdateNowFlag(OnUpdateNowFlag), check.IsTrue) 37 | } 38 | -------------------------------------------------------------------------------- /parser/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all parser clean goyacc 2 | 3 | ARCH:="$(shell uname -s)" 4 | MAC:="Darwin" 5 | LINUX:="Linux" 6 | 7 | all: parser.go fmt 8 | 9 | test: parser.go fmt 10 | GO111MODULE=on go test ./... -coverprofile=coverage.out 11 | 12 | parser.go: parser.y 13 | make parser 14 | 15 | parser: goyacc 16 | ./goyacc/bin/goyacc -o /dev/null parser.y 17 | ./goyacc/bin/goyacc -o parser.go parser.y 2>&1 | egrep "(shift|reduce)/reduce" | awk '{print} END {if (NR > 0) {print "Find conflict in parser.y. Please check y.output for more information."; exit 1;}}' 18 | rm -f y.output 19 | 20 | # @if [ $(ARCH) = $(LINUX) ]; \ 21 | # then \ 22 | # sed -i -e 's|//line.*||' -e 's/yyEofCode/yyEOFCode/' parser.go; \ 23 | # elif [ $(ARCH) = $(MAC) ]; \ 24 | # then \ 25 | # /usr/bin/sed -i "" 's|//line.*||' parser.go; \ 26 | # /usr/bin/sed -i "" 's/yyEofCode/yyEOFCode/' parser.go; \ 27 | # fi 28 | 29 | @awk 'BEGIN{print "// Code generated by goyacc DO NOT EDIT."} {print $0}' parser.go > tmp_parser.go && mv tmp_parser.go parser.go; 30 | 31 | #bin/goyacc: goyacc/main.go 32 | goyacc: 33 | #@echo "build goyacc" 34 | cd ./goyacc && make goyacc 35 | 36 | fmt: 37 | #@echo "gofmt (simplify)" 38 | #@ gofmt -s -l -w . 2>&1 | awk '{print} END{if(NR>0) {exit 1}}' 39 | 40 | clean: 41 | go clean -i ./... 42 | rm -rf *.out 43 | rm parser.go 44 | -------------------------------------------------------------------------------- /parser/README.md: -------------------------------------------------------------------------------- 1 | # parser 2 | 3 | 4 | ## goyacc 5 | 基于 tidb 的 goyacc 版本:https://github.com/pingcap/parser/releases/tag/v2.1.5 6 | 7 | ### 使用 8 | 9 | ```bash 10 | # 编译 goyacc 11 | make goyacc 12 | 13 | # 生成 parser.go 14 | make parser 15 | 16 | ``` 17 | 18 | - 修改 parser.y 后请添加对应的测试用例,并保证测试通过 19 | ```bash 20 | make test 21 | ``` 22 | -------------------------------------------------------------------------------- /parser/ast/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package ast 15 | 16 | // IsReadOnly checks whether the input ast is readOnly. 17 | func IsReadOnly(node Node) bool { 18 | switch st := node.(type) { 19 | case *SelectStmt: 20 | if st.LockTp == SelectLockForUpdate { 21 | return false 22 | } 23 | 24 | checker := readOnlyChecker{ 25 | readOnly: true, 26 | } 27 | 28 | node.Accept(&checker) 29 | return checker.readOnly 30 | case *ExplainStmt, *DoStmt: 31 | return true 32 | default: 33 | return false 34 | } 35 | } 36 | 37 | // readOnlyChecker checks whether a query's ast is readonly, if it satisfied 38 | // 1. selectstmt; 39 | // 2. need not to set var; 40 | // it is readonly statement. 41 | type readOnlyChecker struct { 42 | readOnly bool 43 | } 44 | 45 | // Enter implements Visitor interface. 46 | func (checker *readOnlyChecker) Enter(in Node) (out Node, skipChildren bool) { 47 | switch node := in.(type) { 48 | case *VariableExpr: 49 | // like func rewriteVariable(), this stands for SetVar. 50 | if !node.IsSystem && node.Value != nil { 51 | checker.readOnly = false 52 | return in, true 53 | } 54 | } 55 | return in, false 56 | } 57 | 58 | // Leave implements Visitor interface. 59 | func (checker *readOnlyChecker) Leave(in Node) (out Node, ok bool) { 60 | return in, checker.readOnly 61 | } 62 | -------------------------------------------------------------------------------- /parser/auth/auth_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package auth 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/stretchr/testify/require" 20 | 21 | "github.com/XiaoMi/Gaea/util/testleak" 22 | ) 23 | 24 | func TestEncodePassword(t *testing.T) { 25 | defer testleak.AfterTestT(t)() 26 | pwd := "123" 27 | require.Equal(t, "*23AE809DDACAF96AF0FD78ED04B6A265E05AA257", EncodePassword(pwd)) // NUL 28 | } 29 | 30 | func TestDecodePassword(t *testing.T) { 31 | defer testleak.AfterTestT(t)() 32 | x, err := DecodePassword(EncodePassword("123")) 33 | require.NoError(t, err) 34 | require.Equal(t, Sha1Hash(Sha1Hash([]byte("123"))), x) 35 | } 36 | 37 | func TestCheckScramble(t *testing.T) { 38 | defer testleak.AfterTestT(t)() 39 | pwd := "abc" 40 | salt := []byte{85, 92, 45, 22, 58, 79, 107, 6, 122, 125, 58, 80, 12, 90, 103, 32, 90, 10, 74, 82} 41 | auth := []byte{24, 180, 183, 225, 166, 6, 81, 102, 70, 248, 199, 143, 91, 204, 169, 9, 161, 171, 203, 33} 42 | encodepwd := EncodePassword(pwd) 43 | hpwd, err := DecodePassword(encodepwd) 44 | require.NoError(t, err) 45 | 46 | res := CheckScrambledPassword(salt, hpwd, auth) 47 | require.True(t, res) 48 | } 49 | -------------------------------------------------------------------------------- /parser/goyacc/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: goyacc clean 2 | 3 | goyacc: 4 | GO111MODULE=on go build -o ./bin/goyacc main.go 5 | 6 | clean: 7 | rm -rf bin/goyacc 8 | -------------------------------------------------------------------------------- /parser/goyacc/go.mod: -------------------------------------------------------------------------------- 1 | module goyacc 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/cznic/golex v0.0.0-20181122101858-9c343928389c // indirect 7 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 8 | github.com/cznic/parser v0.0.0-20181122101858-d773202d5b1f 9 | github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8 10 | github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 11 | github.com/cznic/y v0.0.0-20181122101901-b05e8c2e8d7b 12 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /parser/goyacc/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cznic/golex v0.0.0-20181122101858-9c343928389c h1:G8zTsaqyVfIHpgMFcGgdbhHSFhlNc77rAKkhVbQ9kQg= 2 | github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= 3 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= 4 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= 5 | github.com/cznic/parser v0.0.0-20181122101858-d773202d5b1f h1:DUtr2TvhM9rmiHKVJWoLqDY2+MdxljW9hlaS/oYoi1c= 6 | github.com/cznic/parser v0.0.0-20181122101858-d773202d5b1f/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM= 7 | github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8 h1:LpMLYGyy67BoAFGda1NeOBQwqlv7nUXpm+rIVHGxZZ4= 8 | github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= 9 | github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 h1:MZRmHqDBd0vxNwenEbKSQqRVT24d3C05ft8kduSwlqM= 10 | github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= 11 | github.com/cznic/y v0.0.0-20181122101901-b05e8c2e8d7b h1:gvFsf4zJcnW6GRN+HPGTxwuw+7sTwzmoeoBQQCZDEnk= 12 | github.com/cznic/y v0.0.0-20181122101901-b05e8c2e8d7b/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs= 13 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 14 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 15 | -------------------------------------------------------------------------------- /parser/opcode/opcode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package opcode 15 | 16 | import ( 17 | "bytes" 18 | "testing" 19 | ) 20 | 21 | func TestT(t *testing.T) { 22 | op := Plus 23 | if op.String() != "plus" { 24 | t.Fatalf("invalid op code") 25 | } 26 | 27 | if len(Ops) != len(opsLiteral) { 28 | t.Error("inconsistent count ops and opsliteral") 29 | } 30 | var buf bytes.Buffer 31 | for op := range Ops { 32 | op.Format(&buf) 33 | if buf.String() != opsLiteral[op] { 34 | t.Error("format op fail", op) 35 | } 36 | buf.Reset() 37 | } 38 | 39 | // Test invalid opcode 40 | defer func() { 41 | recover() 42 | }() 43 | 44 | op = 0 45 | s := op.String() 46 | if len(s) > 0 { 47 | t.Fail() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /parser/parser_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package parser_test 15 | 16 | import ( 17 | "fmt" 18 | 19 | "github.com/XiaoMi/Gaea/parser" 20 | _ "github.com/XiaoMi/Gaea/parser/tidb-types/parser_driver" 21 | ) 22 | 23 | // This example show how to parse a text sql into ast. 24 | func Example_parseSQL() { 25 | 26 | // 0. make sure import parser_driver implemented by TiDB(user also can implement own driver by self). 27 | // and add `import _ "github.com/pingcap/tidb/types/parser_driver"` in the head of file. 28 | 29 | // 1. Create a parser. The parser is NOT goroutine safe and should 30 | // not be shared among multiple goroutines. However, parser is also 31 | // heavy, so each goroutine should reuse its own local instance if 32 | // possible. 33 | p := parser.New() 34 | 35 | // 2. Parse a text SQL into AST([]ast.StmtNode). 36 | stmtNodes, _, err := p.Parse("select * from tbl where id = 1", "", "") 37 | 38 | // 3. Use AST to do cool things. 39 | fmt.Println(stmtNodes[0], err) 40 | } 41 | -------------------------------------------------------------------------------- /parser/tidb-types/compare.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | // CompareInt64 returns an integer comparing the int64 x to y. 17 | func CompareInt64(x, y int64) int { 18 | if x < y { 19 | return -1 20 | } else if x == y { 21 | return 0 22 | } 23 | 24 | return 1 25 | } 26 | 27 | // CompareUint64 returns an integer comparing the uint64 x to y. 28 | func CompareUint64(x, y uint64) int { 29 | if x < y { 30 | return -1 31 | } else if x == y { 32 | return 0 33 | } 34 | 35 | return 1 36 | } 37 | 38 | // CompareFloat64 returns an integer comparing the float64 x to y. 39 | func CompareFloat64(x, y float64) int { 40 | if x < y { 41 | return -1 42 | } else if x == y { 43 | return 0 44 | } 45 | 46 | return 1 47 | } 48 | 49 | // CompareString returns an integer comparing the string x to y. 50 | func CompareString(x, y string) int { 51 | if x < y { 52 | return -1 53 | } else if x == y { 54 | return 0 55 | } 56 | 57 | return 1 58 | } 59 | -------------------------------------------------------------------------------- /parser/tidb-types/datum_eval.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | import ( 17 | "github.com/cznic/mathutil" 18 | "github.com/pingcap/errors" 19 | 20 | "github.com/XiaoMi/Gaea/parser/opcode" 21 | ) 22 | 23 | // ComputePlus computes the result of a+b. 24 | func ComputePlus(a, b Datum) (d Datum, err error) { 25 | switch a.Kind() { 26 | case KindInt64: 27 | switch b.Kind() { 28 | case KindInt64: 29 | r, err1 := AddInt64(a.GetInt64(), b.GetInt64()) 30 | d.SetInt64(r) 31 | return d, errors.Trace(err1) 32 | case KindUint64: 33 | r, err1 := AddInteger(b.GetUint64(), a.GetInt64()) 34 | d.SetUint64(r) 35 | return d, errors.Trace(err1) 36 | } 37 | case KindUint64: 38 | switch b.Kind() { 39 | case KindInt64: 40 | r, err1 := AddInteger(a.GetUint64(), b.GetInt64()) 41 | d.SetUint64(r) 42 | return d, errors.Trace(err1) 43 | case KindUint64: 44 | r, err1 := AddUint64(a.GetUint64(), b.GetUint64()) 45 | d.SetUint64(r) 46 | return d, errors.Trace(err1) 47 | } 48 | case KindFloat64: 49 | switch b.Kind() { 50 | case KindFloat64: 51 | r := a.GetFloat64() + b.GetFloat64() 52 | d.SetFloat64(r) 53 | return d, nil 54 | } 55 | case KindMysqlDecimal: 56 | switch b.Kind() { 57 | case KindMysqlDecimal: 58 | r := new(MyDecimal) 59 | err = DecimalAdd(a.GetMysqlDecimal(), b.GetMysqlDecimal(), r) 60 | d.SetMysqlDecimal(r) 61 | d.SetFrac(mathutil.Max(a.Frac(), b.Frac())) 62 | return d, err 63 | } 64 | } 65 | _, err = InvOp2(a.GetValue(), b.GetValue(), opcode.Plus) 66 | return d, err 67 | } 68 | -------------------------------------------------------------------------------- /parser/tidb-types/enum.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | import ( 17 | "strconv" 18 | "strings" 19 | 20 | "github.com/pingcap/errors" 21 | ) 22 | 23 | // Enum is for MySQL enum type. 24 | type Enum struct { 25 | Name string 26 | Value uint64 27 | } 28 | 29 | // String implements fmt.Stringer interface. 30 | func (e Enum) String() string { 31 | return e.Name 32 | } 33 | 34 | // ToNumber changes enum index to float64 for numeric operation. 35 | func (e Enum) ToNumber() float64 { 36 | return float64(e.Value) 37 | } 38 | 39 | // ParseEnumName creates a Enum with item name. 40 | func ParseEnumName(elems []string, name string) (Enum, error) { 41 | for i, n := range elems { 42 | if strings.EqualFold(n, name) { 43 | return Enum{Name: n, Value: uint64(i) + 1}, nil 44 | } 45 | } 46 | 47 | // name doesn't exist, maybe an integer? 48 | if num, err := strconv.ParseUint(name, 0, 64); err == nil { 49 | return ParseEnumValue(elems, num) 50 | } 51 | 52 | return Enum{}, errors.Errorf("item %s is not in enum %v", name, elems) 53 | } 54 | 55 | // ParseEnumValue creates a Enum with special number. 56 | func ParseEnumValue(elems []string, number uint64) (Enum, error) { 57 | if number == 0 || number > uint64(len(elems)) { 58 | return Enum{}, errors.Errorf("number %d overflow enum boundary [1, %d]", number, len(elems)) 59 | } 60 | 61 | return Enum{Name: elems[number-1], Value: number}, nil 62 | } 63 | -------------------------------------------------------------------------------- /parser/tidb-types/enum_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/stretchr/testify/require" 20 | 21 | "github.com/XiaoMi/Gaea/util/testleak" 22 | ) 23 | 24 | func TestEnum(t *testing.T) { 25 | defer testleak.AfterTestT(t)() 26 | tbl := []struct { 27 | Elems []string 28 | Name string 29 | Expected int 30 | }{ 31 | {[]string{"a", "b"}, "a", 1}, 32 | {[]string{"a"}, "b", 0}, 33 | {[]string{"a"}, "1", 1}, 34 | } 35 | 36 | for _, tt := range tbl { 37 | e, err := ParseEnumName(tt.Elems, tt.Name) 38 | if tt.Expected == 0 { 39 | require.Error(t, err) 40 | require.Equal(t, float64(0), e.ToNumber()) 41 | require.Equal(t, "", e.String()) 42 | continue 43 | } 44 | 45 | require.NoError(t, err) 46 | require.Equal(t, tt.Elems[tt.Expected-1], e.String()) 47 | require.Equal(t, float64(tt.Expected), e.ToNumber()) 48 | } 49 | 50 | tblNumber := []struct { 51 | Elems []string 52 | Number uint64 53 | Expected int 54 | }{ 55 | {[]string{"a"}, 1, 1}, 56 | {[]string{"a"}, 0, 0}, 57 | } 58 | 59 | for _, tt := range tblNumber { 60 | e, err := ParseEnumValue(tt.Elems, tt.Number) 61 | if tt.Expected == 0 { 62 | require.Error(t, err) 63 | continue 64 | } 65 | require.NoError(t, err) 66 | require.Equal(t, float64(tt.Expected), e.ToNumber()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /parser/tidb-types/eval_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | import ast "github.com/XiaoMi/Gaea/parser/types" 17 | 18 | // EvalType indicates the specified types that arguments and result of a built-in function should be. 19 | type EvalType = ast.EvalType 20 | 21 | const ( 22 | // ETInt represents type INT in evaluation. 23 | ETInt = ast.ETInt 24 | // ETReal represents type REAL in evaluation. 25 | ETReal = ast.ETReal 26 | // ETDecimal represents type DECIMAL in evaluation. 27 | ETDecimal = ast.ETDecimal 28 | // ETString represents type STRING in evaluation. 29 | ETString = ast.ETString 30 | // ETDatetime represents type DATETIME in evaluation. 31 | ETDatetime = ast.ETDatetime 32 | // ETTimestamp represents type TIMESTAMP in evaluation. 33 | ETTimestamp = ast.ETTimestamp 34 | // ETDuration represents type DURATION in evaluation. 35 | ETDuration = ast.ETDuration 36 | // ETJson represents type JSON in evaluation. 37 | ETJson = ast.ETJson 38 | ) 39 | -------------------------------------------------------------------------------- /parser/tidb-types/export_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | // CheckTimestampTypeForTest export CheckTimestampType for test. 17 | var CheckTimestampTypeForTest = checkTimestampType 18 | -------------------------------------------------------------------------------- /parser/tidb-types/helper_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | import ( 17 | "strconv" 18 | "testing" 19 | 20 | "github.com/pingcap/errors" 21 | "github.com/stretchr/testify/require" 22 | ) 23 | 24 | func TestStrToInt(t *testing.T) { 25 | tests := []struct { 26 | input string 27 | output string 28 | err error 29 | }{ 30 | {"9223372036854775806", "9223372036854775806", nil}, 31 | {"9223372036854775807", "9223372036854775807", nil}, 32 | {"9223372036854775808", "9223372036854775807", ErrBadNumber}, 33 | {"-9223372036854775807", "-9223372036854775807", nil}, 34 | {"-9223372036854775808", "-9223372036854775808", nil}, 35 | {"-9223372036854775809", "-9223372036854775808", ErrBadNumber}, 36 | } 37 | for _, tt := range tests { 38 | output, err := strToInt(tt.input) 39 | require.Equal(t, tt.err, errors.Cause(err)) 40 | require.Equal(t, tt.output, strconv.FormatInt(output, 10)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /parser/tidb-types/json/path_expr_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package json 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/stretchr/testify/require" 20 | ) 21 | 22 | func TestContainsAnyAsterisk(t *testing.T) { 23 | var tests = []struct { 24 | exprString string 25 | containsAsterisks bool 26 | }{ 27 | {"$.a[b]", false}, 28 | {"$.a[*]", true}, 29 | {"$.*[b]", true}, 30 | {"$**.a[b]", true}, 31 | } 32 | for _, tt := range tests { 33 | pe, err := ParseJSONPathExpr(tt.exprString) 34 | require.NoError(t, err) 35 | require.Equal(t, tt.containsAsterisks, pe.flags.containsAnyAsterisk()) 36 | } 37 | } 38 | 39 | func TestValidatePathExpr(t *testing.T) { 40 | var tests = []struct { 41 | exprString string 42 | success bool 43 | legs int 44 | }{ 45 | {` $ `, true, 0}, 46 | {" $ . key1 [ 3 ]\t[*].*.key3", true, 5}, 47 | {" $ . key1 [ 3 ]**[*].*.key3", true, 6}, 48 | {`$."key1 string"[ 3 ][*].*.key3`, true, 5}, 49 | {`$."hello \"escaped quotes\" world\\n"[3][*].*.key3`, true, 5}, 50 | 51 | {`$.\"escaped quotes\"[3][*].*.key3`, false, 0}, 52 | {`$.hello \"escaped quotes\" world[3][*].*.key3`, false, 0}, 53 | {`$NoValidLegsHere`, false, 0}, 54 | {`$ No Valid Legs Here .a.b.c`, false, 0}, 55 | } 56 | 57 | for _, tt := range tests { 58 | pe, err := ParseJSONPathExpr(tt.exprString) 59 | if tt.success { 60 | require.NoError(t, err) 61 | require.Len(t, pe.legs, tt.legs) 62 | } else { 63 | require.Error(t, err) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /parser/tidb-types/parser_driver/value_expr_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package driver 15 | 16 | import ( 17 | "strings" 18 | "testing" 19 | 20 | "github.com/stretchr/testify/require" 21 | 22 | "github.com/XiaoMi/Gaea/parser/format" 23 | types "github.com/XiaoMi/Gaea/parser/tidb-types" 24 | ) 25 | 26 | func TestValueExprRestore(t *testing.T) { 27 | testCases := []struct { 28 | datum types.Datum 29 | expect string 30 | }{ 31 | {types.NewDatum(nil), "NULL"}, 32 | {types.NewIntDatum(1), "1"}, 33 | {types.NewIntDatum(-1), "-1"}, 34 | {types.NewUintDatum(1), "1"}, 35 | {types.NewFloat32Datum(1.1), "1.1e+00"}, 36 | {types.NewFloat64Datum(1.1), "1.1e+00"}, 37 | {types.NewStringDatum("test `s't\"r."), "'test `s''t\"r.'"}, 38 | {types.NewBytesDatum([]byte("test `s't\"r.")), "'test `s''t\"r.'"}, 39 | {types.NewBinaryLiteralDatum([]byte("test `s't\"r.")), "b'11101000110010101110011011101000010000001100000011100110010011101110100001000100111001000101110'"}, 40 | {types.NewDecimalDatum(types.NewDecFromInt(321)), "321"}, 41 | } 42 | // Run Test 43 | var sb strings.Builder 44 | for _, testCase := range testCases { 45 | sb.Reset() 46 | expr := &ValueExpr{Datum: testCase.datum} 47 | err := expr.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)) 48 | require.NoError(t, err) 49 | require.Equal(t, testCase.expect, sb.String()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /parser/types/eval_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package types 15 | 16 | // EvalType indicates the specified types that arguments and result of a built-in function should be. 17 | type EvalType byte 18 | 19 | const ( 20 | // ETInt represents type INT in evaluation. 21 | ETInt EvalType = iota 22 | // ETReal represents type REAL in evaluation. 23 | ETReal 24 | // ETDecimal represents type DECIMAL in evaluation. 25 | ETDecimal 26 | // ETString represents type STRING in evaluation. 27 | ETString 28 | // ETDatetime represents type DATETIME in evaluation. 29 | ETDatetime 30 | // ETTimestamp represents type TIMESTAMP in evaluation. 31 | ETTimestamp 32 | // ETDuration represents type DURATION in evaluation. 33 | ETDuration 34 | // ETJson represents type JSON in evaluation. 35 | ETJson 36 | ) 37 | 38 | // IsStringKind returns true for ETString, ETDatetime, ETTimestamp, ETDuration, ETJson EvalTypes. 39 | func (et EvalType) IsStringKind() bool { 40 | return et == ETString || et == ETDatetime || 41 | et == ETTimestamp || et == ETDuration || et == ETJson 42 | } 43 | -------------------------------------------------------------------------------- /parser/yy_parser_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/XiaoMi/Gaea/parser/ast" 8 | "github.com/XiaoMi/Gaea/parser/tidb-types/parser_driver" 9 | ) 10 | 11 | func TestNodeToString(t *testing.T) { 12 | tableName := "tb1" 13 | d := &driver.ValueExpr{} 14 | d.SetValue(tableName) 15 | s, err := NodeToStringWithoutQuote(d) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | if s != tableName { 20 | t.Errorf("table name not equal, expect: %s, actual: %s", tableName, s) 21 | } 22 | } 23 | 24 | type NodePrintVisitor struct { 25 | } 26 | 27 | func (v *NodePrintVisitor) Enter(n ast.Node) (ast.Node, bool) { 28 | fmt.Printf("enter: %T\n", n) 29 | return n, false 30 | } 31 | 32 | func (v *NodePrintVisitor) Leave(n ast.Node) (ast.Node, bool) { 33 | fmt.Printf("leave: %T\n", n) 34 | return n, true 35 | } 36 | 37 | func TestASTNode(t *testing.T) { 38 | sql := `desc xm_order` 39 | n, err := ParseSQL(sql) 40 | if err != nil { 41 | t.Fatalf("parse sql error: %v", err) 42 | } 43 | v := &NodePrintVisitor{} 44 | n.Accept(v) 45 | } 46 | -------------------------------------------------------------------------------- /proxy/plan/decorator_binary_operation_expr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plan 16 | 17 | import ( 18 | "github.com/XiaoMi/Gaea/parser/ast" 19 | driver "github.com/XiaoMi/Gaea/parser/tidb-types/parser_driver" 20 | ) 21 | 22 | // BinaryOperationFieldtype declares field type of binary operation 23 | type BinaryOperationFieldtype int 24 | 25 | // Expr type 26 | const ( 27 | UnsupportExpr BinaryOperationFieldtype = iota 28 | ValueExpr 29 | ColumnNameExpr 30 | FuncCallExpr 31 | ) 32 | 33 | func getExprNodeTypeInBinaryOperation(n ast.ExprNode) BinaryOperationFieldtype { 34 | switch n.(type) { 35 | case *ast.ColumnNameExpr: 36 | return ColumnNameExpr 37 | case *driver.ValueExpr: 38 | return ValueExpr 39 | case *ast.FuncCallExpr: 40 | return FuncCallExpr 41 | default: 42 | return UnsupportExpr 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /proxy/plan/decorator_limit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plan 16 | 17 | import ( 18 | "github.com/XiaoMi/Gaea/parser/ast" 19 | driver "github.com/XiaoMi/Gaea/parser/tidb-types/parser_driver" 20 | ) 21 | 22 | // NeedRewriteLimitOrCreateRewrite check if SelectStmt need rewrite limit clause, 23 | // if need, create a rewritten limit clause. 24 | // count == -1代表没有Limit子句 25 | func NeedRewriteLimitOrCreateRewrite(stmt *ast.SelectStmt) (bool, int64, int64, *ast.Limit) { 26 | limit := stmt.Limit 27 | if limit == nil { 28 | return false, -1, -1, nil 29 | } 30 | 31 | count := limit.Count.(*driver.ValueExpr).GetInt64() 32 | 33 | if limit.Offset == nil { 34 | return false, 0, count, nil 35 | } 36 | 37 | offset := limit.Offset.(*driver.ValueExpr).GetInt64() 38 | 39 | if offset == 0 { 40 | return false, 0, count, nil 41 | } 42 | 43 | newCount := count + offset 44 | nv := &driver.ValueExpr{} 45 | nv.SetInt64(newCount) 46 | newLimit := &ast.Limit{ 47 | Count: nv, 48 | } 49 | return true, offset, count, newLimit 50 | } 51 | -------------------------------------------------------------------------------- /proxy/plan/merge_result_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plan 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/XiaoMi/Gaea/mysql" 22 | ) 23 | 24 | func TestLimitSelectResult(t *testing.T) { 25 | tests := []struct { 26 | offset int64 27 | count int64 28 | retLen int64 29 | trimedRetLen int64 30 | }{ 31 | {-1, -1, 5, 5}, 32 | {0, -1, 5, 5}, 33 | {0, 3, 5, 3}, 34 | {0, 10, 5, 5}, 35 | {3, 10, 5, 2}, 36 | {3, 1, 5, 1}, 37 | {4, 1, 5, 1}, 38 | {5, 1, 5, 0}, 39 | {5, 10, 5, 0}, 40 | } 41 | 42 | for _, test := range tests { 43 | t.Run(fmt.Sprintf("%d:%d", test.offset, test.count), func(t *testing.T) { 44 | info := &SelectPlan{ 45 | offset: test.offset, 46 | count: test.count, 47 | } 48 | 49 | ret := &mysql.Result{ 50 | Resultset: &mysql.Resultset{ 51 | Values: make([][]interface{}, test.retLen), 52 | RowDatas: make([]mysql.RowData, test.retLen), 53 | }, 54 | } 55 | 56 | if err := limitSelectResult(info, ret); err != nil { 57 | t.Fatalf("limitSelectResult error: %v", err) 58 | } 59 | 60 | if int64(len(ret.Values)) != test.trimedRetLen { 61 | t.Errorf("len Values not equal, expect: %d, actual: %d", test.trimedRetLen, len(ret.Values)) 62 | } 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /proxy/plan/plan_exec_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plan 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/XiaoMi/Gaea/parser" 21 | "github.com/XiaoMi/Gaea/util" 22 | ) 23 | 24 | func TestExecuteIn(t *testing.T) { 25 | planInfo, _ := preparePlanInfo() 26 | sql := "SELECT * FROM tbl_mycat_murmur WHERE tbl_mycat_murmur.id=5 AND tbl_mycat_murmur.id=4" 27 | stmt, _ := parser.ParseSQL(sql) 28 | plan, err := BuildPlan(stmt, nil, "db_mycat", sql, planInfo.rt, planInfo.seqs, nil) 29 | if err != nil { 30 | t.Fatalf("build plan error: %v", err) 31 | } 32 | ret, err := plan.ExecuteIn(util.NewRequestContext(), nil) 33 | if err != nil { 34 | t.Fatalf("execute error: %v", err) 35 | } 36 | t.Logf("result: %v", ret) 37 | } 38 | -------------------------------------------------------------------------------- /proxy/router/shard_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package router 16 | 17 | import "testing" 18 | 19 | func TestGetString(t *testing.T) { 20 | tests := []struct { 21 | v interface{} 22 | vs string 23 | }{ 24 | {int(-1), "-1"}, 25 | {int64(-1), "-1"}, 26 | {"-1", "-1"}, 27 | {[]byte("-1"), "-1"}, 28 | {int(0), "0"}, 29 | {int64(0), "0"}, 30 | {uint(0), "0"}, 31 | {uint64(0), "0"}, 32 | {"0", "0"}, 33 | {[]byte("0"), "0"}, 34 | {int(1), "1"}, 35 | {int64(1), "1"}, 36 | {uint(1), "1"}, 37 | {uint64(1), "1"}, 38 | {"1", "1"}, 39 | {[]byte("1"), "1"}, 40 | } 41 | for _, test := range tests { 42 | t.Run(test.vs, func(t *testing.T) { 43 | actualVs := GetString(test.v) 44 | if actualVs != test.vs { 45 | t.Errorf("not equal, v: %v, expect: %s, actual: %s", test.v, test.vs, actualVs) 46 | } 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /proxy/sequence/sequence.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sequence 16 | 17 | import "fmt" 18 | 19 | // Sequence is interface of global sequences with different types 20 | type Sequence interface { 21 | GetPKName() string 22 | NextSeq() (int64, error) 23 | } 24 | 25 | type SequenceManager struct { 26 | sequences map[string]map[string]Sequence 27 | } 28 | 29 | func NewSequenceManager() *SequenceManager { 30 | return &SequenceManager{ 31 | sequences: make(map[string]map[string]Sequence), 32 | } 33 | } 34 | 35 | func (s *SequenceManager) SetSequence(db, table string, seq Sequence) error { 36 | if _, ok := s.sequences[db]; !ok { 37 | s.sequences[db] = make(map[string]Sequence) 38 | } 39 | if _, ok := s.sequences[db][table]; ok { 40 | return fmt.Errorf("already set sequence, db: %s, table: %s", db, table) 41 | } 42 | 43 | s.sequences[db][table] = seq 44 | return nil 45 | } 46 | 47 | func (s *SequenceManager) GetSequence(db, table string) (Sequence, bool) { 48 | dbSeq, ok := s.sequences[db] 49 | if !ok { 50 | return nil, false 51 | } 52 | seq, ok := dbSeq[table] 53 | return seq, ok 54 | } 55 | -------------------------------------------------------------------------------- /proxy/server/client_conn_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/XiaoMi/Gaea/backend" 5 | "github.com/XiaoMi/Gaea/mysql" 6 | "github.com/bytedance/mockey" 7 | "github.com/stretchr/testify/assert" 8 | "testing" 9 | ) 10 | 11 | // 检测 continueConn.MoreRowsExist Fields 是否正常 12 | func TestWriteOkResultStreamMoreRowsExistFields(t *testing.T) { 13 | mockey.PatchConvey("test", t, func() { 14 | testMoreRowExistStatus := []bool{true, true, false} 15 | testMoreRowExistIndex := 0 16 | mockey.Mock((*ClientConn).writeOK).Return(nil).Build() 17 | mockey.Mock((*ClientConn).writeOKResult).To(func(status uint16, moreRows bool, r *mysql.Result) error { 18 | r.Free() 19 | return nil 20 | }).Build() 21 | mockey.Mock((*ClientConn).writeRowsWithEOF).To(func(r *mysql.Result, moreRowsExists bool, status uint16) error { 22 | r.Free() 23 | return nil 24 | }).Build() 25 | mockey.Mock((*mysql.Result).BuildBinaryResultSet).Return(nil).Build() 26 | mockey.Mock((*backend.MockPooledConnect).MoreRowsExist).To(func() bool { 27 | if testMoreRowExistIndex >= len(testMoreRowExistStatus) { 28 | return false 29 | } 30 | ret := testMoreRowExistStatus[testMoreRowExistIndex] 31 | testMoreRowExistIndex += 1 32 | return ret 33 | }).Build() 34 | mockey.Mock((*backend.MockPooledConnect).MoreResultsExist).Return(false).Build() 35 | mockey.Mock((*backend.MockPooledConnect).FetchMoreRows).To(func(result *mysql.Result, maxRows int) error { 36 | // 检测 fields 长度是否正常 37 | assert.Equal(t, 1, len(result.Fields)) 38 | return nil 39 | }).Build() 40 | 41 | rs := mysql.ResultPool.Get() 42 | rs.Resultset = &mysql.Resultset{ 43 | Fields: []*mysql.Field{ 44 | {}, 45 | }, 46 | } 47 | backendConn := &backend.MockPooledConnect{} 48 | c := ClientConn{} 49 | c.writeOKResultStream(0, rs, backendConn, 0, true) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /proxy/server/executor_stmt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func Test_calcParams(t *testing.T) { 22 | sql := "update micf_order_0 set order_status=4, update_time=1541831505" + 23 | "where\n" + 24 | "order_id in ('1321989216361392') and\n" + 25 | "project_id = 371 and\n" + 26 | "order_status = 2" 27 | paramCount, offsets, sqlItems, err := CalcParams(sql) 28 | t.Log(paramCount) 29 | t.Log(offsets) 30 | t.Log(sqlItems) 31 | t.Log(err) 32 | if err != nil { 33 | t.Logf("test calcParams failed, %v\n", err) 34 | } 35 | 36 | sql = "select * from t1 where id = ? and col = ?" 37 | paramCount, offsets, sqlItems, err = CalcParams(sql) 38 | t.Log(paramCount) 39 | t.Log(offsets) 40 | t.Log(sqlItems) 41 | t.Log(err) 42 | if err != nil || paramCount != 2 { 43 | t.Logf("test calcParams failed, %v\n", err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /proxy/server/namespace_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/XiaoMi/Gaea/models" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | type phyDBCase struct { 10 | defaultPhyDBs map[string]string 11 | allowedDBs map[string]bool 12 | shardRules []*models.Shard 13 | realPhyDBs map[string]string 14 | } 15 | 16 | func TestParsePhyDBs(t *testing.T) { 17 | tests := []phyDBCase{ 18 | {defaultPhyDBs: map[string]string{"db_mycat": "db_mycat"}, 19 | allowedDBs: map[string]bool{"db_mycat": true}, 20 | shardRules: []*models.Shard{{Databases: []string{"db_mycat_[0-1]"}}}, 21 | realPhyDBs: map[string]string{"db_mycat": "db_mycat", "db_mycat_0": "db_mycat_0", "db_mycat_1": "db_mycat_1"}}, 22 | {defaultPhyDBs: map[string]string{}, 23 | allowedDBs: map[string]bool{"db_mycat": true}, 24 | shardRules: []*models.Shard{}, 25 | realPhyDBs: map[string]string{"db_mycat": "db_mycat"}}, 26 | } 27 | for index, test := range tests { 28 | t.Run("test", func(t *testing.T) { 29 | realPhyDBs, _ := parseDefaultPhyDB(test.defaultPhyDBs, test.allowedDBs, test.shardRules) 30 | if !reflect.DeepEqual(realPhyDBs, test.realPhyDBs) { 31 | t.Errorf("test %d, parse real phyDBs error, %v", index, realPhyDBs) 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /proxy/server/var.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import "sync/atomic" 18 | 19 | // Process global variables. 20 | var ( 21 | ProcessGeneralLog uint32 22 | ) 23 | 24 | func OpenProcessGeneralQueryLog() bool { 25 | return atomic.LoadUint32(&ProcessGeneralLog) == 1 26 | } 27 | -------------------------------------------------------------------------------- /stats/counter_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Vitess Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | import ( 20 | "expvar" 21 | "testing" 22 | ) 23 | 24 | func TestCounter(t *testing.T) { 25 | var gotname string 26 | var gotv *Counter 27 | clear() 28 | Register(func(name string, v expvar.Var) { 29 | gotname = name 30 | gotv = v.(*Counter) 31 | }) 32 | v := NewCounter("Int", "help") 33 | if gotname != "Int" { 34 | t.Errorf("want Int, got %s", gotname) 35 | } 36 | if gotv != v { 37 | t.Errorf("want %#v, got %#v", v, gotv) 38 | } 39 | v.Add(1) 40 | if v.Get() != 1 { 41 | t.Errorf("want 1, got %v", v.Get()) 42 | } 43 | if v.String() != "1" { 44 | t.Errorf("want 1, got %v", v.Get()) 45 | } 46 | v.Reset() 47 | if v.Get() != 0 { 48 | t.Errorf("want 0, got %v", v.Get()) 49 | } 50 | } 51 | 52 | func TestGaugeFunc(t *testing.T) { 53 | var gotname string 54 | var gotv *GaugeFunc 55 | clear() 56 | Register(func(name string, v expvar.Var) { 57 | gotname = name 58 | gotv = v.(*GaugeFunc) 59 | }) 60 | 61 | v := NewGaugeFunc("name", "help", func() int64 { 62 | return 1 63 | }) 64 | if gotname != "name" { 65 | t.Errorf("want name, got %s", gotname) 66 | } 67 | if gotv != v { 68 | t.Errorf("want %#v, got %#v", v, gotv) 69 | } 70 | if v.String() != "1" { 71 | t.Errorf("want 1, got %v", v.String()) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /stats/kebab_case_converter.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Vitess Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | import ( 20 | "regexp" 21 | "strings" 22 | "sync" 23 | ) 24 | 25 | // toKebabCase produces a monitoring compliant name from the 26 | // original. It converts CamelCase to camel-case, 27 | // and CAMEL_CASE to camel-case. For numbers, it 28 | // converts 0.5 to v0_5. 29 | func toKebabCase(name string) (hyphenated string) { 30 | memoizer.Lock() 31 | defer memoizer.Unlock() 32 | if hyphenated = memoizer.memo[name]; hyphenated != "" { 33 | return hyphenated 34 | } 35 | hyphenated = name 36 | for _, converter := range kebabConverters { 37 | hyphenated = converter.re.ReplaceAllString(hyphenated, converter.repl) 38 | } 39 | hyphenated = strings.ToLower(hyphenated) 40 | memoizer.memo[name] = hyphenated 41 | return 42 | } 43 | 44 | var kebabConverters = []struct { 45 | re *regexp.Regexp 46 | repl string 47 | }{ 48 | // example: LC -> L-C (e.g. CamelCase -> Camel-Case). 49 | {regexp.MustCompile("([a-z])([A-Z])"), "$1-$2"}, 50 | // example: CCa -> C-Ca (e.g. CCamel -> C-Camel). 51 | {regexp.MustCompile("([A-Z])([A-Z][a-z])"), "$1-$2"}, 52 | {regexp.MustCompile("_"), "-"}, 53 | {regexp.MustCompile("\\."), "_"}, 54 | } 55 | 56 | var memoizer = memoizerType{ 57 | memo: make(map[string]string), 58 | } 59 | 60 | type memoizerType struct { 61 | sync.Mutex 62 | memo map[string]string 63 | } 64 | -------------------------------------------------------------------------------- /stats/kebab_case_converter_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Vitess Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | import "testing" 20 | 21 | func TestToKebabCase(t *testing.T) { 22 | var kebabCaseTest = []struct{ input, output string }{ 23 | {"Camel", "camel"}, 24 | {"Camel", "camel"}, 25 | {"CamelCase", "camel-case"}, 26 | {"CamelCaseAgain", "camel-case-again"}, 27 | {"CCamel", "c-camel"}, 28 | {"CCCamel", "cc-camel"}, 29 | {"CAMEL_CASE", "camel-case"}, 30 | {"camel-case", "camel-case"}, 31 | {"0", "0"}, 32 | {"0.0", "0_0"}, 33 | {"JSON", "json"}, 34 | } 35 | 36 | for _, tt := range kebabCaseTest { 37 | if got, want := toKebabCase(tt.input), tt.output; got != want { 38 | t.Errorf("want '%s', got '%s'", want, got) 39 | } 40 | } 41 | } 42 | 43 | func TestMemoize(t *testing.T) { 44 | key := "Test" 45 | if memoizer.memo[key] != "" { 46 | t.Errorf("want '', got '%s'", memoizer.memo[key]) 47 | } 48 | toKebabCase(key) 49 | if memoizer.memo[key] != "test" { 50 | t.Errorf("want 'test', got '%s'", memoizer.memo[key]) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stats/multidimensional.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreedto in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | import ( 20 | "fmt" 21 | "strings" 22 | ) 23 | 24 | // MultiTracker is a CountTracker that tracks counts grouping them by 25 | // more than one dimension. 26 | type MultiTracker interface { 27 | CountTracker 28 | Labels() []string 29 | } 30 | 31 | // CounterForDimension returns a CountTracker for the provided 32 | // dimension. It will panic if the dimension isn't a legal label for 33 | // mt. 34 | func CounterForDimension(mt MultiTracker, dimension string) CountTracker { 35 | for i, lab := range mt.Labels() { 36 | if lab == dimension { 37 | return wrappedCountTracker{ 38 | f: func() map[string]int64 { 39 | result := make(map[string]int64) 40 | for k, v := range mt.Counts() { 41 | if k == "All" { 42 | result[k] = v 43 | continue 44 | } 45 | result[strings.Split(k, ".")[i]] += v 46 | } 47 | return result 48 | }, 49 | } 50 | } 51 | } 52 | 53 | panic(fmt.Sprintf("label %v is not one of %v", dimension, mt.Labels())) 54 | } 55 | -------------------------------------------------------------------------------- /stats/multidimensional_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreedto in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | import ( 20 | "reflect" 21 | "testing" 22 | "time" 23 | ) 24 | 25 | func TestMultiTimingsCounterFor(t *testing.T) { 26 | clear() 27 | mtm := NewMultiTimings("multitimings3", "help", []string{"dim1", "dim2"}) 28 | 29 | mtm.Add([]string{"tag1a", "tag1b"}, 500*time.Microsecond) 30 | mtm.Add([]string{"tag1a", "tag2b"}, 500*time.Millisecond) 31 | mtm.Add([]string{"tag2a", "tag2b"}, 500*time.Millisecond) 32 | 33 | cases := []struct { 34 | dim string 35 | want map[string]int64 36 | }{ 37 | {"dim1", map[string]int64{"tag1a": 2, "tag2a": 1, "All": 3}}, 38 | {"dim2", map[string]int64{"tag1b": 1, "tag2b": 2, "All": 3}}, 39 | } 40 | for _, c := range cases { 41 | counts := CounterForDimension(mtm, c.dim).Counts() 42 | if !reflect.DeepEqual(c.want, counts) { 43 | t.Errorf("mtm.CounterFor(%q).Counts()=%v, want %v", c.dim, counts, c.want) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /stats/ring.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | // RingInt64 Ring of int64 values 20 | // Not thread safe 21 | type RingInt64 struct { 22 | position int 23 | values []int64 24 | } 25 | 26 | // NewRingInt64 create ring 27 | func NewRingInt64(capacity int) *RingInt64 { 28 | return &RingInt64{values: make([]int64, 0, capacity)} 29 | } 30 | 31 | // Add add value into ring 32 | func (ri *RingInt64) Add(val int64) { 33 | if len(ri.values) == cap(ri.values) { 34 | ri.values[ri.position] = val 35 | ri.position = (ri.position + 1) % cap(ri.values) 36 | } else { 37 | ri.values = append(ri.values, val) 38 | } 39 | } 40 | 41 | // Values return values in ring 42 | func (ri *RingInt64) Values() (values []int64) { 43 | values = make([]int64, len(ri.values)) 44 | for i := 0; i < len(ri.values); i++ { 45 | values[i] = ri.values[(ri.position+i)%cap(ri.values)] 46 | } 47 | return values 48 | } 49 | -------------------------------------------------------------------------------- /stats/snake_case_converter_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Vitess Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | import "testing" 20 | 21 | func TestToSnakeCase(t *testing.T) { 22 | var snakeCaseTest = []struct{ input, output string }{ 23 | {"Camel", "camel"}, 24 | {"Camel", "camel"}, 25 | {"CamelCase", "camel_case"}, 26 | {"CamelCaseAgain", "camel_case_again"}, 27 | {"CCamel", "c_camel"}, 28 | {"CCCamel", "cc_camel"}, 29 | {"CAMEL_CASE", "camel_case"}, 30 | {"camel-case", "camel_case"}, 31 | {"0", "0"}, 32 | {"0.0", "0_0"}, 33 | {"JSON", "json"}, 34 | } 35 | 36 | for _, tt := range snakeCaseTest { 37 | if got, want := toSnakeCase(tt.input), tt.output; got != want { 38 | t.Errorf("want '%s', got '%s'", want, got) 39 | } 40 | } 41 | } 42 | 43 | func TestSnakeMemoize(t *testing.T) { 44 | key := "Test" 45 | if snakeMemoizer.memo[key] != "" { 46 | t.Errorf("want '', got '%s'", snakeMemoizer.memo[key]) 47 | } 48 | toSnakeCase(key) 49 | if snakeMemoizer.memo[key] != "test" { 50 | t.Errorf("want 'test', got '%s'", snakeMemoizer.memo[key]) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stats/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | import "strings" 20 | 21 | // safeLabel turns a label into a safe label for stats export. 22 | // It is in its own file so it can be customized. 23 | func safeLabel(label string) string { 24 | return strings.Replace(label, ".", "_", -1) 25 | } 26 | -------------------------------------------------------------------------------- /stats/variable_interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Vitess Authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package stats 18 | 19 | // Variable is the minimal interface which each type in this "stats" package 20 | // must implement. 21 | // When integrating the Vitess stats types ("variables") with the different 22 | // monitoring systems, you can rely on this interface. 23 | type Variable interface { 24 | // Help returns the description of the variable. 25 | Help() string 26 | 27 | // String must implement String() from the expvar.Var interface. 28 | String() string 29 | } 30 | -------------------------------------------------------------------------------- /tests/docker/mysql5/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7 2 | 3 | # Install dependencies 4 | COPY install_dependencies.sh /dist/install_dependencies.sh 5 | RUN /dist/install_dependencies.sh 6 | ENV PATH /usr/local/go/bin:/root/go/bin/:$PATH 7 | -------------------------------------------------------------------------------- /tests/docker/mysql5/my3319.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3319 3 | socket = /data/tmp/mysql3319.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3319 11 | socket = /data/tmp/mysql3319.sock 12 | pid_file = /data/mysql/data3319/mysql3319.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3319 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3319 32 | 33 | server-id = 38166114 34 | sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3319/mysql-bin 37 | relay-log = /data/mysql/data3319/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 1 43 | general_log_file = /data/mysql/data3319/general.log 44 | 45 | log_warnings 46 | log_error = /data/mysql/data3319/log_error.err 47 | max_connections = 4096 48 | max_user_connections = 4096 49 | -------------------------------------------------------------------------------- /tests/docker/mysql5/my3329.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3329 3 | socket = /data/tmp/mysql3329.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3329 11 | socket = /data/tmp/mysql3329.sock 12 | pid_file = /data/mysql/data3329/mysql3329.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3329 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3329 32 | 33 | server-id = 38166124 34 | sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3329/mysql-bin 37 | relay-log = /data/mysql/data3329/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 1 43 | general_log_file = /data/mysql/data3329/general.log 44 | 45 | log_warnings 46 | log_error = /data/mysql/data3329/log_error.err 47 | max_connections = 4096 48 | max_user_connections = 4096 49 | -------------------------------------------------------------------------------- /tests/docker/mysql5/my3339.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3339 3 | socket = /data/tmp/mysql3339.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3339 11 | socket = /data/tmp/mysql3339.sock 12 | pid_file = /data/mysql/data3339/mysql3339.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3339 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3339 32 | 33 | server-id = 38166134 34 | sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3339/mysql-bin 37 | relay-log = /data/mysql/data3339/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 1 43 | general_log_file = /data/mysql/data3339/general.log 44 | 45 | log_warnings 46 | log_error = /data/mysql/data3339/log_error.err 47 | max_connections = 4096 48 | max_user_connections = 4096 49 | -------------------------------------------------------------------------------- /tests/docker/mysql5/my3349.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3349 3 | socket = /data/tmp/mysql3349.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3349 11 | socket = /data/tmp/mysql3349.sock 12 | pid_file = /data/mysql/data3349/mysql3349.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3349 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3349 32 | 33 | server-id = 38166144 34 | sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3349/mysql-bin 37 | relay-log = /data/mysql/data3349/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 1 43 | general_log_file = /data/mysql/data3349/general.log 44 | 45 | log_warnings 46 | log_error = /data/mysql/data3349/log_error.err 47 | max_connections = 4096 48 | max_user_connections = 4096 49 | -------------------------------------------------------------------------------- /tests/docker/mysql5/my3379.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3379 3 | socket = /data/tmp/mysql3379.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3379 11 | socket = /data/tmp/mysql3379.sock 12 | pid_file = /data/mysql/data3379/mysql3379.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3379 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3379 32 | 33 | server-id = 38166104 34 | sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3379/mysql-bin 37 | relay-log = /data/mysql/data3379/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 1 43 | general_log_file = /data/mysql/data3379/general.log 44 | 45 | log_warnings 46 | log_error = /data/mysql/data3379/log_error.err 47 | max_connections = 4096 48 | max_user_connections = 4096 49 | -------------------------------------------------------------------------------- /tests/docker/mysql8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7 2 | 3 | # Install dependencies 4 | COPY install_dependencies_mysql8.sh /dist/install_dependencies_mysql8.sh 5 | RUN /dist/install_dependencies_mysql8.sh 6 | ENV PATH /usr/local/go/bin:/root/go/bin/:$PATH 7 | -------------------------------------------------------------------------------- /tests/docker/mysql8/my3319.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3319 3 | socket = /data/tmp/mysql3319.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3319 11 | socket = /data/tmp/mysql3319.sock 12 | pid_file = /data/mysql/data3319/mysql3319.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3319 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3319 32 | 33 | server-id = 38166114 34 | sql_mode = "" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3319/mysql-bin 37 | relay-log = /data/mysql/data3319/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 0 43 | general_log_file = /data/mysql/data3319/general.log 44 | 45 | log_error = /data/mysql/data3319/log_error.err 46 | max_connections = 4096 47 | max_user_connections = 4096 48 | -------------------------------------------------------------------------------- /tests/docker/mysql8/my3329.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3329 3 | socket = /data/tmp/mysql3329.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3329 11 | socket = /data/tmp/mysql3329.sock 12 | pid_file = /data/mysql/data3329/mysql3329.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3329 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3329 32 | 33 | server-id = 38166124 34 | sql_mode = "" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3329/mysql-bin 37 | relay-log = /data/mysql/data3329/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 0 43 | general_log_file = /data/mysql/data3329/general.log 44 | 45 | log_error = /data/mysql/data3329/log_error.err 46 | max_connections = 4096 47 | max_user_connections = 4096 48 | -------------------------------------------------------------------------------- /tests/docker/mysql8/my3339.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3339 3 | socket = /data/tmp/mysql3339.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3339 11 | socket = /data/tmp/mysql3339.sock 12 | pid_file = /data/mysql/data3339/mysql3339.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3339 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3339 32 | 33 | server-id = 38166134 34 | sql_mode = "" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3339/mysql-bin 37 | relay-log = /data/mysql/data3339/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 0 43 | general_log_file = /data/mysql/data3339/general.log 44 | 45 | log_error = /data/mysql/data3339/log_error.err 46 | max_connections = 4096 47 | max_user_connections = 4096 48 | -------------------------------------------------------------------------------- /tests/docker/mysql8/my3349.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3349 3 | socket = /data/tmp/mysql3349.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3349 11 | socket = /data/tmp/mysql3349.sock 12 | pid_file = /data/mysql/data3349/mysql3349.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3349 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3349 32 | 33 | server-id = 38166144 34 | sql_mode = "" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3349/mysql-bin 37 | relay-log = /data/mysql/data3349/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 0 43 | general_log_file = /data/mysql/data3349/general.log 44 | 45 | log_error = /data/mysql/data3349/log_error.err 46 | max_connections = 4096 47 | max_user_connections = 4096 48 | -------------------------------------------------------------------------------- /tests/docker/mysql8/my3379.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3379 3 | socket = /data/tmp/mysql3379.sock 4 | 5 | [mysqld] 6 | default_password_lifetime = 0 7 | lower_case_table_names = 1 8 | character-set-server = utf8 9 | tmpdir = /data/tmp 10 | port = 3379 11 | socket = /data/tmp/mysql3379.sock 12 | pid_file = /data/mysql/data3379/mysql3379.pid 13 | 14 | #gtid 15 | gtid-mode = ON 16 | enforce-gtid-consistency = ON 17 | 18 | relay_log_info_repository = TABLE 19 | relay_log_purge = 1 20 | relay_log_recovery = 1 21 | 22 | datadir = /data/mysql/data3379 23 | #memlock 24 | 25 | #innodb_file_per_table 26 | #innodb_buffer_pool_size = 100M 27 | #innodb_change_buffering = all 28 | #innodb_data_file_path = ibdata1:12M:autoextend 29 | #innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:100G 30 | 31 | innodb_log_group_home_dir = /data/mysql/data3379 32 | 33 | server-id = 38166104 34 | sql_mode = "" 35 | log-slave-updates 36 | log-bin = /data/mysql/data3379/mysql-bin 37 | relay-log = /data/mysql/data3379/mysql-relay-bin 38 | binlog_format = row 39 | log_bin_trust_function_creators = 1 40 | skip_slave_start 41 | 42 | general-log = 0 43 | general_log_file = /data/mysql/data3379/general.log 44 | 45 | log_error = /data/mysql/data3379/log_error.err 46 | max_connections = 4096 47 | max_user_connections = 4096 48 | -------------------------------------------------------------------------------- /tests/e2e/cmd/gaea.ini: -------------------------------------------------------------------------------- 1 | ; config type, etcd/file/etcdv3, you can test gaea with file type, you shoud use etcd/etcdv3 in production 2 | ; 请指定设定方式为 file 或 etcd 或 etcdv3 3 | ;config_type=file 4 | config_type=etcdv3 5 | ;file config path, 具体配置放到file_config_path的namespace目录下,该下级目录为固定目录 6 | ;file_config_path=./etc/file 7 | ;file_config_path=/etc/ 8 | 9 | ;coordinator addr 10 | coordinator_addr=http://127.0.0.1:2379 11 | ;etcd user config 12 | username=root 13 | password=root 14 | 15 | ;environ 16 | environ=local 17 | ;service name 18 | service_name=gaea_proxy 19 | ;gaea_proxy cluster name 20 | cluster_name=gaea_default_cluster 21 | 22 | ;log config 23 | log_path=./logs 24 | log_level=Notice 25 | log_filename=gaea 26 | log_output=file 27 | ; 日志保留天数 28 | log_keep_days=1 29 | ; 日志保留数量 30 | log_keep_counts=3 31 | 32 | ;admin addr 33 | admin_addr=0.0.0.0:13307 34 | ; basic auth 35 | admin_user=test 36 | admin_password=test 37 | 38 | ;proxy addr 39 | proto_type=tcp4 40 | proxy_addr=0.0.0.0:13306 41 | proxy_charset=utf8 42 | ;slow sql time, when execute time is higher than this, log it, unit: ms 43 | slow_sql_time=100 44 | ;close session after session timeout, unit: seconds 45 | session_timeout=3600 46 | 47 | ;stats conf 48 | stats_enabled=true 49 | ;stats interval 50 | stats_interval=10 51 | 52 | ;encrypt key 53 | encrypt_key=1234abcd5678efg* 54 | 55 | ;server_version 56 | server_version=5.6.20-gaea 57 | 58 | ;auth plugin mysql_native_password or caching_sha2_password or '' 59 | auth_plugin=mysql_native_password 60 | -------------------------------------------------------------------------------- /tests/e2e/cmd/gaea_cc.ini: -------------------------------------------------------------------------------- 1 | addr=0.0.0.0:23306 2 | ; basic auth of gaea-cc 3 | admin_username=admin 4 | admin_password=admin 5 | 6 | ; basic auth of gaea-proxy's admin service 7 | proxy_username=test 8 | proxy_password=test 9 | 10 | ;Debug, Trace, Notice, Warn, Fatal, 建议测试采用debug级别,上线采用Notice级别 11 | log_level=Notice 12 | log_path =./logs 13 | log_filename=gaea_cc 14 | log_output=file 15 | log_keep_days=3 16 | 17 | ;coordinator 目前支持 etcd 和 etcdv3,coodinator config 18 | coordinator_type=etcdv3 19 | coordinator_addr=http://127.0.0.1:2379 20 | username=root 21 | password=root 22 | 23 | ;指定一个的默认gaea集群名称 24 | default_cluster=gaea_default_cluster 25 | 26 | ;encrypt key 27 | encrypt_key=1234abcd5678efg* 28 | -------------------------------------------------------------------------------- /tests/e2e/dml/case/sql.json: -------------------------------------------------------------------------------- 1 | { 2 | "execCases": [ 3 | { 4 | "description": "Test case 1 description", 5 | "setUp": [ 6 | { 7 | "slice": "slice-0", 8 | "sql": "DROP DATABASE IF EXISTS db_test" 9 | }, 10 | { 11 | "slice": "slice-0", 12 | "sql": "CREATE DATABASE db_test" 13 | }, 14 | { 15 | "slice": "slice-0", 16 | "sql": "USE db_test" 17 | }, 18 | { 19 | "slice": "slice-0", 20 | "sql": "CREATE TABLE tbl_users (col1 INT AUTO_INCREMENT, col2 VARCHAR(20), PRIMARY KEY (col1))" 21 | } 22 | ], 23 | "gaeaActions": [ 24 | { 25 | "sql":"USE db_test", 26 | "execType": "Default" 27 | }, 28 | { 29 | "sql": "INSERT INTO tbl_users (col2) VALUES ('test')", 30 | "execType": "Default" 31 | }, 32 | { 33 | "sql": "UPDATE tbl_users SET col2='updated' WHERE col1=1", 34 | "execType": "Default" 35 | } 36 | ], 37 | "masterCheckSQL": [ 38 | { 39 | "slice": "slice-0", 40 | "db": "db_test", 41 | "name": "Check master after insert and update", 42 | "sql": "SELECT col2 FROM tbl_users WHERE col1=1", 43 | "execType": "Query", 44 | "expect": [ 45 | ["updated"] 46 | ] 47 | } 48 | ], 49 | "tearDown": [ 50 | { 51 | "slice": "slice-0", 52 | "sql": "DROP DATABASE IF EXISTS db_test" 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | 59 | -------------------------------------------------------------------------------- /tests/e2e/e2e_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/XiaoMi/Gaea/tests/e2e/util" 22 | 23 | _ "github.com/XiaoMi/Gaea/tests/e2e/dml" 24 | _ "github.com/XiaoMi/Gaea/tests/e2e/function" 25 | _ "github.com/XiaoMi/Gaea/tests/e2e/shard" 26 | _ "github.com/XiaoMi/Gaea/tests/e2e/unshard" 27 | "github.com/onsi/ginkgo/v2" 28 | "github.com/onsi/gomega" 29 | ) 30 | 31 | func TestE2E(t *testing.T) { 32 | if testing.Short() { 33 | t.Skip("skipping e2e test in short mode.") 34 | } 35 | gomega.RegisterFailHandler(ginkgo.Fail) 36 | ginkgo.RunSpecs(t, "gaea E2E Testing") 37 | } 38 | 39 | var _ = ginkgo.BeforeSuite(func() { 40 | ginkgo.By("start gaea default.") 41 | err := util.StartGaeaDefault() 42 | util.ExpectNoError(err) 43 | time.Sleep(5 * time.Second) 44 | ginkgo.By("start gaea default success.") 45 | 46 | ginkgo.By("start gaea-cc default.") 47 | err = util.StartGaeaCCDefault() 48 | util.ExpectNoError(err) 49 | time.Sleep(5 * time.Second) 50 | ginkgo.By("start gaea-cc default success.") 51 | }) 52 | 53 | var _ = ginkgo.AfterSuite(func() { 54 | ginkgo.By("stop gaea-cc default.") 55 | err := util.StopGaeaCCDefault() 56 | util.ExpectNoError(err) 57 | ginkgo.By("stop gaea-cc default success.") 58 | 59 | ginkgo.By("stop gaea default.") 60 | err = util.StopGaeaDefault() 61 | util.ExpectNoError(err) 62 | ginkgo.By("stop gaea default success.") 63 | 64 | }) 65 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/kingshard/show.sql: -------------------------------------------------------------------------------- 1 | SELECT last_insert_id(); 2 | 3 | # databases 4 | show databases like 'sbtest'; 5 | 6 | # schemas 7 | show schemas; 8 | show open tables; 9 | show open tables from sbtest; 10 | show open tables in sbtest; 11 | show open tables from sbtest like 'aly_o%'; 12 | 13 | # tables 14 | show table status like 'aly_o%'/*allow_diff*/; 15 | show table status/*allow_diff*/; 16 | show tables; 17 | show full tables; 18 | show tables like 'aly_o%'; 19 | show tables from sbtest; 20 | show tables in sbtest; 21 | show full tables from sbtest; 22 | show full tables in sbtest; 23 | show full tables from sbtest like 'aly%'; 24 | show full tables in sbtest where table_type like 'base%'; 25 | 26 | # create 27 | show create database sbtest; 28 | show create schema sbtest; 29 | show create schema if not exists sbtest; 30 | show create database if not exists sbtest; 31 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/kingshard/skip.sql: -------------------------------------------------------------------------------- 1 | SELECT last_insert_id(); 2 | 3 | show databases; 4 | show table status/*allow_diff*/; 5 | 6 | show index from t1; 7 | show index in t1; 8 | show keys from t1; 9 | show keys in t1; 10 | 11 | 12 | show columns from t1 from sbtest; 13 | show full columns from t1 from sbtest; 14 | show full columns from t1 from sbtest like 'n%'; 15 | show full columns from t1 from sbtest where field like 's%'; 16 | 17 | 18 | show index from t1 from sbtest; 19 | show index in t1 in sbtest; 20 | show index in t1 from sbtest; 21 | show index from t1 in sbtest; 22 | show keys from t1 from sbtest; 23 | show keys from t1 in sbtest; 24 | show keys in t1 in sbtest; 25 | show keys in t1 from sbtest; 26 | 27 | 28 | show index from t1; 29 | show index in t1; 30 | show keys from t1; 31 | show keys in t1; 32 | show databases; 33 | 34 | show columns from t1 from sbtest; 35 | show full columns from t1 from sbtest; 36 | show full columns from t1 from sbtest like 'n%'; 37 | show full columns from t1 from sbtest where field like 's%'; 38 | show index from t1 from sbtest; 39 | show index in t1 in sbtest; 40 | show index in t1 from sbtest; 41 | show index from t1 in sbtest; 42 | show keys from t1 from sbtest; 43 | show keys from t1 in sbtest; 44 | show keys in t1 in sbtest; 45 | show keys in t1 from sbtest; 46 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/long/show.sql: -------------------------------------------------------------------------------- 1 | # databases 2 | show databases like 'sbtest'; 3 | 4 | # schemas 5 | show schemas; 6 | show open tables; 7 | show open tables from sbtest; 8 | show open tables in sbtest; 9 | show open tables from sbtest like 'aly_o%'; 10 | 11 | # tables 12 | show table status like 'aly_o%'/*allow_diff*/; 13 | show table status/*allow_diff*/; 14 | show tables; 15 | show full tables; 16 | show tables like 'aly_o%'; 17 | show tables from sbtest_0; 18 | show tables in sbtest_0; 19 | show full tables from sbtest_0; 20 | show full tables in sbtest_0; 21 | show full tables from sbtest_0 like 'aly%'; 22 | show full tables in sbtest_0 where table_type like 'base%'; 23 | 24 | 25 | # columns 26 | show columns in t1; 27 | show columns from t1; 28 | show full columns from t1; 29 | show columns from t1 from sbtest_0; 30 | show full columns from t1 from sbtest_0; 31 | show full columns from t1 from sbtest_0 like 'n%'; 32 | show full columns from t1 from sbtest_0 where field like 's%'; 33 | 34 | # index 35 | show index from t1; 36 | show index in t1; 37 | show index from t1 from sbtest_0; 38 | show index in t1 in sbtest_0; 39 | show index in t1 from sbtest_0; 40 | show index from t1 in sbtest_0; 41 | 42 | # keys 43 | show keys from t1; 44 | show keys in t1; 45 | show keys from t1 from sbtest_0; 46 | show keys from t1 in sbtest_0; 47 | show keys in t1 in sbtest_0; 48 | show keys in t1 from sbtest_0; 49 | 50 | # create 51 | show create database sbtest_0; 52 | show create schema sbtest_0; 53 | show create schema if not exists sbtest_0; 54 | show create database if not exists sbtest_0; 55 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/long/skip.sql: -------------------------------------------------------------------------------- 1 | SELECT last_insert_id(); 2 | 3 | # databases 4 | show databases; 5 | show databases like 'sbtest'; 6 | 7 | # schemas 8 | show schemas; 9 | show open tables; 10 | show open tables from sbtest; 11 | show open tables in sbtest; 12 | show open tables from sbtest like 'aly_o%'; 13 | 14 | # tables 15 | show table status like 'aly_o%'/*allow_diff*/; 16 | show table status/*allow_diff*/; 17 | show tables; 18 | show full tables; 19 | show tables like 'aly_o%'; 20 | show tables from sbtest; 21 | show tables in sbtest; 22 | show full tables from sbtest; 23 | show full tables in sbtest; 24 | show full tables from sbtest like 'aly%'; 25 | show full tables in sbtest where table_type like 'base%'; 26 | 27 | 28 | # columns 29 | show columns in t1; 30 | show columns from t1; 31 | show full columns from t1; 32 | show columns from t1 from sbtest; 33 | show full columns from t1 from sbtest; 34 | show full columns from t1 from sbtest like 'n%'; 35 | show full columns from t1 from sbtest where field like 's%'; 36 | 37 | # index 38 | show index from t1; 39 | show index in t1; 40 | show index from t1 from sbtest; 41 | show index in t1 in sbtest; 42 | show index in t1 from sbtest; 43 | show index from t1 in sbtest; 44 | 45 | # keys 46 | show keys from t1; 47 | show keys in t1; 48 | show keys from t1 from sbtest; 49 | show keys from t1 in sbtest; 50 | show keys in t1 in sbtest; 51 | show keys in t1 from sbtest; 52 | 53 | # create 54 | show create database sbtest; 55 | show create schema sbtest; 56 | show create schema if not exists sbtest; 57 | show create database if not exists sbtest; 58 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/long/unsupport.sql: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/mod/show.sql: -------------------------------------------------------------------------------- 1 | # databases 2 | show databases like 'sbtest'; 3 | 4 | # schemas 5 | show schemas; 6 | show open tables; 7 | show open tables from sbtest; 8 | show open tables in sbtest; 9 | show open tables from sbtest like 'aly_o%'; 10 | 11 | # tables 12 | show table status like 'aly_o%'/*allow_diff*/; 13 | show table status/*allow_diff*/; 14 | show tables; 15 | show full tables; 16 | show tables like 'aly_o%'; 17 | show tables from sbtest_0; 18 | show tables in sbtest_0; 19 | show full tables from sbtest_0; 20 | show full tables in sbtest_0; 21 | show full tables from sbtest_0 like 'aly%'; 22 | show full tables in sbtest_0 where table_type like 'base%'; 23 | 24 | 25 | # columns 26 | show columns in t1; 27 | show columns from t1; 28 | show full columns from t1; 29 | show columns from t1 from sbtest_0; 30 | show full columns from t1 from sbtest_0; 31 | show full columns from t1 from sbtest_0 like 'n%'; 32 | show full columns from t1 from sbtest_0 where field like 's%'; 33 | 34 | # index 35 | show index from t1; 36 | show index in t1; 37 | show index from t1 from sbtest_0; 38 | show index in t1 in sbtest_0; 39 | show index in t1 from sbtest_0; 40 | show index from t1 in sbtest_0; 41 | 42 | # keys 43 | show keys from t1; 44 | show keys in t1; 45 | show keys from t1 from sbtest_0; 46 | show keys from t1 in sbtest_0; 47 | show keys in t1 in sbtest_0; 48 | show keys in t1 from sbtest_0; 49 | 50 | # create 51 | show create database sbtest_0; 52 | show create schema sbtest_0; 53 | show create schema if not exists sbtest_0; 54 | show create database if not exists sbtest_0; 55 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/mod/skip.sql: -------------------------------------------------------------------------------- 1 | SELECT last_insert_id(); 2 | 3 | # databases 4 | show databases; 5 | show databases like 'sbtest'; 6 | 7 | # schemas 8 | show schemas; 9 | show open tables; 10 | show open tables from sbtest; 11 | show open tables in sbtest; 12 | show open tables from sbtest like 'aly_o%'; 13 | 14 | # tables 15 | show table status like 'aly_o%'/*allow_diff*/; 16 | show table status/*allow_diff*/; 17 | show tables; 18 | show full tables; 19 | show tables like 'aly_o%'; 20 | show tables from sbtest; 21 | show tables in sbtest; 22 | show full tables from sbtest; 23 | show full tables in sbtest; 24 | show full tables from sbtest like 'aly%'; 25 | show full tables in sbtest where table_type like 'base%'; 26 | 27 | 28 | # columns 29 | show columns in t1; 30 | show columns from t1; 31 | show full columns from t1; 32 | show columns from t1 from sbtest; 33 | show full columns from t1 from sbtest; 34 | show full columns from t1 from sbtest like 'n%'; 35 | show full columns from t1 from sbtest where field like 's%'; 36 | 37 | # index 38 | show index from t1; 39 | show index in t1; 40 | show index from t1 from sbtest; 41 | show index in t1 in sbtest; 42 | show index in t1 from sbtest; 43 | show index from t1 in sbtest; 44 | 45 | # keys 46 | show keys from t1; 47 | show keys in t1; 48 | show keys from t1 from sbtest; 49 | show keys from t1 in sbtest; 50 | show keys in t1 in sbtest; 51 | show keys in t1 from sbtest; 52 | 53 | # create 54 | show create database sbtest; 55 | show create schema sbtest; 56 | show create schema if not exists sbtest; 57 | show create database if not exists sbtest; 58 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/mod/unsupport.sql: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/murmur/show.sql: -------------------------------------------------------------------------------- 1 | # databases 2 | show databases like 'sbtest'; 3 | 4 | # schemas 5 | show schemas; 6 | show open tables; 7 | show open tables from sbtest; 8 | show open tables in sbtest; 9 | show open tables from sbtest like 'aly_o%'; 10 | 11 | # tables 12 | show table status like 'aly_o%'/*allow_diff*/; 13 | show table status/*allow_diff*/; 14 | show tables; 15 | show full tables; 16 | show tables like 'aly_o%'; 17 | show tables from sbtest_0; 18 | show tables in sbtest_0; 19 | show full tables from sbtest_0; 20 | show full tables in sbtest_0; 21 | show full tables from sbtest_0 like 'aly%'; 22 | show full tables in sbtest_0 where table_type like 'base%'; 23 | 24 | 25 | # columns 26 | show columns in t1; 27 | show columns from t1; 28 | show full columns from t1; 29 | show columns from t1 from sbtest_0; 30 | show full columns from t1 from sbtest_0; 31 | show full columns from t1 from sbtest_0 like 'n%'; 32 | show full columns from t1 from sbtest_0 where field like 's%'; 33 | 34 | # index 35 | show index from t1; 36 | show index in t1; 37 | show index from t1 from sbtest_0; 38 | show index in t1 in sbtest_0; 39 | show index in t1 from sbtest_0; 40 | show index from t1 in sbtest_0; 41 | 42 | # keys 43 | show keys from t1; 44 | show keys in t1; 45 | show keys from t1 from sbtest_0; 46 | show keys from t1 in sbtest_0; 47 | show keys in t1 in sbtest_0; 48 | show keys in t1 from sbtest_0; 49 | 50 | # create 51 | show create database sbtest_0; 52 | show create schema sbtest_0; 53 | show create schema if not exists sbtest_0; 54 | show create database if not exists sbtest_0; 55 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/murmur/skip.sql: -------------------------------------------------------------------------------- 1 | SELECT last_insert_id(); 2 | 3 | # databases 4 | show databases; 5 | show databases like 'sbtest'; 6 | 7 | # schemas 8 | show schemas; 9 | show open tables; 10 | show open tables from sbtest; 11 | show open tables in sbtest; 12 | show open tables from sbtest like 'aly_o%'; 13 | 14 | # tables 15 | show table status like 'aly_o%'/*allow_diff*/; 16 | show table status/*allow_diff*/; 17 | show tables; 18 | show full tables; 19 | show tables like 'aly_o%'; 20 | show tables from sbtest; 21 | show tables in sbtest; 22 | show full tables from sbtest; 23 | show full tables in sbtest; 24 | show full tables from sbtest like 'aly%'; 25 | show full tables in sbtest where table_type like 'base%'; 26 | 27 | 28 | # columns 29 | show columns in t1; 30 | show columns from t1; 31 | show full columns from t1; 32 | show columns from t1 from sbtest; 33 | show full columns from t1 from sbtest; 34 | show full columns from t1 from sbtest like 'n%'; 35 | show full columns from t1 from sbtest where field like 's%'; 36 | 37 | # index 38 | show index from t1; 39 | show index in t1; 40 | show index from t1 from sbtest; 41 | show index in t1 in sbtest; 42 | show index in t1 from sbtest; 43 | show index from t1 in sbtest; 44 | 45 | # keys 46 | show keys from t1; 47 | show keys in t1; 48 | show keys from t1 from sbtest; 49 | show keys from t1 in sbtest; 50 | show keys in t1 in sbtest; 51 | show keys in t1 from sbtest; 52 | 53 | # create 54 | show create database sbtest; 55 | show create schema sbtest; 56 | show create schema if not exists sbtest; 57 | show create database if not exists sbtest; 58 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/murmur/unsupport.sql: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/string/show.sql: -------------------------------------------------------------------------------- 1 | # databases 2 | show databases like 'sbtest'; 3 | 4 | # schemas 5 | show schemas; 6 | show open tables; 7 | show open tables from sbtest; 8 | show open tables in sbtest; 9 | show open tables from sbtest like 'aly_o%'; 10 | 11 | # tables 12 | show table status like 'aly_o%'/*allow_diff*/; 13 | show table status/*allow_diff*/; 14 | show tables; 15 | show full tables; 16 | show tables like 'aly_o%'; 17 | show tables from sbtest_0; 18 | show tables in sbtest_0; 19 | show full tables from sbtest_0; 20 | show full tables in sbtest_0; 21 | show full tables from sbtest_0 like 'aly%'; 22 | show full tables in sbtest_0 where table_type like 'base%'; 23 | 24 | 25 | # columns 26 | show columns in t1; 27 | show columns from t1; 28 | show full columns from t1; 29 | show columns from t1 from sbtest_0; 30 | show full columns from t1 from sbtest_0; 31 | show full columns from t1 from sbtest_0 like 'n%'; 32 | show full columns from t1 from sbtest_0 where field like 's%'; 33 | 34 | # index 35 | show index from t1; 36 | show index in t1; 37 | show index from t1 from sbtest_0; 38 | show index in t1 in sbtest_0; 39 | show index in t1 from sbtest_0; 40 | show index from t1 in sbtest_0; 41 | 42 | # keys 43 | show keys from t1; 44 | show keys in t1; 45 | show keys from t1 from sbtest_0; 46 | show keys from t1 in sbtest_0; 47 | show keys in t1 in sbtest_0; 48 | show keys in t1 from sbtest_0; 49 | 50 | # create 51 | show create database sbtest_0; 52 | show create schema sbtest_0; 53 | show create schema if not exists sbtest_0; 54 | show create database if not exists sbtest_0; 55 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/string/skip.sql: -------------------------------------------------------------------------------- 1 | SELECT last_insert_id(); 2 | 3 | # databases 4 | show databases; 5 | show databases like 'sbtest'; 6 | 7 | # schemas 8 | show schemas; 9 | show open tables; 10 | show open tables from sbtest; 11 | show open tables in sbtest; 12 | show open tables from sbtest like 'aly_o%'; 13 | 14 | # tables 15 | show table status like 'aly_o%'/*allow_diff*/; 16 | show table status/*allow_diff*/; 17 | show tables; 18 | show full tables; 19 | show tables like 'aly_o%'; 20 | show tables from sbtest; 21 | show tables in sbtest; 22 | show full tables from sbtest; 23 | show full tables in sbtest; 24 | show full tables from sbtest like 'aly%'; 25 | show full tables in sbtest where table_type like 'base%'; 26 | 27 | 28 | # columns 29 | show columns in t1; 30 | show columns from t1; 31 | show full columns from t1; 32 | show columns from t1 from sbtest; 33 | show full columns from t1 from sbtest; 34 | show full columns from t1 from sbtest like 'n%'; 35 | show full columns from t1 from sbtest where field like 's%'; 36 | 37 | # index 38 | show index from t1; 39 | show index in t1; 40 | show index from t1 from sbtest; 41 | show index in t1 in sbtest; 42 | show index in t1 from sbtest; 43 | show index from t1 in sbtest; 44 | 45 | # keys 46 | show keys from t1; 47 | show keys in t1; 48 | show keys from t1 from sbtest; 49 | show keys from t1 in sbtest; 50 | show keys in t1 in sbtest; 51 | show keys in t1 from sbtest; 52 | 53 | # create 54 | show create database sbtest; 55 | show create schema sbtest; 56 | show create schema if not exists sbtest; 57 | show create database if not exists sbtest; 58 | -------------------------------------------------------------------------------- /tests/e2e/shard/case/join/mycat/string/unsupport.sql: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/e2e/unshard/case/dml/sql.json: -------------------------------------------------------------------------------- 1 | { 2 | "execCases": [ 3 | { 4 | "description": "Test case 1 description", 5 | "setUp": [ 6 | { 7 | "slice": "slice-0", 8 | "sql": "DROP DATABASE IF EXISTS db_test" 9 | }, 10 | { 11 | "slice": "slice-0", 12 | "sql": "CREATE DATABASE db_test" 13 | }, 14 | { 15 | "slice": "slice-0", 16 | "sql": "USE db_test" 17 | }, 18 | { 19 | "slice": "slice-0", 20 | "sql": "CREATE TABLE tbl_users (col1 INT AUTO_INCREMENT, col2 VARCHAR(20), PRIMARY KEY (col1))" 21 | } 22 | ], 23 | "gaeaActions": [ 24 | { 25 | "sql":"USE db_test", 26 | "execType": "Default" 27 | }, 28 | { 29 | "sql": "INSERT INTO tbl_users (col2) VALUES ('test')", 30 | "execType": "Default" 31 | }, 32 | { 33 | "sql": "UPDATE tbl_users SET col2='updated' WHERE col1=1", 34 | "execType": "Default" 35 | } 36 | ], 37 | "masterCheckSQL": [ 38 | { 39 | "slice": "slice-0", 40 | "db": "db_test", 41 | "name": "Check master after insert and update", 42 | "sql": "SELECT col2 FROM tbl_users WHERE col1=1", 43 | "execType": "Query", 44 | "expect": [ 45 | ["updated"] 46 | ] 47 | } 48 | ], 49 | "tearDown": [ 50 | { 51 | "slice": "slice-0", 52 | "sql": "DROP DATABASE IF EXISTS db_test" 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | 59 | -------------------------------------------------------------------------------- /tests/e2e/unshard/case/join/1-clean.sql: -------------------------------------------------------------------------------- 1 | //Attention, You may need to name the clean sql files with '1-clean.sql' and thus ensure clean files will 2 | //be executed at end 3 | drop table t,t1,t2,t3; 4 | 5 | # for test_join.sql 6 | drop table if exists test1,test2,test3; 7 | 8 | # test_reference_no_sharding.sql 9 | drop table if exists noshard_t1,noshard_t2,noshard_t3; 10 | 11 | # 12 | drop table if exists test1,test2,test3; 13 | 14 | # 15 | drop table if exists test4,test5,test6,test7,est8,test9; 16 | -------------------------------------------------------------------------------- /tests/e2e/unshard/case/join/skip.sql: -------------------------------------------------------------------------------- 1 | # un equal 2 | SHOW STATUS LIKE 'Up%'; 3 | SHOW GLOBAL VARIABLES; 4 | SHOW VARIABLES; 5 | SHOW STATUS; 6 | SHOW GLOBAL STATUS; 7 | SHOW SESSION STATUS; 8 | SELECT CONNECTION_ID(); 9 | SELECT ROW_COUNT(); 10 | SELECT GET_LOCK('lock1',10); 11 | SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 where t1.col1 = t2.col1; 12 | (select a.id,a.t_id,a.name,a.pad from test1 a where a.pad=1) union (select c.id,c.o_id,c.name,c.pad from sbtest1.test2 c where c.pad=1) order by id limit 10/*allow_diff_sequence*/ 13 | 14 | # no privileges 15 | SHOW GRANTS; 16 | SHOW GRANTS FOR current_user(); 17 | SHOW GRANTS FOR current_user; 18 | SHOW EVENTS FROM t WHERE definer = 'current_user'; 19 | 20 | # not equal 21 | show full columns in t; 22 | SELECT USER(); 23 | SELECT CURRENT_USER(); 24 | SELECT CURRENT_USER; 25 | SELECT SESSION_USER(); 26 | SELECT SYSTEM_USER(); 27 | SHOW STATUS WHERE Variable_name LIKE 'Up%'; 28 | -------------------------------------------------------------------------------- /tests/e2e/unshard/case/join/test_show.sql: -------------------------------------------------------------------------------- 1 | show columns in t1; 2 | show columns from t1; 3 | show full columns from t1; 4 | show columns from t1 from sbtest1/*not_support*/; 5 | -- show columns from t1 from sbtest1_0; 6 | show full columns from t1 from sbtest1; 7 | show full columns from t1 from sbtest1 like 'n%'; 8 | show full columns from t1 from sbtest1 where field like 's%'; 9 | show table status/*allow_diff*/; 10 | show table status like 'aly_o%'/*allow_diff*/; 11 | show tables; 12 | show full tables; 13 | show tables from sbtest1; 14 | show tables in sbtest1; 15 | show full tables from sbtest1; 16 | show full tables in sbtest1; 17 | show tables like 'aly_o%'; 18 | show full tables from sbtest1 like 'aly%'; 19 | show full tables in sbtest1 where table_type like 'base%'; 20 | #create INDEX index_001 ON t1 (ID); 21 | show index from t1; 22 | show index in t1; 23 | show index from t1 from sbtest1; 24 | show index in t1 in sbtest1; 25 | show index in t1 from sbtest1; 26 | show index from t1 in sbtest1; 27 | show keys from t1; 28 | show keys in t1; 29 | show keys from t1 from sbtest1; 30 | show keys from t1 in sbtest1; 31 | show keys in t1 in sbtest1; 32 | show keys in t1 from sbtest1; 33 | #drop index index_001 on t1; 34 | create database if not exists sbtest1; 35 | show create database sbtest1; 36 | show create schema sbtest1; 37 | show create schema if not exists sbtest1; 38 | show create database if not exists sbtest1; 39 | show databases; 40 | show schemas; 41 | show databases like 'sbtest1'; 42 | show schemas like 'sbtest1'; 43 | show open tables; 44 | show open tables from sbtest1; 45 | show open tables in sbtest1; 46 | show open tables from sbtest1 like 'aly_o%'; 47 | -------------------------------------------------------------------------------- /tests/e2e/util/action.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "os/exec" 19 | ) 20 | 21 | // StartGaeaDefault gaea的一些操作 22 | func StartGaeaDefault() error { 23 | cmd := exec.Command("sh", "-c", CmdStartGaea) 24 | _, err := cmd.CombinedOutput() 25 | return err 26 | } 27 | 28 | // StartGaeaCCDefault gaeacc的一些操作 29 | func StartGaeaCCDefault() error { 30 | cmd := exec.Command("sh", "-c", CmdStartGaeaCC) 31 | _, err := cmd.CombinedOutput() 32 | return err 33 | } 34 | 35 | // StopGaeaDefault gaea的一些操作 36 | func StopGaeaDefault() error { 37 | cmd := exec.Command("sh", "-c", CmdStopGaea) 38 | _, err := cmd.CombinedOutput() 39 | return err 40 | } 41 | 42 | // StopGaeaCCDefault gaeacc的一些操作 43 | func StopGaeaCCDefault() error { 44 | cmd := exec.Command("sh", "-c", CmdStopGaeaCC) 45 | _, err := cmd.CombinedOutput() 46 | return err 47 | } 48 | -------------------------------------------------------------------------------- /tests/e2e/util/constant.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | var ( 18 | // gaea 相关命令 19 | CmdStartGaea = "cd ./cmd ; ./gaea --config=gaea.ini >/dev/null 2>&1 &" 20 | CmdStopGaea = "pkill gaea" 21 | CmdStartGaeaCC = "cd ./cmd ; ./gaea-cc -c=gaea_cc.ini >/dev/null 2>&1 &" 22 | CmdStopGaeaCC = "pkill gaea-cc" 23 | ) 24 | 25 | type ResultType int 26 | 27 | const ( 28 | Equal = iota 29 | UnEqual 30 | UnSupport 31 | ) 32 | -------------------------------------------------------------------------------- /util/array.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | func ArrayFindIndex(items []string, findItem string) int { 18 | for index, item := range items { 19 | if item == findItem { 20 | return index 21 | } 22 | } 23 | return -1 24 | } 25 | 26 | func ArrayRemoveItem(items []string, findItem string) []string { 27 | newArray := make([]string, 0) 28 | for _, item := range items { 29 | if item == findItem { 30 | continue 31 | } 32 | newArray = append(newArray, item) 33 | } 34 | return newArray 35 | } 36 | -------------------------------------------------------------------------------- /util/array_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | -------------------------------------------------------------------------------- /util/cache/perf_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cache 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | type MyValue []byte 24 | 25 | func (mv MyValue) Size() int { 26 | return cap(mv) 27 | } 28 | 29 | func BenchmarkGet(b *testing.B) { 30 | cache := NewLRUCache(64 * 1024 * 1024) 31 | value := make(MyValue, 1000) 32 | cache.Set("stuff", value) 33 | for i := 0; i < b.N; i++ { 34 | val, ok := cache.Get("stuff") 35 | if !ok { 36 | panic("error") 37 | } 38 | _ = val 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /util/crypto/xaes_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crypto 16 | 17 | import ( 18 | "encoding/base64" 19 | "testing" 20 | ) 21 | 22 | func TestEncryptECB(t *testing.T) { 23 | 24 | key := "1234abcd5678efg*" 25 | msg := "mQSa0mS1Gi1Q8VCVLHrbU_izaHqzaPmh" 26 | data, err := EncryptECB(key, []byte(msg)) 27 | if err != nil { 28 | t.Fatalf("encrypt failed, err:%v", err) 29 | return 30 | } 31 | base64Str := base64.StdEncoding.EncodeToString(data) 32 | 33 | t.Logf("encrypt succ, data: %v, data str: %s, len:%v", data, base64Str, len(data)) 34 | 35 | data, _ = base64.StdEncoding.DecodeString(base64Str) 36 | origin, err := DecryptECB(key, data) 37 | if err != nil { 38 | t.Fatalf("decrypt failed, err:%v", err) 39 | return 40 | } 41 | 42 | if string(origin) != msg { 43 | t.Fatalf("origin not equal msg") 44 | } 45 | t.Log("decrypt succ") 46 | } 47 | -------------------------------------------------------------------------------- /util/hack/hack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The kingshard Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package hack 16 | 17 | import ( 18 | "reflect" 19 | "strconv" 20 | "strings" 21 | "unsafe" 22 | ) 23 | 24 | // String no copy to change slice to string 25 | // use your own risk 26 | func String(b []byte) (s string) { 27 | pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 28 | pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) 29 | pstring.Data = pbytes.Data 30 | pstring.Len = pbytes.Len 31 | return 32 | } 33 | 34 | // Slice no copy to change string to slice 35 | // use your own risk 36 | func Slice(s string) (b []byte) { 37 | pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 38 | pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) 39 | pbytes.Data = pstring.Data 40 | pbytes.Len = pstring.Len 41 | pbytes.Cap = pstring.Len 42 | return 43 | } 44 | 45 | // IsSQLSep check if sql separation 46 | func IsSQLSep(r rune) bool { 47 | return r == ' ' || r == ',' || 48 | r == '\t' || r == '/' || 49 | r == '\n' || r == '\r' 50 | } 51 | 52 | // ArrayToString translate array to string 53 | func ArrayToString(array []int) string { 54 | if len(array) == 0 { 55 | return "" 56 | } 57 | var strArray []string 58 | for _, v := range array { 59 | strArray = append(strArray, strconv.FormatInt(int64(v), 10)) 60 | } 61 | 62 | return strings.Join(strArray, ", ") 63 | } 64 | 65 | // Abs int64 abs 66 | func Abs(n int64) int64 { 67 | y := n >> 63 68 | return (n ^ y) - y 69 | } 70 | -------------------------------------------------------------------------------- /util/hack/hack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The kingshard Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package hack 16 | 17 | import ( 18 | "bytes" 19 | "testing" 20 | ) 21 | 22 | func TestString(t *testing.T) { 23 | b := []byte("hello world") 24 | a := String(b) 25 | 26 | if a != "hello world" { 27 | t.Fatal(a) 28 | } 29 | 30 | b[0] = 'a' 31 | 32 | if a != "aello world" { 33 | t.Fatal(a) 34 | } 35 | 36 | b = append(b, "abc"...) 37 | if a != "aello world" { 38 | t.Fatal(a) 39 | } 40 | } 41 | 42 | func TestByte(t *testing.T) { 43 | a := "hello world" 44 | 45 | b := Slice(a) 46 | 47 | if !bytes.Equal(b, []byte("hello world")) { 48 | t.Fatal(string(b)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /util/math/compare.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package math 16 | 17 | // MaxInt64 returns the larger of a and b. 18 | func MaxInt64(a, b int64) int64 { 19 | if a > b { 20 | return a 21 | } 22 | 23 | return b 24 | } 25 | 26 | // MinInt64 returns the smaller of a and b. 27 | func MinInt64(a, b int64) int64 { 28 | if a < b { 29 | return a 30 | } 31 | 32 | return b 33 | } 34 | -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/PipeTest测试函数抽换缓存时序图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/util/mocks/pipeTest/assets/PipeTest测试函数抽换缓存时序图.png -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/PipeTest测试函数抽换缓存时序图.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | participant MockClient 3 | participant Pipe 4 | participant MockServer 5 | 6 | group #lightYellow 初始化 [建立客户端和服务端的连接] 7 | MockClient->>Pipe: mockClient 连接到 Pipe 8 | MockServer->>Pipe: mockServer 连接到 Pipe 9 | 10 | alt #Pink 进行修改的部份 11 | MockClient->>Pipe: 用 OverwriteConnBufWrite() 抽換寫入緩存 12 | MockClient->>Pipe: 用 Reset 重置写入连接 13 | end 14 | 15 | end 16 | 17 | loop 5次 18 | 19 | group #lightYellow 客户端传送讯息到服务端 [包含重置连接,等待连接读写完成] 20 | MockClient->>Pipe: 使用 SendOrReceive() 传送讯息1 21 | MockServer->>Pipe: 使用 Reply() 读取 Pipe 里的讯息1 22 | MockClient->>MockClient: 等待 Pipe 读写操作流程完成 23 | MockClient->>Pipe: 使用 ResetDcMockers() 单方向重置 Pipe 24 | MockServer->>MockServer: 决定回传的讯息,在测试环境下,原讯息的数值加1,等于之后的讯息 25 | end 26 | 27 | group #lightYellow 服务端传送讯息到客户端 [包含重置连接,等待连接读写完成] 28 | MockServer->>Pipe: 使用 SendOrReceive() 传送讯息2 29 | MockClient->>Pipe: 使用 Reply() 读取 Pipe 里的讯息2 30 | MockServer->>MockServer: 等待 Pipe 读写操作流程完成 31 | MockServer->>Pipe: 使用 ResetDcMockers() 单方向重置 Pipe 32 | MockClient->>MockClient: 决定回传的讯息,在测试环境下,原讯息的数值加1,等于之后的讯息 33 | end 34 | 35 | end 36 | 37 | @enduml 38 | -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest测试临时函数介入时序图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/util/mocks/pipeTest/assets/pipeTest测试临时函数介入时序图.png -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest测试临时函数介入时序图.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | participant MockClient 3 | participant Pipe 4 | participant MockServer 5 | 6 | group #LightYellow 初始化 [建立客户端和服务端的连接] 7 | MockClient->>Pipe: mockClient 连接到 Pipe 8 | MockServer->>Pipe: mockServer 连接到 Pipe 9 | end 10 | 11 | group #LightYellow 客户端传送讯息到服务端 [包含重置连接,等待连接读写完成] 12 | MockClient->>Pipe: 使用 SendOrReceive() 传送讯息1 13 | 14 | alt #Pink 进行修改的部份 15 | MockServer->>Pipe: 被测试函数临时连接到 Pipe 16 | MockServer->>Pipe: 被测试函数读取 Pipe 里的讯息1 17 | end 18 | 19 | MockClient->>MockClient: 等待 Pipe 读写操作流程完成 20 | MockClient->>Pipe: 使用 ResetDcMockers() 单方向重置 Pipe 21 | MockServer->>MockServer: 决定回传的讯息,在测试环境下,原讯息的数值加1,等于之后的讯息 22 | end 23 | 24 | group #LightYellow 服务端传送讯息到客户端 [包含重置连接,等待连接读写完成] 25 | MockServer->>Pipe: 使用 SendOrReceive() 传送讯息2 26 | MockClient->>Pipe: 使用 Reply() 读取 Pipe 里的讯息2 27 | MockServer->>MockServer: 等待 Pipe 读写操作流程完成 28 | MockServer->>Pipe: 使用 ResetDcMockers() 单方向重置 Pipe 29 | MockClient->>MockClient: 决定回传的讯息,在测试环境下,原讯息的数值加1,等于之后的讯息 30 | end 31 | @enduml 32 | -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest的对象图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/util/mocks/pipeTest/assets/pipeTest的对象图.png -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest的对象图.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | namespace pipeTest { 4 | object mockClient 5 | object mockServer 6 | } 7 | 8 | mockClient : connRead 9 | mockClient : connWrite 10 | mockServer : connRead 11 | mockServer : connWrite 12 | 13 | note top of mockClient 14 | DcMocker 类的对象,用来模拟客户端 15 | end note 16 | 17 | note top of mockServer 18 | DcMocker 类的对象,用来模拟服务端 19 | end note 20 | 21 | namespace net { 22 | object read0 23 | object read1 24 | object write0 25 | object write1 26 | } 27 | 28 | note top of read0 29 | read0 为 mockClient 的传送端 30 | end note 31 | 32 | note bottom of write0 33 | write0 为 mockServer 的接收端 34 | end note 35 | 36 | note top of read1 37 | read1 为 mockServer 的传送端 38 | end note 39 | 40 | note bottom of write1 41 | write1 为 mockClient 的接收端 42 | end note 43 | 44 | read0 -- write0 45 | read1 -- write1 46 | 47 | mockClient::connRead *-- read0 48 | mockClient::connWrite *-- write1 49 | mockServer::connRead *-- read1 50 | mockServer::connWrite *-- write0 51 | 52 | @enduml 53 | -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest的类图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/util/mocks/pipeTest/assets/pipeTest的类图.png -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest的类图.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | class pipeTest.DcMocker { 4 | - testing.T t 5 | - *bufio.Reader bufReader 6 | - *bufio.Writer bufWriter 7 | - Conn connRead 8 | - Conn connWrite 9 | - sync.WaitGroup wg 10 | - ReplyFuncType replyFunc 11 | - error err 12 | GetConnRead() net.Conn 13 | GetConnWrite() net.Conn 14 | GetBufReader() *bufio.Reader 15 | GetBufWriter() *bufio.Writer 16 | OverwriteConnBufRead(connRead net.Conn, bufReader *bufio.Reader) error 17 | OverwriteConnBufWrite(connWrite net.Conn, bufWriter *bufio.Writer) error 18 | ResetDcMockers(otherSide *DcMocker) error 19 | SendOrReceive(data []uint8) *DcMocker 20 | Reply(otherSide *DcMocker) (msg []uint8) 21 | WaitAndReset(otherSide *DcMocker) error 22 | } 23 | 24 | class testing.T {} 25 | class bufio.Reader {} 26 | class bufio.Writer {} 27 | class net.Conn {} 28 | class sync.WaitGroup {} 29 | 30 | pipeTest.DcMocker "1" o-- "1" testing.T 31 | pipeTest.DcMocker "1" *-- "1" bufio.Reader 32 | pipeTest.DcMocker "1" *-- "1" bufio.Writer 33 | pipeTest.DcMocker "1" *-- "2" net.Conn 34 | pipeTest.DcMocker "1" *-- "1" sync.WaitGroup 35 | 36 | note top of pipeTest.DcMocker 37 | DcMocker 为 模拟连用的类 38 | end note 39 | 40 | note bottom of testing.T 41 | T 为 测试用的类 42 | end note 43 | 44 | note bottom of bufio.Reader 45 | Reader 为有缓存的传送 46 | end note 47 | 48 | note bottom of bufio.Writer 49 | Writer 为有缓存的接收 50 | end note 51 | 52 | note bottom of net.Conn 53 | Conn 为传送或接收的连接 54 | end note 55 | 56 | note bottom of sync.WaitGroup 57 | WaitGroup 用来做等待连接的读写功能全部完成 58 | end note 59 | 60 | @enduml 61 | -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest连续测试时序图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/util/mocks/pipeTest/assets/pipeTest连续测试时序图.png -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/pipeTest连续测试时序图.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | participant MockClient 3 | participant Pipe 4 | participant MockServer 5 | 6 | group #LightYellow 初始化 [建立客户端和服务端的连接] 7 | MockClient->>Pipe: mockClient 连接到 Pipe 8 | MockServer->>Pipe: mockServer 连接到 Pipe 9 | end 10 | 11 | loop 5次 12 | group #LightYellow 客户端传送讯息到服务端 [包含重置连接,等待连接读写完成] 13 | MockClient->>Pipe: 使用 SendOrReceive() 传送讯息1 14 | MockServer->>Pipe: 使用 Reply() 读取 Pipe 里的讯息1 15 | MockClient->>MockClient: 等待 Pipe 读写操作流程完成 16 | MockClient->>Pipe: 使用 ResetDcMockers() 单方向重置 Pipe 17 | MockServer->>MockServer: 决定回传的讯息,在测试环境下,原讯息的数值加1,等于之后的讯息 18 | end 19 | group #LightYellow 服务端传送讯息到客户端 [包含重置连接,等待连接读写完成] 20 | MockServer->>Pipe: 使用 SendOrReceive() 传送讯息2 21 | MockClient->>Pipe: 使用 Reply() 读取 Pipe 里的讯息2 22 | MockServer->>MockServer: 等待 Pipe 读写操作流程完成 23 | MockServer->>Pipe: 使用 ResetDcMockers() 单方向重置 Pipe 24 | MockClient->>MockClient: 决定回传的讯息,在测试环境下,原讯息的数值加1,等于之后的讯息 25 | end 26 | end 27 | @enduml 28 | -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/单一连线方向重置.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiaoMi/Gaea/fb0925b31b190228e50c06c707baf2d7c48c7149/util/mocks/pipeTest/assets/单一连线方向重置.png -------------------------------------------------------------------------------- /util/mocks/pipeTest/assets/单一连线方向重置.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | [MockCLient] #Orchid 4 | [MockServer] #Orchid 5 | 6 | [Pipe] 7 | [read0] 8 | [write0] 9 | 10 | [Pipe2] #LightBlue 11 | [read1] #LightBlue 12 | [write1] #LightBlue 13 | 14 | 15 | Pipe -[#0000ff]-- read0 : belong 16 | Pipe -[#0000ff]-- write0 : belong 17 | Pipe2 --- read1 : belong 18 | Pipe2 --- write1 : belong 19 | 20 | MockCLient -[#0000ff]-> write0 : send msg 21 | MockServer -[#0000ff]-> read1 : receive msg 22 | 23 | MockCLient --> write1 : send msg 24 | MockServer --> read0 : receive msg 25 | 26 | @enduml 27 | -------------------------------------------------------------------------------- /util/mocks/pipeTest/pipeTest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pipeTest 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/require" 21 | ) 22 | 23 | // TestPipeTestWorkable 为验证测试 PipeTest 是否能正常运作,以下测试不使用 MariaDB 的服务器,只是单纯的单元测试 24 | func TestPipeTestWorkable(t *testing.T) { 25 | t.Run("此为测试 PipeTest 的验证测试,主要是用来确认整个测试流程没有问题", func(t *testing.T) { 26 | // 开始模拟物件 27 | mockClient, mockServer := NewDcServerClient(t, TestReplyMsgFunc) // 产生 mockClient 和 mockServer 模拟物件 28 | 29 | // 产生一开始的讯息和之后的预期正确讯息 30 | msg0 := []uint8{0} // 起始传送讯息 31 | correct := uint8(0) // 之后的预期正确讯息 32 | 33 | // 产生一连串的接收和回应的操作 34 | for i := 0; i < 5; i++ { 35 | msg1 := mockClient.SendOrReceiveMsg(msg0).ReplyMsg(mockServer) // 接收和回应 36 | correct++ // 每经过一个reply() 函数时,回应讯息会加1 37 | require.Equal(t, msg1[0], correct) 38 | msg0 = mockServer.SendOrReceiveMsg(msg1).ReplyMsg(mockClient) // 接收和回应 39 | correct++ // 每经过一个reply() 函数时,回应讯息会加1 40 | require.Equal(t, msg0[0], correct) 41 | } 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /util/padding.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 Coda Hale 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package util 26 | 27 | func times(str string, n int) (out string) { 28 | for i := 0; i < n; i++ { 29 | out += str 30 | } 31 | return 32 | } 33 | 34 | // Left left-pads the string with pad up to len runes 35 | // len may be exceeded if 36 | func Left(str string, length int, pad string) string { 37 | return times(pad, length-len(str)) + str 38 | } 39 | 40 | // Right right-pads the string with pad up to len runes 41 | func Right(str string, length int, pad string) string { 42 | return str + times(pad, length-len(str)) 43 | } 44 | -------------------------------------------------------------------------------- /util/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import "strings" 18 | 19 | func LowerEqual(src string, dest string) bool { 20 | if len(src) != len(dest) { 21 | return false 22 | } 23 | return strings.ToLower(src) == dest 24 | } 25 | 26 | func UpperEqual(src string, dest string) bool { 27 | if len(src) != len(dest) { 28 | return false 29 | } 30 | return strings.ToUpper(src) == dest 31 | } 32 | 33 | func HasUpperPrefix(src string, dest string) bool { 34 | if len(src) < len(dest) { 35 | return false 36 | } 37 | return strings.ToUpper(src[0:len(dest)]) == dest 38 | } 39 | -------------------------------------------------------------------------------- /util/sync2/atomic_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sync2 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func TestAtomicString(t *testing.T) { 24 | var s AtomicString 25 | if s.Get() != "" { 26 | t.Errorf("want empty, got %s", s.Get()) 27 | } 28 | s.Set("a") 29 | if s.Get() != "a" { 30 | t.Errorf("want a, got %s", s.Get()) 31 | } 32 | if s.CompareAndSwap("b", "c") { 33 | t.Errorf("want false, got true") 34 | } 35 | if s.Get() != "a" { 36 | t.Errorf("want a, got %s", s.Get()) 37 | } 38 | if !s.CompareAndSwap("a", "c") { 39 | t.Errorf("want true, got false") 40 | } 41 | if s.Get() != "c" { 42 | t.Errorf("want c, got %s", s.Get()) 43 | } 44 | } 45 | 46 | func TestAtomicBool(t *testing.T) { 47 | b := NewAtomicBool(true) 48 | if !b.Get() { 49 | t.Error("b.Get: false, want true") 50 | } 51 | b.Set(false) 52 | if b.Get() { 53 | t.Error("b.Get: true, want false") 54 | } 55 | b.Set(true) 56 | if !b.Get() { 57 | t.Error("b.Get: false, want true") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /util/sync2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package sync2 provides extra functionality along the same lines as sync. 18 | package sync2 19 | -------------------------------------------------------------------------------- /util/sync2/semaphore_flaky_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sync2 18 | 19 | import ( 20 | "testing" 21 | "time" 22 | ) 23 | 24 | func TestSemaNoTimeout(t *testing.T) { 25 | s := NewSemaphore(1, 0) 26 | s.Acquire() 27 | released := false 28 | go func() { 29 | time.Sleep(10 * time.Millisecond) 30 | released = true 31 | s.Release() 32 | }() 33 | s.Acquire() 34 | if !released { 35 | t.Errorf("release: false, want true") 36 | } 37 | } 38 | 39 | func TestSemaTimeout(t *testing.T) { 40 | s := NewSemaphore(1, 5*time.Millisecond) 41 | s.Acquire() 42 | go func() { 43 | time.Sleep(10 * time.Millisecond) 44 | s.Release() 45 | }() 46 | if s.Acquire() { 47 | t.Errorf("Acquire: true, want false") 48 | } 49 | time.Sleep(10 * time.Millisecond) 50 | if !s.Acquire() { 51 | t.Errorf("Acquire: false, want true") 52 | } 53 | } 54 | 55 | func TestSemaTryAcquire(t *testing.T) { 56 | s := NewSemaphore(1, 0) 57 | if !s.TryAcquire() { 58 | t.Errorf("TryAcquire: false, want true") 59 | } 60 | if s.TryAcquire() { 61 | t.Errorf("TryAcquire: true, want false") 62 | } 63 | s.Release() 64 | if !s.TryAcquire() { 65 | t.Errorf("TryAcquire: false, want true") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /util/testleak/add-leaktest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Usage: add-leaktest.sh pkg/*_test.go 4 | 5 | set -eu 6 | 7 | sed -i'~' -e ' 8 | /^func (s \*test.*Suite) Test.*(c \*C) {/ { 9 | n 10 | /testleak.AfterTest/! i\ 11 | defer testleak.AfterTest(c)() 12 | } 13 | ' $@ 14 | 15 | for i in $@; do 16 | if ! cmp -s $i $i~ ; then 17 | goimports -w $i 18 | fi 19 | echo $i 20 | rm -f $i~ 21 | done 22 | -------------------------------------------------------------------------------- /util/testleak/check-leaktest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Usage: check-leaktest.sh 4 | # It needs to run under the github.com/pingcap/tidb directory. 5 | 6 | set -e 7 | 8 | pkgs=$(git grep 'Suite' |grep -vE "Godeps|tags" |awk -F: '{print $1}' | xargs -n1 dirname | sort |uniq) 9 | echo $pkgs 10 | for pkg in ${pkgs}; do 11 | if [ -z "$(ls ${pkg}/*_test.go 2>/dev/null)" ]; then 12 | continue 13 | fi 14 | awk -F'[(]' ' 15 | /func \(s .*Suite\) Test.*C\) {/ { 16 | test = $1"("$2 17 | next 18 | } 19 | 20 | /defer testleak.AfterTest/ { 21 | test = 0 22 | next 23 | } 24 | 25 | { 26 | if (test && (FILENAME != "./tidb_test.go")) { 27 | printf "%s: %s: missing defer testleak.AfterTest\n", FILENAME, test 28 | test = 0 29 | code = 1 30 | } 31 | } 32 | 33 | END { 34 | exit code 35 | } 36 | 37 | ' ${pkg}/*_test.go 38 | done 39 | -------------------------------------------------------------------------------- /util/testleak/fake.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | //go:build !leak 14 | // +build !leak 15 | 16 | package testleak 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/pingcap/check" 22 | ) 23 | 24 | // BeforeTest is a dummy implementation when build tag 'leak' is not set. 25 | func BeforeTest() { 26 | } 27 | 28 | // AfterTest is a dummy implementation when build tag 'leak' is not set. 29 | func AfterTest(c *check.C) func() { 30 | return func() { 31 | } 32 | } 33 | 34 | // AfterTestT is used after all the test cases is finished. 35 | func AfterTestT(t *testing.T) func() { 36 | return func() { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /util/timer/randticker.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package timer 18 | 19 | import ( 20 | "math/rand" 21 | "time" 22 | ) 23 | 24 | // RandTicker is just like time.Ticker, except that 25 | // it adds randomness to the events. 26 | type RandTicker struct { 27 | C <-chan time.Time 28 | done chan struct{} 29 | } 30 | 31 | // NewRandTicker creates a new RandTicker. d is the duration, 32 | // and variance specifies the variance. The ticker will tick 33 | // every d +/- variance. 34 | func NewRandTicker(d, variance time.Duration) *RandTicker { 35 | c := make(chan time.Time, 1) 36 | done := make(chan struct{}) 37 | go func() { 38 | rnd := rand.New(rand.NewSource(time.Now().UnixNano())) 39 | for { 40 | vr := time.Duration(rnd.Int63n(int64(2*variance)) - int64(variance)) 41 | tmr := time.NewTimer(d + vr) 42 | select { 43 | case <-tmr.C: 44 | select { 45 | case c <- time.Now(): 46 | default: 47 | } 48 | case <-done: 49 | tmr.Stop() 50 | close(c) 51 | return 52 | } 53 | } 54 | }() 55 | return &RandTicker{ 56 | C: c, 57 | done: done, 58 | } 59 | } 60 | 61 | // Stop stops the ticker and closes the underlying channel. 62 | func (tkr *RandTicker) Stop() { 63 | close(tkr.done) 64 | } 65 | -------------------------------------------------------------------------------- /util/timer/randticker_flaky_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package timer 18 | 19 | import ( 20 | "testing" 21 | "time" 22 | ) 23 | 24 | const ( 25 | testDuration = 100 * time.Millisecond 26 | testVariance = 20 * time.Millisecond 27 | ) 28 | 29 | func TestTick(t *testing.T) { 30 | if testing.Short() { 31 | t.Skip("Skip TestTick in short mode") 32 | } 33 | tkr := NewRandTicker(testDuration, testVariance) 34 | for i := 0; i < 5; i++ { 35 | start := time.Now() 36 | end := <-tkr.C 37 | diff := start.Add(testDuration).Sub(end) 38 | tolerance := testVariance + 20*time.Millisecond 39 | if diff < -tolerance || diff > tolerance { 40 | t.Errorf("start: %v, end: %v, diff %v. Want <%v tolerenace", start, end, diff, tolerance) 41 | } 42 | } 43 | tkr.Stop() 44 | _, ok := <-tkr.C 45 | if ok { 46 | t.Error("Channel was not closed") 47 | } 48 | } 49 | 50 | func TestTickSkip(t *testing.T) { 51 | if testing.Short() { 52 | t.Skip("Skip TestTickSkip in short mode") 53 | } 54 | tkr := NewRandTicker(10*time.Millisecond, 1*time.Millisecond) 55 | time.Sleep(35 * time.Millisecond) 56 | end := <-tkr.C 57 | diff := time.Now().Sub(end) 58 | if diff < 20*time.Millisecond { 59 | t.Errorf("diff: %v, want >20ms", diff) 60 | } 61 | 62 | // This tick should be up-to-date 63 | end = <-tkr.C 64 | diff = time.Now().Sub(end) 65 | if diff > 1*time.Millisecond { 66 | t.Errorf("diff: %v, want <1ms", diff) 67 | } 68 | tkr.Stop() 69 | } 70 | -------------------------------------------------------------------------------- /util/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "strings" 19 | 20 | "github.com/XiaoMi/Gaea/parser/format" 21 | types "github.com/XiaoMi/Gaea/parser/tidb-types" 22 | driver "github.com/XiaoMi/Gaea/parser/tidb-types/parser_driver" 23 | ) 24 | 25 | // GetValueExprResult copy from ValueExpr.Restore() 26 | // TODO: 分表列是否需要支持等值比较NULL 27 | func GetValueExprResult(n *driver.ValueExpr) (interface{}, error) { 28 | switch n.Kind() { 29 | case types.KindNull: 30 | return nil, nil // TODO: null str or nil? 31 | case types.KindInt64: 32 | return n.GetInt64(), nil 33 | case types.KindUint64: 34 | return n.GetUint64(), nil 35 | case types.KindFloat32: 36 | return n.GetFloat32(), nil 37 | case types.KindFloat64: 38 | return n.GetFloat64(), nil 39 | case types.KindString, types.KindBytes: 40 | return n.GetString(), nil 41 | default: 42 | s := &strings.Builder{} 43 | ctx := format.NewRestoreCtx(format.EscapeRestoreFlags, s) 44 | err := n.Restore(ctx) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return s.String(), nil 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "fmt" 19 | "strconv" 20 | "sync/atomic" 21 | "time" 22 | ) 23 | 24 | // BoolIndex rolled array switch mark 25 | type BoolIndex struct { 26 | index int32 27 | } 28 | 29 | // Set set index value 30 | func (b *BoolIndex) Set(index bool) { 31 | if index { 32 | atomic.StoreInt32(&b.index, 1) 33 | } else { 34 | atomic.StoreInt32(&b.index, 0) 35 | } 36 | } 37 | 38 | // Get return current, next, current bool value 39 | func (b *BoolIndex) Get() (int32, int32, bool) { 40 | index := atomic.LoadInt32(&b.index) 41 | if index == 1 { 42 | return 1, 0, true 43 | } 44 | return 0, 1, false 45 | } 46 | 47 | // ItoString interface to string 48 | func ItoString(a interface{}) (bool, string) { 49 | switch a.(type) { 50 | case nil: 51 | return false, "NULL" 52 | case []byte: 53 | return true, string(a.([]byte)) 54 | default: 55 | return false, fmt.Sprintf("%v", a) 56 | } 57 | } 58 | 59 | // Int2TimeDuration convert int to Time.Duration 60 | func Int2TimeDuration(t int) (time.Duration, error) { 61 | tmp := strconv.Itoa(t) 62 | tmp = tmp + "s" 63 | idleTimeout, err := time.ParseDuration(tmp) 64 | if err != nil { 65 | return 0, err 66 | 67 | } 68 | return idleTimeout, nil 69 | } 70 | -------------------------------------------------------------------------------- /util/version_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/XiaoMi/Gaea/mysql" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestCompactServerVersion(t *testing.T) { 10 | type tCase struct { 11 | oVersion string 12 | dVersion string 13 | } 14 | 15 | tts := []tCase{ 16 | { 17 | "", 18 | mysql.ServerVersion, 19 | }, 20 | { 21 | "5.7-gaea", 22 | mysql.ServerVersion, 23 | }, 24 | { 25 | "5.7.11-gaea", 26 | "5.7.11-gaea", 27 | }, 28 | { 29 | "5.7.11", 30 | "5.7.11", 31 | }, 32 | { 33 | "8.0.0-gaea", 34 | "8.0.0-gaea", 35 | }, 36 | } 37 | for _, tt := range tts { 38 | assert.Equal(t, CompactServerVersion(tt.oVersion), tt.dVersion) 39 | } 40 | } 41 | 42 | func TestCheckMySQLVersion(t *testing.T) { 43 | type tCase struct { 44 | version string 45 | lessThan string 46 | isTrue bool 47 | } 48 | 49 | tts := []tCase{{ 50 | "5.7.20-gaea", 51 | "< 5.7.25", 52 | true, 53 | }, 54 | { 55 | "5.7.20-gaea", 56 | ">= 5.7.10", 57 | true, 58 | }, 59 | { 60 | "5.7.11-gaea", 61 | "< 5.7.25", 62 | true, 63 | }, 64 | { 65 | "5.6.20-gaea", 66 | "< 5.7.25", 67 | true, 68 | }, 69 | { 70 | "5.6.20-gaea", 71 | "< 8.0.0", 72 | true, 73 | }, 74 | { 75 | "5.6.20", 76 | "< 8.0.0", 77 | true, 78 | }, 79 | } 80 | for _, tt := range tts { 81 | assert.Equal(t, CheckMySQLVersion(tt.version, tt.lessThan), tt.isTrue) 82 | } 83 | } 84 | --------------------------------------------------------------------------------