clickhouse.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // Copyright 2019 Yunion
  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 dbutils
  15. import (
  16. "fmt"
  17. "strings"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/pkg/errors"
  20. "yunion.io/x/onecloud/pkg/httperrors"
  21. )
  22. // convert clickhouse sqlstr v1 to v2
  23. // v1: tcp://192.168.222.4:9000?database=yunionmeter&read_timeout=10&write_timeout=20
  24. // v2: clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60
  25. func ClickhouseSqlStrV1ToV2(sqlstr string) (string, error) {
  26. if strings.HasPrefix(sqlstr, "clickhouse://") {
  27. // already v2 format
  28. return sqlstr, nil
  29. }
  30. queryPos := strings.IndexByte(sqlstr, '?')
  31. if queryPos <= 0 {
  32. return "", errors.Wrap(httperrors.ErrInputParameter, "no query string")
  33. }
  34. hostPart := sqlstr[len("tcp://"):queryPos]
  35. qs, err := jsonutils.ParseQueryString(sqlstr[queryPos+1:])
  36. if err != nil {
  37. return "", errors.Wrap(err, "ParseQueryString")
  38. }
  39. dbname, _ := qs.GetString("database")
  40. if len(dbname) == 0 {
  41. return "", errors.Wrap(httperrors.ErrInputParameter, "empty database")
  42. }
  43. uname, _ := qs.GetString("username")
  44. pword, _ := qs.GetString("password")
  45. if len(uname) > 0 {
  46. if len(pword) > 0 {
  47. hostPart = fmt.Sprintf("%s:%s@%s", uname, pword, hostPart)
  48. } else {
  49. hostPart = fmt.Sprintf("%s@%s", uname, hostPart)
  50. }
  51. }
  52. return fmt.Sprintf("clickhouse://%s/%s?dial_timeout=200ms&max_execution_time=60", hostPart, dbname), nil
  53. }
  54. func ClickhouseSqlStrV2ToV1(sqlstr string) (string, error) {
  55. if strings.HasPrefix(sqlstr, "tcp://") {
  56. // already v1 format
  57. return sqlstr, nil
  58. }
  59. hostPart := sqlstr[len("clickhouse://"):]
  60. queryPos := strings.IndexByte(hostPart, '?')
  61. if queryPos > 0 {
  62. hostPart = hostPart[:queryPos]
  63. }
  64. slashPos := strings.IndexByte(hostPart, '/')
  65. if slashPos <= 0 {
  66. return "", errors.Wrap(httperrors.ErrInputParameter, "no database part")
  67. }
  68. qs := make(map[string]string)
  69. qs["database"] = hostPart[slashPos+1:]
  70. hostPart = hostPart[:slashPos]
  71. atPos := strings.IndexByte(hostPart, '@')
  72. if atPos > 0 {
  73. authPart := hostPart[:atPos]
  74. hostPart = hostPart[atPos+1:]
  75. colonPos := strings.IndexByte(authPart, ':')
  76. if colonPos > 0 {
  77. qs["username"] = authPart[:colonPos]
  78. qs["password"] = authPart[colonPos+1:]
  79. } else {
  80. qs["username"] = authPart
  81. }
  82. }
  83. return fmt.Sprintf("tcp://%s?%s&read_timeout=10&write_timeout=20", hostPart, jsonutils.Marshal(qs).QueryString()), nil
  84. }
  85. func ValidateClickhouseV2Str(sqlstr string) error {
  86. if !strings.HasPrefix(sqlstr, "clickhouse://") {
  87. return errors.Wrapf(httperrors.ErrInputParameter, "must start with clickhouse://")
  88. }
  89. qsPos := strings.IndexByte(sqlstr, '?')
  90. if qsPos >= 0 {
  91. sqlstr = sqlstr[:qsPos]
  92. }
  93. slashPos := strings.IndexByte(sqlstr, '/')
  94. if slashPos <= 0 {
  95. return errors.Wrapf(httperrors.ErrInputParameter, "missing db slash")
  96. }
  97. dbName := sqlstr[slashPos+1:]
  98. if len(dbName) == 0 {
  99. return errors.Wrapf(httperrors.ErrInputParameter, "empty database name")
  100. }
  101. return nil
  102. }
  103. func ValidateClickhouseV1Str(sqlstr string) error {
  104. if !strings.HasPrefix(sqlstr, "tcp://") {
  105. return errors.Wrapf(httperrors.ErrInputParameter, "must start with tcp://")
  106. }
  107. qsPos := strings.IndexByte(sqlstr, '?')
  108. if qsPos <= 0 {
  109. return errors.Wrapf(httperrors.ErrInputParameter, "mising query string")
  110. }
  111. qsPart := sqlstr[qsPos+1:]
  112. qs, err := jsonutils.ParseQueryString(qsPart)
  113. if err != nil {
  114. return errors.Wrap(err, "ParseQueryString")
  115. }
  116. dbName, _ := qs.GetString("database")
  117. if len(dbName) == 0 {
  118. return errors.Wrapf(httperrors.ErrInputParameter, "empty database name")
  119. }
  120. return nil
  121. }