ValidationUtil.cpp 14.7 KB
Newer Older
1
// Copyright (C) 2019-2020 Zilliz. All rights reserved.
J
jinhai 已提交
2
//
3 4
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
J
jinhai 已提交
5
//
6 7 8 9 10
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under the License.
J
jinhai 已提交
11

S
starlord 已提交
12
#include "utils/ValidationUtil.h"
J
jinhai 已提交
13
#include "Log.h"
S
starlord 已提交
14
#include "db/engine/ExecutionEngine.h"
15
#include "utils/StringHelpFunctions.h"
J
jinhai 已提交
16

Z
zhiru 已提交
17
#include <arpa/inet.h>
G
groot 已提交
18

Y
youny626 已提交
19
#ifdef MILVUS_GPU_VERSION
G
groot 已提交
20

S
starlord 已提交
21
#include <cuda_runtime.h>
G
groot 已提交
22

Y
youny626 已提交
23
#endif
G
groot 已提交
24

S
shengjh 已提交
25
#include <fiu-local.h>
Z
zhiru 已提交
26
#include <algorithm>
J
JinHai-CN 已提交
27
#include <cmath>
S
starlord 已提交
28 29
#include <regex>
#include <string>
Z
zhiru 已提交
30

J
jinhai 已提交
31 32 33
namespace milvus {
namespace server {

34
constexpr size_t TABLE_NAME_SIZE_LIMIT = 255;
G
groot 已提交
35
constexpr int64_t TABLE_DIMENSION_LIMIT = 32768;
S
starlord 已提交
36
constexpr int32_t INDEX_FILE_SIZE_LIMIT = 4096;  // index trigger size max = 4096 MB
J
jinhai 已提交
37

S
starlord 已提交
38
Status
S
starlord 已提交
39
ValidationUtil::ValidateTableName(const std::string& table_name) {
J
jinhai 已提交
40 41
    // Table name shouldn't be empty.
    if (table_name.empty()) {
42
        std::string msg = "Table name should not be empty.";
S
starlord 已提交
43 44
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_TABLE_NAME, msg);
J
jinhai 已提交
45 46
    }

47
    std::string invalid_msg = "Invalid table name: " + table_name + ". ";
J
jinhai 已提交
48
    // Table name size shouldn't exceed 16384.
49
    if (table_name.size() > TABLE_NAME_SIZE_LIMIT) {
50
        std::string msg = invalid_msg + "The length of a table name must be less than 255 characters.";
S
starlord 已提交
51 52
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_TABLE_NAME, msg);
J
jinhai 已提交
53 54 55 56 57
    }

    // Table name first character should be underscore or character.
    char first_char = table_name[0];
    if (first_char != '_' && std::isalpha(first_char) == 0) {
58
        std::string msg = invalid_msg + "The first character of a table name must be an underscore or letter.";
S
starlord 已提交
59 60
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_TABLE_NAME, msg);
J
jinhai 已提交
61 62 63 64 65 66
    }

    int64_t table_name_size = table_name.size();
    for (int64_t i = 1; i < table_name_size; ++i) {
        char name_char = table_name[i];
        if (name_char != '_' && std::isalnum(name_char) == 0) {
67
            std::string msg = invalid_msg + "Table name can only contain numbers, letters, and underscores.";
S
starlord 已提交
68 69
            SERVER_LOG_ERROR << msg;
            return Status(SERVER_INVALID_TABLE_NAME, msg);
J
jinhai 已提交
70 71 72
        }
    }

S
starlord 已提交
73
    return Status::OK();
J
jinhai 已提交
74 75
}

S
starlord 已提交
76
Status
77
ValidationUtil::ValidateTableDimension(int64_t dimension) {
78
    if (dimension <= 0 || dimension > TABLE_DIMENSION_LIMIT) {
S
starlord 已提交
79
        std::string msg = "Invalid table dimension: " + std::to_string(dimension) + ". " +
G
groot 已提交
80 81
                          "The table dimension must be within the range of 1 ~ " +
                          std::to_string(TABLE_DIMENSION_LIMIT) + ".";
S
starlord 已提交
82 83
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_VECTOR_DIMENSION, msg);
S
starlord 已提交
84
    } else {
S
starlord 已提交
85
        return Status::OK();
J
jinhai 已提交
86 87 88
    }
}

S
starlord 已提交
89
Status
90
ValidationUtil::ValidateTableIndexType(int32_t index_type) {
S
starlord 已提交
91 92
    int engine_type = static_cast<int>(engine::EngineType(index_type));
    if (engine_type <= 0 || engine_type > static_cast<int>(engine::EngineType::MAX_VALUE)) {
S
starlord 已提交
93 94
        std::string msg = "Invalid index type: " + std::to_string(index_type) + ". " +
                          "Make sure the index type is in IndexType list.";
S
starlord 已提交
95 96
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_INDEX_TYPE, msg);
J
jinhai 已提交
97
    }
98

99 100 101 102 103 104 105 106 107
#ifndef CUSTOMIZATION
    // special case, hybird index only available in customize faiss library
    if (engine_type == static_cast<int>(engine::EngineType::FAISS_IVFSQ8H)) {
        std::string msg = "Unsupported index type: " + std::to_string(index_type);
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_INDEX_TYPE, msg);
    }
#endif

S
starlord 已提交
108
    return Status::OK();
J
jinhai 已提交
109 110
}

G
groot 已提交
111 112 113 114 115 116
bool
ValidationUtil::IsBinaryIndexType(int32_t index_type) {
    return (index_type == static_cast<int32_t>(engine::EngineType::FAISS_BIN_IDMAP)) ||
           (index_type == static_cast<int32_t>(engine::EngineType::FAISS_BIN_IVFFLAT));
}

S
starlord 已提交
117
Status
S
starlord 已提交
118
ValidationUtil::ValidateTableIndexNlist(int32_t nlist) {
Z
zhiru 已提交
119
    if (nlist <= 0) {
S
starlord 已提交
120 121
        std::string msg =
            "Invalid index nlist: " + std::to_string(nlist) + ". " + "The index nlist must be greater than 0.";
S
starlord 已提交
122 123
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_INDEX_NLIST, msg);
S
starlord 已提交
124 125
    }

S
starlord 已提交
126
    return Status::OK();
S
starlord 已提交
127 128
}

S
starlord 已提交
129
Status
130
ValidationUtil::ValidateTableIndexFileSize(int64_t index_file_size) {
Z
zhiru 已提交
131
    if (index_file_size <= 0 || index_file_size > INDEX_FILE_SIZE_LIMIT) {
S
starlord 已提交
132 133 134
        std::string msg = "Invalid index file size: " + std::to_string(index_file_size) + ". " +
                          "The index file size must be within the range of 1 ~ " +
                          std::to_string(INDEX_FILE_SIZE_LIMIT) + ".";
S
starlord 已提交
135 136
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_INDEX_FILE_SIZE, msg);
S
starlord 已提交
137 138
    }

S
starlord 已提交
139
    return Status::OK();
S
starlord 已提交
140 141
}

S
starlord 已提交
142
Status
S
starlord 已提交
143
ValidationUtil::ValidateTableIndexMetricType(int32_t metric_type) {
G
groot 已提交
144
    if (metric_type <= 0 || metric_type > static_cast<int32_t>(engine::MetricType::MAX_VALUE)) {
S
starlord 已提交
145
        std::string msg = "Invalid index metric type: " + std::to_string(metric_type) + ". " +
G
groot 已提交
146
                          "Make sure the metric type is in MetricType list.";
S
starlord 已提交
147 148
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_INDEX_METRIC_TYPE, msg);
S
starlord 已提交
149
    }
S
starlord 已提交
150
    return Status::OK();
S
starlord 已提交
151 152
}

G
groot 已提交
153 154 155 156 157 158 159
bool
ValidationUtil::IsBinaryMetricType(int32_t metric_type) {
    return (metric_type == static_cast<int32_t>(engine::MetricType::HAMMING)) ||
           (metric_type == static_cast<int32_t>(engine::MetricType::JACCARD)) ||
           (metric_type == static_cast<int32_t>(engine::MetricType::TANIMOTO));
}

S
starlord 已提交
160
Status
S
starlord 已提交
161
ValidationUtil::ValidateSearchTopk(int64_t top_k, const engine::meta::TableSchema& table_schema) {
162
    if (top_k <= 0 || top_k > 2048) {
S
starlord 已提交
163 164
        std::string msg =
            "Invalid topk: " + std::to_string(top_k) + ". " + "The topk must be within the range of 1 ~ 2048.";
S
starlord 已提交
165 166
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_TOPK, msg);
167 168
    }

S
starlord 已提交
169
    return Status::OK();
170 171
}

S
starlord 已提交
172
Status
S
starlord 已提交
173
ValidationUtil::ValidateSearchNprobe(int64_t nprobe, const engine::meta::TableSchema& table_schema) {
174
    if (nprobe <= 0 || nprobe > table_schema.nlist_) {
S
starlord 已提交
175 176
        std::string msg = "Invalid nprobe: " + std::to_string(nprobe) + ". " +
                          "The nprobe must be within the range of 1 ~ index nlist.";
S
starlord 已提交
177 178
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_NPROBE, msg);
179 180
    }

S
starlord 已提交
181
    return Status::OK();
182 183
}

184 185 186 187 188 189 190 191
Status
ValidationUtil::ValidatePartitionName(const std::string& partition_name) {
    if (partition_name.empty()) {
        std::string msg = "Partition name should not be empty.";
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_TABLE_NAME, msg);
    }

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    std::string invalid_msg = "Invalid partition name: " + partition_name + ". ";
    // Table name size shouldn't exceed 16384.
    if (partition_name.size() > TABLE_NAME_SIZE_LIMIT) {
        std::string msg = invalid_msg + "The length of a partition name must be less than 255 characters.";
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_TABLE_NAME, msg);
    }

    // Table name first character should be underscore or character.
    char first_char = partition_name[0];
    if (first_char != '_' && std::isalpha(first_char) == 0) {
        std::string msg = invalid_msg + "The first character of a partition name must be an underscore or letter.";
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_TABLE_NAME, msg);
    }

    int64_t table_name_size = partition_name.size();
    for (int64_t i = 1; i < table_name_size; ++i) {
        char name_char = partition_name[i];
        if (name_char != '_' && std::isalnum(name_char) == 0) {
            std::string msg = invalid_msg + "Partition name can only contain numbers, letters, and underscores.";
            SERVER_LOG_ERROR << msg;
            return Status(SERVER_INVALID_TABLE_NAME, msg);
        }
    }

    return Status::OK();
219 220
}

G
groot 已提交
221 222
Status
ValidationUtil::ValidatePartitionTags(const std::vector<std::string>& partition_tags) {
223 224 225 226 227 228 229
    for (const std::string& tag : partition_tags) {
        // trim side-blank of tag, only compare valid characters
        // for example: " ab cd " is treated as "ab cd"
        std::string valid_tag = tag;
        StringHelpFunctions::TrimStringBlank(valid_tag);
        if (valid_tag.empty()) {
            std::string msg = "Invalid partition tag: " + valid_tag + ". " + "Partition tag should not be empty.";
G
groot 已提交
230
            SERVER_LOG_ERROR << msg;
231
            return Status(SERVER_INVALID_TABLE_NAME, msg);
G
groot 已提交
232 233 234 235 236 237
        }
    }

    return Status::OK();
}

S
starlord 已提交
238
Status
239
ValidationUtil::ValidateGpuIndex(int32_t gpu_index) {
Y
youny626 已提交
240
#ifdef MILVUS_GPU_VERSION
241 242
    int num_devices = 0;
    auto cuda_err = cudaGetDeviceCount(&num_devices);
S
shengjh 已提交
243 244
    fiu_do_on("ValidationUtil.ValidateGpuIndex.get_device_count_fail", cuda_err = cudaError::cudaErrorUnknown);

S
starlord 已提交
245
    if (cuda_err != cudaSuccess) {
S
starlord 已提交
246 247 248
        std::string msg = "Failed to get gpu card number, cuda error:" + std::to_string(cuda_err);
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_UNEXPECTED_ERROR, msg);
249 250
    }

Z
zhiru 已提交
251
    if (gpu_index >= num_devices) {
S
starlord 已提交
252 253 254
        std::string msg = "Invalid gpu index: " + std::to_string(gpu_index);
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_INVALID_ARGUMENT, msg);
255
    }
Y
youny626 已提交
256
#endif
257

S
starlord 已提交
258
    return Status::OK();
259 260
}

G
groot 已提交
261
#ifdef MILVUS_GPU_VERSION
G
groot 已提交
262

S
starlord 已提交
263
Status
264
ValidationUtil::GetGpuMemory(int32_t gpu_index, size_t& memory) {
S
shengjh 已提交
265 266
    fiu_return_on("ValidationUtil.GetGpuMemory.return_error", Status(SERVER_UNEXPECTED_ERROR, ""));

267 268 269
    cudaDeviceProp deviceProp;
    auto cuda_err = cudaGetDeviceProperties(&deviceProp, gpu_index);
    if (cuda_err) {
Y
youny626 已提交
270 271
        std::string msg = "Failed to get gpu properties for gpu" + std::to_string(gpu_index) +
                          " , cuda error:" + std::to_string(cuda_err);
S
starlord 已提交
272 273
        SERVER_LOG_ERROR << msg;
        return Status(SERVER_UNEXPECTED_ERROR, msg);
274 275 276
    }

    memory = deviceProp.totalGlobalMem;
S
starlord 已提交
277
    return Status::OK();
278
}
G
groot 已提交
279

G
groot 已提交
280
#endif
281

S
starlord 已提交
282
Status
S
starlord 已提交
283
ValidationUtil::ValidateIpAddress(const std::string& ip_address) {
Z
zhiru 已提交
284 285 286
    struct in_addr address;

    int result = inet_pton(AF_INET, ip_address.c_str(), &address);
S
shengjh 已提交
287
    fiu_do_on("ValidationUtil.ValidateIpAddress.error_ip_result", result = 2);
Z
zhiru 已提交
288

Z
zhiru 已提交
289
    switch (result) {
S
starlord 已提交
290 291
        case 1:
            return Status::OK();
S
starlord 已提交
292 293 294 295 296 297 298 299 300 301
        case 0: {
            std::string msg = "Invalid IP address: " + ip_address;
            SERVER_LOG_ERROR << msg;
            return Status(SERVER_INVALID_ARGUMENT, msg);
        }
        default: {
            std::string msg = "IP address conversion error: " + ip_address;
            SERVER_LOG_ERROR << msg;
            return Status(SERVER_UNEXPECTED_ERROR, msg);
        }
Z
zhiru 已提交
302 303 304
    }
}

S
starlord 已提交
305
Status
S
starlord 已提交
306
ValidationUtil::ValidateStringIsNumber(const std::string& str) {
Y
yudong.cai 已提交
307 308
    if (str.empty() || !std::all_of(str.begin(), str.end(), ::isdigit)) {
        return Status(SERVER_INVALID_ARGUMENT, "Invalid number");
Z
zhiru 已提交
309
    }
Y
yudong.cai 已提交
310
    try {
B
BossZou 已提交
311
        int64_t value = std::stol(str);
S
shengjh 已提交
312
        fiu_do_on("ValidationUtil.ValidateStringIsNumber.throw_exception", throw std::exception());
313 314 315
        if (value < 0) {
            return Status(SERVER_INVALID_ARGUMENT, "Negative number");
        }
S
starlord 已提交
316
    } catch (...) {
Y
yudong.cai 已提交
317
        return Status(SERVER_INVALID_ARGUMENT, "Invalid number");
Z
zhiru 已提交
318
    }
Y
yudong.cai 已提交
319
    return Status::OK();
Z
zhiru 已提交
320 321
}

S
starlord 已提交
322
Status
S
starlord 已提交
323
ValidationUtil::ValidateStringIsBool(const std::string& str) {
S
shengjh 已提交
324
    fiu_return_on("ValidateStringNotBool", Status(SERVER_INVALID_ARGUMENT, "Invalid boolean: " + str));
Y
yudong.cai 已提交
325 326
    std::string s = str;
    std::transform(s.begin(), s.end(), s.begin(), ::tolower);
S
starlord 已提交
327
    if (s == "true" || s == "on" || s == "yes" || s == "1" || s == "false" || s == "off" || s == "no" || s == "0" ||
Y
yudong.cai 已提交
328
        s.empty()) {
S
starlord 已提交
329
        return Status::OK();
Z
zhiru 已提交
330
    }
S
starlord 已提交
331
    return Status(SERVER_INVALID_ARGUMENT, "Invalid boolean: " + str);
Z
zhiru 已提交
332 333
}

S
starlord 已提交
334
Status
S
starlord 已提交
335
ValidationUtil::ValidateStringIsFloat(const std::string& str) {
Y
yudong.cai 已提交
336 337
    try {
        float val = std::stof(str);
338 339 340
        if (val < 0.0) {
            return Status(SERVER_INVALID_ARGUMENT, "Negative float: " + str);
        }
S
starlord 已提交
341
    } catch (...) {
Y
yudong.cai 已提交
342
        return Status(SERVER_INVALID_ARGUMENT, "Invalid float: " + str);
Z
zhiru 已提交
343
    }
Y
yudong.cai 已提交
344
    return Status::OK();
Z
zhiru 已提交
345 346
}

S
starlord 已提交
347
Status
S
starlord 已提交
348
ValidationUtil::ValidateDbURI(const std::string& uri) {
Z
zhiru 已提交
349 350 351 352 353 354
    std::string dialectRegex = "(.*)";
    std::string usernameRegex = "(.*)";
    std::string passwordRegex = "(.*)";
    std::string hostRegex = "(.*)";
    std::string portRegex = "(.*)";
    std::string dbNameRegex = "(.*)";
S
starlord 已提交
355 356
    std::string uriRegexStr = dialectRegex + "\\:\\/\\/" + usernameRegex + "\\:" + passwordRegex + "\\@" + hostRegex +
                              "\\:" + portRegex + "\\/" + dbNameRegex;
Z
zhiru 已提交
357 358 359 360 361 362 363 364 365 366 367 368 369
    std::regex uriRegex(uriRegexStr);
    std::smatch pieces_match;

    bool okay = true;

    if (std::regex_match(uri, pieces_match, uriRegex)) {
        std::string dialect = pieces_match[1].str();
        std::transform(dialect.begin(), dialect.end(), dialect.begin(), ::tolower);
        if (dialect.find("mysql") == std::string::npos && dialect.find("sqlite") == std::string::npos) {
            SERVER_LOG_ERROR << "Invalid dialect in URI: dialect = " << dialect;
            okay = false;
        }

S
starlord 已提交
370 371 372 373 374 375 376 377 378 379 380
        /*
         *      Could be DNS, skip checking
         *
                std::string host = pieces_match[4].str();
                if (!host.empty() && host != "localhost") {
                    if (ValidateIpAddress(host) != SERVER_SUCCESS) {
                        SERVER_LOG_ERROR << "Invalid host ip address in uri = " << host;
                        okay = false;
                    }
                }
        */
Z
zhiru 已提交
381 382 383

        std::string port = pieces_match[5].str();
        if (!port.empty()) {
S
starlord 已提交
384 385
            auto status = ValidateStringIsNumber(port);
            if (!status.ok()) {
Z
zhiru 已提交
386 387 388 389
                SERVER_LOG_ERROR << "Invalid port in uri = " << port;
                okay = false;
            }
        }
S
starlord 已提交
390
    } else {
Z
zhiru 已提交
391 392 393 394
        SERVER_LOG_ERROR << "Wrong URI format: URI = " << uri;
        okay = false;
    }

S
starlord 已提交
395
    return (okay ? Status::OK() : Status(SERVER_INVALID_ARGUMENT, "Invalid db backend uri"));
Z
zhiru 已提交
396 397
}

398 399 400 401 402 403 404 405 406 407 408 409 410
Status
ValidationUtil::ValidateStoragePath(const std::string& path) {
    // Validate storage path if is valid, only correct absolute path will be validated pass
    // Invalid path only contain character[a-zA-Z], number[0-9], '-', and '_',
    // and path must start with '/'.
    // examples below are invalid
    // '/a//a', '/a--/a', '/-a/a', '/a@#/a', 'aaa/sfs'
    std::string path_pattern = "^\\/(\\w+-?\\/?)+$";
    std::regex regex(path_pattern);

    return std::regex_match(path, regex) ? Status::OK() : Status(SERVER_INVALID_ARGUMENT, "Invalid file path");
}

S
starlord 已提交
411 412
}  // namespace server
}  // namespace milvus