az: libaz module from ablob
This commit is contained in:
parent
3905c94f03
commit
c0ef07c050
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can use, redistribute, and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License, version 3
|
||||||
|
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TD_AZURE_H_
|
||||||
|
#define _TD_AZURE_H_
|
||||||
|
|
||||||
|
#include "os.h"
|
||||||
|
#include "tarray.h"
|
||||||
|
#include "tdef.h"
|
||||||
|
#include "tlog.h"
|
||||||
|
#include "tmsg.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int32_t azBegin();
|
||||||
|
void azEnd();
|
||||||
|
int32_t azCheckCfg();
|
||||||
|
int32_t azPutObjectFromFileOffset(const char *file, const char *object_name, int64_t offset, int64_t size);
|
||||||
|
int32_t azGetObjectBlock(const char *object_name, int64_t offset, int64_t size, bool check, uint8_t **ppBlock);
|
||||||
|
void azDeleteObjectsByPrefix(const char *prefix);
|
||||||
|
|
||||||
|
int32_t azPutObjectFromFile2(const char *file, const char *object, int8_t withcp);
|
||||||
|
int32_t azGetObjectsByPrefix(const char *prefix, const char *path);
|
||||||
|
int32_t azGetObjectToFile(const char *object_name, const char *fileName);
|
||||||
|
int32_t azDeleteObjects(const char *object_name[], int nobject);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _TD_AZURE_H_
|
|
@ -23,5 +23,5 @@ add_subdirectory(planner)
|
||||||
add_subdirectory(qworker)
|
add_subdirectory(qworker)
|
||||||
add_subdirectory(geometry)
|
add_subdirectory(geometry)
|
||||||
add_subdirectory(command)
|
add_subdirectory(command)
|
||||||
#add_subdirectory(azure)
|
add_subdirectory(azure)
|
||||||
add_subdirectory(tcs)
|
add_subdirectory(tcs)
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#if(${TD_LINUX})
|
||||||
|
aux_source_directory(src AZ_SRC)
|
||||||
|
|
||||||
|
add_library(az STATIC ${AZ_SRC})
|
||||||
|
|
||||||
|
if(${BUILD_S3})
|
||||||
|
add_definitions(-DUSE_S3)
|
||||||
|
target_link_libraries(
|
||||||
|
az
|
||||||
|
PUBLIC _azure_sdk
|
||||||
|
PUBLIC crypt
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
az
|
||||||
|
PUBLIC "${TD_SOURCE_DIR}/include/libs/azure"
|
||||||
|
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
az
|
||||||
|
PUBLIC cjson
|
||||||
|
PUBLIC os
|
||||||
|
PUBLIC util
|
||||||
|
PUBLIC common
|
||||||
|
)
|
||||||
|
|
||||||
|
if(${BUILD_TEST})
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif(${BUILD_TEST})
|
||||||
|
#endif(${TD_LINUX})
|
|
@ -0,0 +1,260 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "azure/storage/blobs/blob_client.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Azure {
|
||||||
|
namespace Storage {
|
||||||
|
namespace Files {
|
||||||
|
namespace DataLake {
|
||||||
|
class FileClient;
|
||||||
|
}
|
||||||
|
} // namespace Files
|
||||||
|
} // namespace Storage
|
||||||
|
} // namespace Azure
|
||||||
|
|
||||||
|
namespace Azure {
|
||||||
|
namespace Storage {
|
||||||
|
namespace Blobs {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The TDBlockBlobClient allows you to manipulate Azure Storage block blobs.
|
||||||
|
*
|
||||||
|
* Block blobs let you upload large blobs efficiently. Block blobs are comprised of blocks, each
|
||||||
|
* of which is identified by a block ID. You create or modify a block blob by writing a set of
|
||||||
|
* blocks and committing them by their block IDs. Each block can be a different size.
|
||||||
|
*
|
||||||
|
* When you upload a block to a blob in your storage account, it is associated with the specified
|
||||||
|
* block blob, but it does not become part of the blob until you commit a list of blocks that
|
||||||
|
* includes the new block's ID. New blocks remain in an uncommitted state until they are
|
||||||
|
* specifically committed or discarded. Writing a block does not update the last modified time of
|
||||||
|
* an existing blob.
|
||||||
|
*/
|
||||||
|
class TDBlockBlobClient final : public BlobClient {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Initialize a new instance of TDBlockBlobClient.
|
||||||
|
*
|
||||||
|
* @param connectionString A connection string includes the authentication information required
|
||||||
|
* for your application to access data in an Azure Storage account at runtime.
|
||||||
|
* @param blobContainerName The name of the container containing this blob.
|
||||||
|
* @param blobName The name of this blob.
|
||||||
|
* @param options Optional client options that define the transport pipeline policies for
|
||||||
|
* authentication, retries, etc., that are applied to every request.
|
||||||
|
* @return A new TDBlockBlobClient instance.
|
||||||
|
*/
|
||||||
|
static TDBlockBlobClient CreateFromConnectionString(const std::string& connectionString,
|
||||||
|
const std::string& blobContainerName, const std::string& blobName,
|
||||||
|
const BlobClientOptions& options = BlobClientOptions());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a new instance of TDBlockBlobClient.
|
||||||
|
*
|
||||||
|
* @param blobUrl A URL
|
||||||
|
* referencing the blob that includes the name of the account, the name of the container, and
|
||||||
|
* the name of the blob.
|
||||||
|
* @param credential The shared key credential used to sign
|
||||||
|
* requests.
|
||||||
|
* @param options Optional client options that define the transport pipeline
|
||||||
|
* policies for authentication, retries, etc., that are applied to every request.
|
||||||
|
*/
|
||||||
|
explicit TDBlockBlobClient(const std::string& blobUrl, std::shared_ptr<StorageSharedKeyCredential> credential,
|
||||||
|
const BlobClientOptions& options = BlobClientOptions());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a new instance of TDBlockBlobClient.
|
||||||
|
*
|
||||||
|
* @param blobUrl A URL
|
||||||
|
* referencing the blob that includes the name of the account, the name of the container, and
|
||||||
|
* the name of the blob.
|
||||||
|
* @param credential The token credential used to sign requests.
|
||||||
|
* @param options Optional client options that define the transport pipeline policies for
|
||||||
|
* authentication, retries, etc., that are applied to every request.
|
||||||
|
*/
|
||||||
|
explicit TDBlockBlobClient(const std::string& blobUrl, std::shared_ptr<Core::Credentials::TokenCredential> credential,
|
||||||
|
const BlobClientOptions& options = BlobClientOptions());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a new instance of TDBlockBlobClient.
|
||||||
|
*
|
||||||
|
* @param blobUrl A URL
|
||||||
|
* referencing the blob that includes the name of the account, the name of the container, and
|
||||||
|
* the name of the blob, and possibly also a SAS token.
|
||||||
|
* @param options Optional client
|
||||||
|
* options that define the transport pipeline policies for authentication, retries, etc., that
|
||||||
|
* are applied to every request.
|
||||||
|
*/
|
||||||
|
explicit TDBlockBlobClient(const std::string& blobUrl, const BlobClientOptions& options = BlobClientOptions());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes a new instance of the TDBlockBlobClient class with an identical URL
|
||||||
|
* source but the specified snapshot timestamp.
|
||||||
|
*
|
||||||
|
* @param snapshot The snapshot
|
||||||
|
* identifier.
|
||||||
|
* @return A new TDBlockBlobClient instance.
|
||||||
|
* @remarks Pass empty string to remove the snapshot returning the base blob.
|
||||||
|
*/
|
||||||
|
TDBlockBlobClient WithSnapshot(const std::string& snapshot) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a clone of this instance that references a version ID rather than the base
|
||||||
|
* blob.
|
||||||
|
*
|
||||||
|
* @param versionId The version ID returning a URL to the base blob.
|
||||||
|
* @return A new TDBlockBlobClient instance.
|
||||||
|
* @remarks Pass empty string to remove the version ID returning the base blob.
|
||||||
|
*/
|
||||||
|
TDBlockBlobClient WithVersionId(const std::string& versionId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a new block blob, or updates the content of an existing block blob. Updating
|
||||||
|
* an existing block blob overwrites any existing metadata on the blob.
|
||||||
|
*
|
||||||
|
* @param content A BodyStream containing the content to upload.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A UploadBlockBlobResult describing the state of the updated block blob.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::UploadBlockBlobResult> Upload(
|
||||||
|
Azure::Core::IO::BodyStream& content, const UploadBlockBlobOptions& options = UploadBlockBlobOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a new block blob, or updates the content of an existing block blob. Updating
|
||||||
|
* an existing block blob overwrites any existing metadata on the blob.
|
||||||
|
*
|
||||||
|
* @param buffer A memory buffer containing the content to upload.
|
||||||
|
* @param bufferSize Size of the memory buffer.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A UploadBlockBlobFromResult describing the state of the updated block blob.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromResult> UploadFrom(
|
||||||
|
const uint8_t* buffer, size_t bufferSize,
|
||||||
|
const UploadBlockBlobFromOptions& options = UploadBlockBlobFromOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a new block blob, or updates the content of an existing block blob. Updating
|
||||||
|
* an existing block blob overwrites any existing metadata on the blob.
|
||||||
|
*
|
||||||
|
* @param fileName A file containing the content to upload.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A UploadBlockBlobFromResult describing the state of the updated block blob.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromResult> UploadFrom(
|
||||||
|
const std::string& fileName, const UploadBlockBlobFromOptions& options = UploadBlockBlobFromOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromResult> UploadFrom(
|
||||||
|
const std::string& fileName, int64_t offset, int64_t size,
|
||||||
|
const UploadBlockBlobFromOptions& options = UploadBlockBlobFromOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a new Block Blob where the contents of the blob are read from a given URL.
|
||||||
|
*
|
||||||
|
* @param sourceUri Specifies the URL of the source blob.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A UploadBlockBlobFromUriResult describing the state of the updated block blob.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromUriResult> UploadFromUri(
|
||||||
|
const std::string& sourceUri, const UploadBlockBlobFromUriOptions& options = UploadBlockBlobFromUriOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a new block as part of a block blob's staging area to be eventually
|
||||||
|
* committed via the CommitBlockList operation.
|
||||||
|
*
|
||||||
|
* @param blockId A valid Base64 string value that identifies the block. Prior to encoding, the
|
||||||
|
* string must be less than or equal to 64 bytes in size.
|
||||||
|
* @param content A BodyStream containing the content to upload.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A StageBlockResult describing the state of the updated block.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::StageBlockResult> StageBlock(
|
||||||
|
const std::string& blockId, Azure::Core::IO::BodyStream& content,
|
||||||
|
const StageBlockOptions& options = StageBlockOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a new block to be committed as part of a blob where the contents are read from
|
||||||
|
* the sourceUri.
|
||||||
|
*
|
||||||
|
* @param blockId A valid Base64 string value that identifies the block. Prior to encoding, the
|
||||||
|
* string must be less than or equal to 64 bytes in size.
|
||||||
|
* @param sourceUri Specifies the uri of the source
|
||||||
|
* blob. The value may be a uri of up to 2 KB in length that specifies a blob. The source blob
|
||||||
|
* must either be public or must be authenticated via a shared access signature. If the source
|
||||||
|
* blob is public, no authentication is required to perform the operation.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A StageBlockFromUriResult describing the state of the updated block blob.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::StageBlockFromUriResult> StageBlockFromUri(
|
||||||
|
const std::string& blockId, const std::string& sourceUri,
|
||||||
|
const StageBlockFromUriOptions& options = StageBlockFromUriOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes a blob by specifying the list of block IDs that make up the blob. In order to
|
||||||
|
* be written as part of a blob, a block must have been successfully written to the server in a
|
||||||
|
* prior StageBlock operation. You can call CommitBlockList to update a blob by uploading only
|
||||||
|
* those blocks that have changed, then committing the new and existing blocks together. You can
|
||||||
|
* do this by specifying whether to commit a block from the committed block list or from the
|
||||||
|
* uncommitted block list, or to commit the most recently uploaded version of the block,
|
||||||
|
* whichever list it may belong to.
|
||||||
|
*
|
||||||
|
* @param blockIds Base64 encoded block IDs to indicate that make up the blob.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A CommitBlobBlockListResult describing the state of the updated block blob.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::CommitBlockListResult> CommitBlockList(
|
||||||
|
const std::vector<std::string>& blockIds, const CommitBlockListOptions& options = CommitBlockListOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the list of blocks that have been uploaded as part of a block blob. There
|
||||||
|
* are two block lists maintained for a blob. The Committed Block list has blocks that have been
|
||||||
|
* successfully committed to a given blob with CommitBlockList. The Uncommitted Block list has
|
||||||
|
* blocks that have been uploaded for a blob using StageBlock, but that have not yet been
|
||||||
|
* committed.
|
||||||
|
*
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A GetBlobBlockListResult describing requested block list.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::GetBlockListResult> GetBlockList(
|
||||||
|
const GetBlockListOptions& options = GetBlockListOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the result of a query against the blob.
|
||||||
|
*
|
||||||
|
* @param querySqlExpression The query expression in SQL.
|
||||||
|
* @param options Optional parameters to execute this function.
|
||||||
|
* @param context Context for cancelling long running operations.
|
||||||
|
* @return A QueryBlobResult describing the query result.
|
||||||
|
*/
|
||||||
|
Azure::Response<Models::QueryBlobResult> Query(const std::string& querySqlExpression,
|
||||||
|
const QueryBlobOptions& options = QueryBlobOptions(),
|
||||||
|
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||||
|
|
||||||
|
explicit TDBlockBlobClient(BlobClient blobClient);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class BlobClient;
|
||||||
|
friend class Files::DataLake::DataLakeFileClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Blobs
|
||||||
|
} // namespace Storage
|
||||||
|
} // namespace Azure
|
|
@ -0,0 +1,531 @@
|
||||||
|
#if defined(USE_S3)
|
||||||
|
#include "avro_parser.hpp"
|
||||||
|
|
||||||
|
#include <azure/core/azure_assert.hpp>
|
||||||
|
#include <azure/core/internal/json/json.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace Azure {
|
||||||
|
namespace Storage {
|
||||||
|
namespace Blobs {
|
||||||
|
namespace _detail {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int64_t parseInt(AvroStreamReader::ReaderPos& data) {
|
||||||
|
uint64_t r = 0;
|
||||||
|
int nb = 0;
|
||||||
|
while (true) {
|
||||||
|
uint8_t c = (*data.BufferPtr)[data.Offset++];
|
||||||
|
r = r | ((static_cast<uint64_t>(c) & 0x7f) << (nb * 7));
|
||||||
|
if (c & 0x80) {
|
||||||
|
++nb;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return static_cast<int64_t>(r >> 1) ^ -static_cast<int64_t>(r & 0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
AvroSchema ParseSchemaFromJsonString(const std::string& jsonSchema) {
|
||||||
|
const static std::map<std::string, AvroSchema> BuiltinNameSchemaMap = {
|
||||||
|
{"string", AvroSchema::StringSchema}, {"bytes", AvroSchema::BytesSchema}, {"int", AvroSchema::IntSchema},
|
||||||
|
{"long", AvroSchema::LongSchema}, {"float", AvroSchema::FloatSchema}, {"double", AvroSchema::DoubleSchema},
|
||||||
|
{"boolean", AvroSchema::BoolSchema}, {"null", AvroSchema::NullSchema}, {"string", AvroSchema::StringSchema},
|
||||||
|
};
|
||||||
|
std::map<std::string, AvroSchema> nameSchemaMap = BuiltinNameSchemaMap;
|
||||||
|
|
||||||
|
std::function<AvroSchema(const Core::Json::_internal::json& obj)> parseSchemaFromJsonObject;
|
||||||
|
parseSchemaFromJsonObject = [&](const Core::Json::_internal::json& obj) -> AvroSchema {
|
||||||
|
if (obj.is_string()) {
|
||||||
|
auto typeName = obj.get<std::string>();
|
||||||
|
return nameSchemaMap.find(typeName)->second;
|
||||||
|
} else if (obj.is_array()) {
|
||||||
|
std::vector<AvroSchema> unionSchemas;
|
||||||
|
for (const auto& s : obj) {
|
||||||
|
unionSchemas.push_back(parseSchemaFromJsonObject(s));
|
||||||
|
}
|
||||||
|
return AvroSchema::UnionSchema(std::move(unionSchemas));
|
||||||
|
} else if (obj.is_object()) {
|
||||||
|
if (obj.count("namespace") != 0) {
|
||||||
|
throw std::runtime_error("Namespace isn't supported yet in Avro schema.");
|
||||||
|
}
|
||||||
|
if (obj.count("aliases") != 0) {
|
||||||
|
throw std::runtime_error("Alias isn't supported yet in Avro schema.");
|
||||||
|
}
|
||||||
|
auto typeName = obj["type"].get<std::string>();
|
||||||
|
auto i = nameSchemaMap.find(typeName);
|
||||||
|
if (i != nameSchemaMap.end()) {
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
if (typeName == "record") {
|
||||||
|
std::vector<std::pair<std::string, AvroSchema>> fieldsSchema;
|
||||||
|
for (const auto& field : obj["fields"]) {
|
||||||
|
fieldsSchema.push_back(
|
||||||
|
std::make_pair(field["name"].get<std::string>(), parseSchemaFromJsonObject(field["type"])));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string recordName = obj["name"].get<std::string>();
|
||||||
|
auto recordSchema = AvroSchema::RecordSchema(recordName, std::move(fieldsSchema));
|
||||||
|
nameSchemaMap.insert(std::make_pair(recordName, recordSchema));
|
||||||
|
return recordSchema;
|
||||||
|
} else if (typeName == "enum") {
|
||||||
|
throw std::runtime_error("Enum type isn't supported yet in Avro schema.");
|
||||||
|
} else if (typeName == "array") {
|
||||||
|
return AvroSchema::ArraySchema(parseSchemaFromJsonObject(obj["items"]));
|
||||||
|
} else if (typeName == "map") {
|
||||||
|
return AvroSchema::MapSchema(parseSchemaFromJsonObject(obj["items"]));
|
||||||
|
} else if (typeName == "fixed") {
|
||||||
|
const std::string fixedName = obj["name"].get<std::string>();
|
||||||
|
auto fixedSchema = AvroSchema::FixedSchema(fixedName, obj["size"].get<int64_t>());
|
||||||
|
nameSchemaMap.insert(std::make_pair(fixedName, fixedSchema));
|
||||||
|
return fixedSchema;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Unrecognized type " + typeName + " in Avro schema.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto jsonRoot = Core::Json::_internal::json::parse(jsonSchema.begin(), jsonSchema.end());
|
||||||
|
return parseSchemaFromJsonObject(jsonRoot);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int64_t AvroStreamReader::ParseInt(const Core::Context& context) {
|
||||||
|
uint64_t r = 0;
|
||||||
|
int nb = 0;
|
||||||
|
while (true) {
|
||||||
|
Preload(1, context);
|
||||||
|
uint8_t c = m_streambuffer[m_pos.Offset++];
|
||||||
|
|
||||||
|
r = r | ((static_cast<uint64_t>(c) & 0x7f) << (nb * 7));
|
||||||
|
if (c & 0x80) {
|
||||||
|
++nb;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return static_cast<int64_t>(r >> 1) ^ -static_cast<int64_t>(r & 0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvroStreamReader::Advance(size_t n, const Core::Context& context) {
|
||||||
|
Preload(n, context);
|
||||||
|
m_pos.Offset += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AvroStreamReader::Preload(size_t n, const Core::Context& context) {
|
||||||
|
size_t oldAvailable = AvailableBytes();
|
||||||
|
while (true) {
|
||||||
|
size_t newAvailable = TryPreload(n, context);
|
||||||
|
if (newAvailable >= n) {
|
||||||
|
return newAvailable;
|
||||||
|
}
|
||||||
|
if (oldAvailable == newAvailable) {
|
||||||
|
throw std::runtime_error("Unexpected EOF of Avro stream.");
|
||||||
|
}
|
||||||
|
oldAvailable = newAvailable;
|
||||||
|
}
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AvroStreamReader::TryPreload(size_t n, const Core::Context& context) {
|
||||||
|
size_t availableBytes = AvailableBytes();
|
||||||
|
if (availableBytes >= n) {
|
||||||
|
return availableBytes;
|
||||||
|
}
|
||||||
|
const size_t MinRead = 4096;
|
||||||
|
size_t tryReadSize = (std::max)(n, MinRead);
|
||||||
|
size_t currSize = m_streambuffer.size();
|
||||||
|
m_streambuffer.resize(m_streambuffer.size() + tryReadSize);
|
||||||
|
size_t actualReadSize = m_stream->Read(m_streambuffer.data() + currSize, tryReadSize, context);
|
||||||
|
m_streambuffer.resize(currSize + actualReadSize);
|
||||||
|
return AvailableBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvroStreamReader::Discard() {
|
||||||
|
constexpr size_t MinimumReleaseMemory = 128 * 1024;
|
||||||
|
if (m_pos.Offset < MinimumReleaseMemory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const size_t availableBytes = AvailableBytes();
|
||||||
|
std::memmove(&m_streambuffer[0], &m_streambuffer[m_pos.Offset], availableBytes);
|
||||||
|
m_streambuffer.resize(availableBytes);
|
||||||
|
m_pos.Offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AvroSchema AvroSchema::StringSchema(AvroDatumType::String);
|
||||||
|
const AvroSchema AvroSchema::BytesSchema(AvroDatumType::Bytes);
|
||||||
|
const AvroSchema AvroSchema::IntSchema(AvroDatumType::Int);
|
||||||
|
const AvroSchema AvroSchema::LongSchema(AvroDatumType::Long);
|
||||||
|
const AvroSchema AvroSchema::FloatSchema(AvroDatumType::Float);
|
||||||
|
const AvroSchema AvroSchema::DoubleSchema(AvroDatumType::Double);
|
||||||
|
const AvroSchema AvroSchema::BoolSchema(AvroDatumType::Bool);
|
||||||
|
const AvroSchema AvroSchema::NullSchema(AvroDatumType::Null);
|
||||||
|
|
||||||
|
AvroSchema AvroSchema::RecordSchema(std::string name,
|
||||||
|
const std::vector<std::pair<std::string, AvroSchema>>& fieldsSchema) {
|
||||||
|
AvroSchema recordSchema(AvroDatumType::Record);
|
||||||
|
recordSchema.m_name = std::move(name);
|
||||||
|
recordSchema.m_status = std::make_shared<SharedStatus>();
|
||||||
|
for (auto& i : fieldsSchema) {
|
||||||
|
recordSchema.m_status->m_keys.push_back(i.first);
|
||||||
|
recordSchema.m_status->m_schemas.push_back(i.second);
|
||||||
|
}
|
||||||
|
return recordSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvroSchema AvroSchema::ArraySchema(AvroSchema elementSchema) {
|
||||||
|
AvroSchema arraySchema(AvroDatumType::Array);
|
||||||
|
arraySchema.m_status = std::make_shared<SharedStatus>();
|
||||||
|
arraySchema.m_status->m_schemas.push_back(std::move(elementSchema));
|
||||||
|
return arraySchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvroSchema AvroSchema::MapSchema(AvroSchema elementSchema) {
|
||||||
|
AvroSchema mapSchema(AvroDatumType::Map);
|
||||||
|
mapSchema.m_status = std::make_shared<SharedStatus>();
|
||||||
|
mapSchema.m_status->m_schemas.push_back(std::move(elementSchema));
|
||||||
|
return mapSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvroSchema AvroSchema::UnionSchema(std::vector<AvroSchema> schemas) {
|
||||||
|
AvroSchema unionSchema(AvroDatumType::Union);
|
||||||
|
unionSchema.m_status = std::make_shared<SharedStatus>();
|
||||||
|
unionSchema.m_status->m_schemas = std::move(schemas);
|
||||||
|
return unionSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvroSchema AvroSchema::FixedSchema(std::string name, int64_t size) {
|
||||||
|
AvroSchema fixedSchema(AvroDatumType::Fixed);
|
||||||
|
fixedSchema.m_name = std::move(name);
|
||||||
|
fixedSchema.m_status = std::make_shared<SharedStatus>();
|
||||||
|
fixedSchema.m_status->m_size = size;
|
||||||
|
return fixedSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvroDatum::Fill(AvroStreamReader& reader, const Core::Context& context) {
|
||||||
|
m_data = reader.m_pos;
|
||||||
|
if (m_schema.Type() == AvroDatumType::String || m_schema.Type() == AvroDatumType::Bytes) {
|
||||||
|
int64_t stringSize = reader.ParseInt(context);
|
||||||
|
reader.Advance(static_cast<size_t>(stringSize), context);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Int || m_schema.Type() == AvroDatumType::Long ||
|
||||||
|
m_schema.Type() == AvroDatumType::Enum) {
|
||||||
|
reader.ParseInt(context);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Float) {
|
||||||
|
reader.Advance(4, context);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Double) {
|
||||||
|
reader.Advance(8, context);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Bool) {
|
||||||
|
reader.Advance(1, context);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Null) {
|
||||||
|
reader.Advance(0, context);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Record) {
|
||||||
|
for (const auto& s : m_schema.FieldSchemas()) {
|
||||||
|
AvroDatum(s).Fill(reader, context);
|
||||||
|
}
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Array) {
|
||||||
|
while (true) {
|
||||||
|
int64_t numElementsInBlock = reader.ParseInt(context);
|
||||||
|
if (numElementsInBlock == 0) {
|
||||||
|
break;
|
||||||
|
} else if (numElementsInBlock < 0) {
|
||||||
|
int64_t blockSize = reader.ParseInt(context);
|
||||||
|
reader.Advance(static_cast<size_t>(blockSize), context);
|
||||||
|
} else {
|
||||||
|
for (auto i = 0; i < numElementsInBlock; ++i) {
|
||||||
|
AvroDatum(m_schema.ItemSchema()).Fill(reader, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Map) {
|
||||||
|
while (true) {
|
||||||
|
int64_t numElementsInBlock = reader.ParseInt(context);
|
||||||
|
if (numElementsInBlock == 0) {
|
||||||
|
break;
|
||||||
|
} else if (numElementsInBlock < 0) {
|
||||||
|
int64_t blockSize = reader.ParseInt(context);
|
||||||
|
reader.Advance(static_cast<size_t>(blockSize), context);
|
||||||
|
} else {
|
||||||
|
for (int64_t i = 0; i < numElementsInBlock; ++i) {
|
||||||
|
AvroDatum(AvroSchema::StringSchema).Fill(reader, context);
|
||||||
|
AvroDatum(m_schema.ItemSchema()).Fill(reader, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Union) {
|
||||||
|
int64_t i = reader.ParseInt(context);
|
||||||
|
AvroDatum(m_schema.FieldSchemas()[static_cast<size_t>(i)]).Fill(reader, context);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Fixed) {
|
||||||
|
reader.Advance(m_schema.Size(), context);
|
||||||
|
} else {
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvroDatum::Fill(AvroStreamReader::ReaderPos& data) {
|
||||||
|
m_data = data;
|
||||||
|
if (m_schema.Type() == AvroDatumType::String || m_schema.Type() == AvroDatumType::Bytes) {
|
||||||
|
int64_t stringSize = parseInt(data);
|
||||||
|
data.Offset += static_cast<size_t>(stringSize);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Int || m_schema.Type() == AvroDatumType::Long ||
|
||||||
|
m_schema.Type() == AvroDatumType::Enum) {
|
||||||
|
parseInt(data);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Float) {
|
||||||
|
data.Offset += 4;
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Double) {
|
||||||
|
data.Offset += 8;
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Bool) {
|
||||||
|
data.Offset += 1;
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Null) {
|
||||||
|
data.Offset += 0;
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Record) {
|
||||||
|
for (const auto& s : m_schema.FieldSchemas()) {
|
||||||
|
AvroDatum(s).Fill(data);
|
||||||
|
}
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Array) {
|
||||||
|
while (true) {
|
||||||
|
int64_t numElementsInBlock = parseInt(data);
|
||||||
|
if (numElementsInBlock == 0) {
|
||||||
|
break;
|
||||||
|
} else if (numElementsInBlock < 0) {
|
||||||
|
int64_t blockSize = parseInt(data);
|
||||||
|
data.Offset += static_cast<size_t>(blockSize);
|
||||||
|
} else {
|
||||||
|
for (auto i = 0; i < numElementsInBlock; ++i) {
|
||||||
|
AvroDatum(m_schema.ItemSchema()).Fill(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Map) {
|
||||||
|
while (true) {
|
||||||
|
int64_t numElementsInBlock = parseInt(data);
|
||||||
|
if (numElementsInBlock == 0) {
|
||||||
|
break;
|
||||||
|
} else if (numElementsInBlock < 0) {
|
||||||
|
int64_t blockSize = parseInt(data);
|
||||||
|
data.Offset += static_cast<size_t>(blockSize);
|
||||||
|
} else {
|
||||||
|
for (int64_t i = 0; i < numElementsInBlock; ++i) {
|
||||||
|
AvroDatum(AvroSchema::StringSchema).Fill(data);
|
||||||
|
AvroDatum(m_schema.ItemSchema()).Fill(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Union) {
|
||||||
|
int64_t i = parseInt(data);
|
||||||
|
AvroDatum(m_schema.FieldSchemas()[static_cast<size_t>(i)]).Fill(data);
|
||||||
|
} else if (m_schema.Type() == AvroDatumType::Fixed) {
|
||||||
|
data.Offset += m_schema.Size();
|
||||||
|
} else {
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
AvroDatum::StringView AvroDatum::Value() const {
|
||||||
|
auto data = m_data;
|
||||||
|
if (m_schema.Type() == AvroDatumType::String || m_schema.Type() == AvroDatumType::Bytes) {
|
||||||
|
const int64_t length = parseInt(data);
|
||||||
|
const uint8_t* start = &(*data.BufferPtr)[data.Offset];
|
||||||
|
StringView ret{start, static_cast<size_t>(length)};
|
||||||
|
data.Offset += static_cast<size_t>(length);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (m_schema.Type() == AvroDatumType::Fixed) {
|
||||||
|
const size_t fixedSize = m_schema.Size();
|
||||||
|
const uint8_t* start = &(*data.BufferPtr)[data.Offset];
|
||||||
|
StringView ret{start, fixedSize};
|
||||||
|
data.Offset += fixedSize;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string AvroDatum::Value() const {
|
||||||
|
auto stringView = Value<StringView>();
|
||||||
|
return std::string(stringView.Data, stringView.Data + stringView.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::vector<uint8_t> AvroDatum::Value() const {
|
||||||
|
auto stringView = Value<StringView>();
|
||||||
|
return std::vector<uint8_t>(stringView.Data, stringView.Data + stringView.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
int64_t AvroDatum::Value() const {
|
||||||
|
auto data = m_data;
|
||||||
|
return parseInt(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
int32_t AvroDatum::Value() const {
|
||||||
|
return static_cast<int32_t>(Value<int64_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool AvroDatum::Value() const {
|
||||||
|
return Value<int64_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::nullptr_t AvroDatum::Value() const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
AvroRecord AvroDatum::Value() const {
|
||||||
|
auto data = m_data;
|
||||||
|
|
||||||
|
AvroRecord r;
|
||||||
|
r.m_keys = &m_schema.FieldNames();
|
||||||
|
for (const auto& schema : m_schema.FieldSchemas()) {
|
||||||
|
auto datum = AvroDatum(schema);
|
||||||
|
datum.Fill(data);
|
||||||
|
r.m_values.push_back(std::move(datum));
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
AvroMap AvroDatum::Value() const {
|
||||||
|
auto data = m_data;
|
||||||
|
|
||||||
|
AvroMap m;
|
||||||
|
while (true) {
|
||||||
|
int64_t numElementsInBlock = parseInt(data);
|
||||||
|
if (numElementsInBlock == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (numElementsInBlock < 0) {
|
||||||
|
numElementsInBlock = -numElementsInBlock;
|
||||||
|
parseInt(data);
|
||||||
|
}
|
||||||
|
for (int64_t i = 0; i < numElementsInBlock; ++i) {
|
||||||
|
auto keyDatum = AvroDatum(AvroSchema::StringSchema);
|
||||||
|
keyDatum.Fill(data);
|
||||||
|
auto valueDatum = AvroDatum(m_schema.ItemSchema());
|
||||||
|
valueDatum.Fill(data);
|
||||||
|
m[keyDatum.Value<std::string>()] = valueDatum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
AvroDatum AvroDatum::Value() const {
|
||||||
|
auto data = m_data;
|
||||||
|
if (m_schema.Type() == AvroDatumType::Union) {
|
||||||
|
int64_t i = parseInt(data);
|
||||||
|
auto datum = AvroDatum(m_schema.FieldSchemas()[static_cast<size_t>(i)]);
|
||||||
|
datum.Fill(data);
|
||||||
|
return datum;
|
||||||
|
}
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
}
|
||||||
|
|
||||||
|
AvroObjectContainerReader::AvroObjectContainerReader(Core::IO::BodyStream& stream)
|
||||||
|
: m_reader(std::make_unique<AvroStreamReader>(stream)) {}
|
||||||
|
|
||||||
|
AvroDatum AvroObjectContainerReader::NextImpl(const AvroSchema* schema, const Core::Context& context) {
|
||||||
|
AZURE_ASSERT_FALSE(m_eof);
|
||||||
|
static const auto SyncMarkerSchema = AvroSchema::FixedSchema("Sync", 16);
|
||||||
|
if (!schema) {
|
||||||
|
static AvroSchema FileHeaderSchema = []() {
|
||||||
|
std::vector<std::pair<std::string, AvroSchema>> fieldsSchema;
|
||||||
|
fieldsSchema.push_back(std::make_pair("magic", AvroSchema::FixedSchema("Magic", 4)));
|
||||||
|
fieldsSchema.push_back(std::make_pair("meta", AvroSchema::MapSchema(AvroSchema::BytesSchema)));
|
||||||
|
fieldsSchema.push_back(std::make_pair("sync", SyncMarkerSchema));
|
||||||
|
return AvroSchema::RecordSchema("org.apache.avro.file.Header", std::move(fieldsSchema));
|
||||||
|
}();
|
||||||
|
auto fileHeaderDatum = AvroDatum(FileHeaderSchema);
|
||||||
|
fileHeaderDatum.Fill(*m_reader, context);
|
||||||
|
auto fileHeader = fileHeaderDatum.Value<AvroRecord>();
|
||||||
|
if (fileHeader.Field("magic").Value<std::string>() != "Obj\01") {
|
||||||
|
throw std::runtime_error("Invalid Avro object container magic.");
|
||||||
|
}
|
||||||
|
AvroMap meta = fileHeader.Field("meta").Value<AvroMap>();
|
||||||
|
std::string objectSchemaJson = meta["avro.schema"].Value<std::string>();
|
||||||
|
std::string codec = "null";
|
||||||
|
if (meta.count("avro.codec") != 0) {
|
||||||
|
codec = meta["avro.codec"].Value<std::string>();
|
||||||
|
}
|
||||||
|
if (codec != "null") {
|
||||||
|
throw std::runtime_error("Unsupported Avro codec: " + codec);
|
||||||
|
}
|
||||||
|
m_syncMarker = fileHeader.Field("sync").Value<std::string>();
|
||||||
|
m_objectSchema = std::make_unique<AvroSchema>(ParseSchemaFromJsonString(objectSchemaJson));
|
||||||
|
schema = m_objectSchema.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_remainingObjectInCurrentBlock == 0) {
|
||||||
|
m_reader->Discard();
|
||||||
|
m_remainingObjectInCurrentBlock = m_reader->ParseInt(context);
|
||||||
|
int64_t ObjectsSize = m_reader->ParseInt(context);
|
||||||
|
m_reader->Preload(static_cast<size_t>(ObjectsSize), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto objectDatum = AvroDatum(*m_objectSchema);
|
||||||
|
objectDatum.Fill(*m_reader, context);
|
||||||
|
if (--m_remainingObjectInCurrentBlock == 0) {
|
||||||
|
auto markerDatum = AvroDatum(SyncMarkerSchema);
|
||||||
|
markerDatum.Fill(*m_reader, context);
|
||||||
|
auto marker = markerDatum.Value<std::string>();
|
||||||
|
if (marker != m_syncMarker) {
|
||||||
|
throw std::runtime_error("Sync marker doesn't match.");
|
||||||
|
}
|
||||||
|
m_eof = m_reader->TryPreload(1, context) == 0;
|
||||||
|
}
|
||||||
|
return objectDatum;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AvroStreamParser::OnRead(uint8_t* buffer, size_t count, Azure::Core::Context const& context) {
|
||||||
|
if (m_parserBuffer.Length != 0) {
|
||||||
|
size_t bytesToCopy = (std::min)(m_parserBuffer.Length, count);
|
||||||
|
std::memcpy(buffer, m_parserBuffer.Data, bytesToCopy);
|
||||||
|
m_parserBuffer.Data += bytesToCopy;
|
||||||
|
m_parserBuffer.Length -= bytesToCopy;
|
||||||
|
return bytesToCopy;
|
||||||
|
}
|
||||||
|
while (!m_parser.End()) {
|
||||||
|
auto datum = m_parser.Next(context);
|
||||||
|
if (datum.Schema().Type() == AvroDatumType::Union) {
|
||||||
|
datum = datum.Value<AvroDatum>();
|
||||||
|
}
|
||||||
|
if (datum.Schema().Type() != AvroDatumType::Record) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (datum.Schema().Name() == "com.microsoft.azure.storage.queryBlobContents.resultData") {
|
||||||
|
auto record = datum.Value<AvroRecord>();
|
||||||
|
auto dataDatum = record.Field("data");
|
||||||
|
m_parserBuffer = dataDatum.Value<AvroDatum::StringView>();
|
||||||
|
return OnRead(buffer, count, context);
|
||||||
|
}
|
||||||
|
if (datum.Schema().Name() == "com.microsoft.azure.storage.queryBlobContents.progress" && m_progressCallback) {
|
||||||
|
auto record = datum.Value<AvroRecord>();
|
||||||
|
auto bytesScanned = record.Field("bytesScanned").Value<int64_t>();
|
||||||
|
auto totalBytes = record.Field("totalBytes").Value<int64_t>();
|
||||||
|
m_progressCallback(bytesScanned, totalBytes);
|
||||||
|
}
|
||||||
|
if (datum.Schema().Name() == "com.microsoft.azure.storage.queryBlobContents.error" && m_errorCallback) {
|
||||||
|
auto record = datum.Value<AvroRecord>();
|
||||||
|
BlobQueryError e;
|
||||||
|
e.Name = record.Field("name").Value<std::string>();
|
||||||
|
e.Description = record.Field("description").Value<std::string>();
|
||||||
|
e.IsFatal = record.Field("fatal").Value<bool>();
|
||||||
|
e.Position = record.Field("position").Value<int64_t>();
|
||||||
|
m_errorCallback(std::move(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace _detail
|
||||||
|
} // namespace Blobs
|
||||||
|
} // namespace Storage
|
||||||
|
} // namespace Azure
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,198 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "azure/storage/blobs/blob_options.hpp"
|
||||||
|
|
||||||
|
#include <azure/core/io/body_stream.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Azure { namespace Storage { namespace Blobs { namespace _detail {
|
||||||
|
enum class AvroDatumType
|
||||||
|
{
|
||||||
|
String,
|
||||||
|
Bytes,
|
||||||
|
Int,
|
||||||
|
Long,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
|
Bool,
|
||||||
|
Null,
|
||||||
|
Record,
|
||||||
|
Enum,
|
||||||
|
Array,
|
||||||
|
Map,
|
||||||
|
Union,
|
||||||
|
Fixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvroStreamReader final {
|
||||||
|
public:
|
||||||
|
// position of a vector that lives through vector resizing
|
||||||
|
struct ReaderPos final
|
||||||
|
{
|
||||||
|
const std::vector<uint8_t>* BufferPtr = nullptr;
|
||||||
|
size_t Offset = 0;
|
||||||
|
};
|
||||||
|
explicit AvroStreamReader(Core::IO::BodyStream& stream)
|
||||||
|
: m_stream(&stream), m_pos{&m_streambuffer, 0}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
AvroStreamReader(const AvroStreamReader&) = delete;
|
||||||
|
AvroStreamReader& operator=(const AvroStreamReader&) = delete;
|
||||||
|
|
||||||
|
int64_t ParseInt(const Core::Context& context);
|
||||||
|
void Advance(size_t n, const Core::Context& context);
|
||||||
|
// Read at least n bytes from m_stream and append data to m_streambuffer. Return number of bytes
|
||||||
|
// available in m_streambuffer;
|
||||||
|
size_t Preload(size_t n, const Core::Context& context);
|
||||||
|
size_t TryPreload(size_t n, const Core::Context& context);
|
||||||
|
// discards data that's before m_pos
|
||||||
|
void Discard();
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t AvailableBytes() const { return m_streambuffer.size() - m_pos.Offset; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Core::IO::BodyStream* m_stream;
|
||||||
|
std::vector<uint8_t> m_streambuffer;
|
||||||
|
ReaderPos m_pos;
|
||||||
|
|
||||||
|
friend class AvroDatum;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvroSchema final {
|
||||||
|
public:
|
||||||
|
static const AvroSchema StringSchema;
|
||||||
|
static const AvroSchema BytesSchema;
|
||||||
|
static const AvroSchema IntSchema;
|
||||||
|
static const AvroSchema LongSchema;
|
||||||
|
static const AvroSchema FloatSchema;
|
||||||
|
static const AvroSchema DoubleSchema;
|
||||||
|
static const AvroSchema BoolSchema;
|
||||||
|
static const AvroSchema NullSchema;
|
||||||
|
static AvroSchema RecordSchema(
|
||||||
|
std::string name,
|
||||||
|
const std::vector<std::pair<std::string, AvroSchema>>& fieldsSchema);
|
||||||
|
static AvroSchema ArraySchema(AvroSchema elementSchema);
|
||||||
|
static AvroSchema MapSchema(AvroSchema elementSchema);
|
||||||
|
static AvroSchema UnionSchema(std::vector<AvroSchema> schemas);
|
||||||
|
static AvroSchema FixedSchema(std::string name, int64_t size);
|
||||||
|
|
||||||
|
const std::string& Name() const { return m_name; }
|
||||||
|
AvroDatumType Type() const { return m_type; }
|
||||||
|
const std::vector<std::string>& FieldNames() const { return m_status->m_keys; }
|
||||||
|
AvroSchema ItemSchema() const { return m_status->m_schemas[0]; }
|
||||||
|
const std::vector<AvroSchema>& FieldSchemas() const { return m_status->m_schemas; }
|
||||||
|
size_t Size() const { return static_cast<size_t>(m_status->m_size); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit AvroSchema(AvroDatumType type) : m_type(type) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AvroDatumType m_type;
|
||||||
|
std::string m_name;
|
||||||
|
|
||||||
|
struct SharedStatus
|
||||||
|
{
|
||||||
|
std::vector<std::string> m_keys;
|
||||||
|
std::vector<AvroSchema> m_schemas;
|
||||||
|
int64_t m_size = 0;
|
||||||
|
};
|
||||||
|
std::shared_ptr<SharedStatus> m_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvroDatum final {
|
||||||
|
public:
|
||||||
|
AvroDatum() : m_schema(AvroSchema::NullSchema) {}
|
||||||
|
explicit AvroDatum(AvroSchema schema) : m_schema(std::move(schema)) {}
|
||||||
|
|
||||||
|
void Fill(AvroStreamReader& reader, const Core::Context& context);
|
||||||
|
void Fill(AvroStreamReader::ReaderPos& data);
|
||||||
|
|
||||||
|
const AvroSchema& Schema() const { return m_schema; }
|
||||||
|
|
||||||
|
template <class T> T Value() const;
|
||||||
|
struct StringView
|
||||||
|
{
|
||||||
|
const uint8_t* Data = nullptr;
|
||||||
|
size_t Length = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
AvroSchema m_schema;
|
||||||
|
AvroStreamReader::ReaderPos m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
using AvroMap = std::map<std::string, AvroDatum>;
|
||||||
|
|
||||||
|
class AvroRecord final {
|
||||||
|
public:
|
||||||
|
bool HasField(const std::string& key) const { return FindField(key) != m_keys->size(); }
|
||||||
|
const AvroDatum& Field(const std::string& key) const { return m_values.at(FindField(key)); }
|
||||||
|
AvroDatum& Field(const std::string& key) { return m_values.at(FindField(key)); }
|
||||||
|
const AvroDatum& FieldAt(size_t i) const { return m_values.at(i); }
|
||||||
|
AvroDatum& FieldAt(size_t i) { return m_values.at(i); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t FindField(const std::string& key) const
|
||||||
|
{
|
||||||
|
auto i = find(m_keys->begin(), m_keys->end(), key);
|
||||||
|
return i - m_keys->begin();
|
||||||
|
}
|
||||||
|
const std::vector<std::string>* m_keys = nullptr;
|
||||||
|
std::vector<AvroDatum> m_values;
|
||||||
|
|
||||||
|
friend class AvroDatum;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvroObjectContainerReader final {
|
||||||
|
public:
|
||||||
|
explicit AvroObjectContainerReader(Core::IO::BodyStream& stream);
|
||||||
|
|
||||||
|
bool End() const { return m_eof; }
|
||||||
|
// Calling Next() will invalidates the previous AvroDatum returned by this function and all
|
||||||
|
// AvroDatums propagated from there.
|
||||||
|
AvroDatum Next(const Core::Context& context) { return NextImpl(m_objectSchema.get(), context); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AvroDatum NextImpl(const AvroSchema* schema, const Core::Context& context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<AvroStreamReader> m_reader;
|
||||||
|
std::unique_ptr<AvroSchema> m_objectSchema;
|
||||||
|
std::string m_syncMarker;
|
||||||
|
int64_t m_remainingObjectInCurrentBlock = 0;
|
||||||
|
bool m_eof = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvroStreamParser final : public Core::IO::BodyStream {
|
||||||
|
public:
|
||||||
|
explicit AvroStreamParser(
|
||||||
|
std::unique_ptr<Azure::Core::IO::BodyStream> inner,
|
||||||
|
std::function<void(int64_t, int64_t)> progressCallback,
|
||||||
|
std::function<void(BlobQueryError)> errorCallback)
|
||||||
|
: m_inner(std::move(inner)), m_parser(*m_inner),
|
||||||
|
m_progressCallback(std::move(progressCallback)), m_errorCallback(std::move(errorCallback))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t Length() const override { return -1; }
|
||||||
|
void Rewind() override { this->m_inner->Rewind(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t OnRead(uint8_t* buffer, size_t count, const Azure::Core::Context& context) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Azure::Core::IO::BodyStream> m_inner;
|
||||||
|
AvroObjectContainerReader m_parser;
|
||||||
|
std::function<void(int64_t, int64_t)> m_progressCallback;
|
||||||
|
std::function<void(BlobQueryError)> m_errorCallback;
|
||||||
|
AvroDatum::StringView m_parserBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}}} // namespace Azure::Storage::Blobs::_detail
|
|
@ -0,0 +1,402 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can use, redistribute, and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License, version 3
|
||||||
|
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ALLOW_FORBID_FUNC
|
||||||
|
|
||||||
|
#include "az.h"
|
||||||
|
|
||||||
|
#include "os.h"
|
||||||
|
#include "taoserror.h"
|
||||||
|
#include "tglobal.h"
|
||||||
|
|
||||||
|
#if defined(USE_S3)
|
||||||
|
|
||||||
|
#include <azure/core.hpp>
|
||||||
|
#include <azure/storage/blobs.hpp>
|
||||||
|
#include "td_block_blob_client.hpp"
|
||||||
|
|
||||||
|
// Add appropriate using namespace directives
|
||||||
|
using namespace Azure::Storage;
|
||||||
|
using namespace Azure::Storage::Blobs;
|
||||||
|
|
||||||
|
extern char tsS3Hostname[][TSDB_FQDN_LEN];
|
||||||
|
extern char tsS3AccessKeyId[][TSDB_FQDN_LEN];
|
||||||
|
extern char tsS3AccessKeySecret[][TSDB_FQDN_LEN];
|
||||||
|
extern char tsS3BucketName[TSDB_FQDN_LEN];
|
||||||
|
|
||||||
|
extern int8_t tsS3Enabled;
|
||||||
|
extern int8_t tsS3EpNum;
|
||||||
|
|
||||||
|
int32_t azBegin() { return TSDB_CODE_SUCCESS; }
|
||||||
|
|
||||||
|
void azEnd() {}
|
||||||
|
|
||||||
|
static void azDumpCfgByEp(int8_t epIndex) {
|
||||||
|
// clang-format off
|
||||||
|
(void)fprintf(stdout,
|
||||||
|
"%-24s %s\n"
|
||||||
|
"%-24s %s\n"
|
||||||
|
"%-24s %s\n"
|
||||||
|
// "%-24s %s\n"
|
||||||
|
"%-24s %s\n"
|
||||||
|
"%-24s %s\n",
|
||||||
|
"hostName", tsS3Hostname[epIndex],
|
||||||
|
"bucketName", tsS3BucketName,
|
||||||
|
"protocol", "https only",
|
||||||
|
//"uristyle", (uriStyleG[epIndex] == S3UriStyleVirtualHost ? "virtualhost" : "path"),
|
||||||
|
"accessKey", tsS3AccessKeyId[epIndex],
|
||||||
|
"accessKeySecret", tsS3AccessKeySecret[epIndex]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t azListBucket(char const *bucketname) {
|
||||||
|
int32_t code = 0;
|
||||||
|
const std::string delimiter = "/";
|
||||||
|
std::string accountName = tsS3AccessKeyId[0];
|
||||||
|
std::string accountKey = tsS3AccessKeySecret[0];
|
||||||
|
std::string accountURL = tsS3Hostname[0];
|
||||||
|
accountURL = "https://" + accountURL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto sharedKeyCredential = std::make_shared<StorageSharedKeyCredential>(accountName, accountKey);
|
||||||
|
|
||||||
|
StorageSharedKeyCredential *pSharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
|
||||||
|
|
||||||
|
BlobServiceClient blobServiceClient(accountURL, sharedKeyCredential);
|
||||||
|
|
||||||
|
std::string containerName = bucketname;
|
||||||
|
auto containerClient = blobServiceClient.GetBlobContainerClient(containerName);
|
||||||
|
|
||||||
|
Azure::Storage::Blobs::ListBlobsOptions options;
|
||||||
|
options.Prefix = "s3";
|
||||||
|
|
||||||
|
(void)fprintf(stderr, "objects:\n");
|
||||||
|
// std::set<std::string> listBlobs;
|
||||||
|
for (auto pageResult = containerClient.ListBlobs(options); pageResult.HasPage(); pageResult.MoveToNextPage()) {
|
||||||
|
for (const auto &blob : pageResult.Blobs) {
|
||||||
|
(void)fprintf(stderr, "%s\n", blob.Name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const Azure::Core::RequestFailedException &e) {
|
||||||
|
uError("%s failed at line %d since %d(%s)", __func__, __LINE__, static_cast<int>(e.StatusCode),
|
||||||
|
e.ReasonPhrase.c_str());
|
||||||
|
// uError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(TAOS_SYSTEM_ERROR(EIO)));
|
||||||
|
|
||||||
|
code = TAOS_SYSTEM_ERROR(EIO);
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t azCheckCfg() {
|
||||||
|
int32_t code = 0, lino = 0;
|
||||||
|
int8_t i = 0;
|
||||||
|
|
||||||
|
// for (; i < tsS3EpNum; i++) {
|
||||||
|
(void)fprintf(stdout, "test s3 ep (%d/%d):\n", i + 1, tsS3EpNum);
|
||||||
|
// s3DumpCfgByEp(i);
|
||||||
|
azDumpCfgByEp(0);
|
||||||
|
|
||||||
|
// test put
|
||||||
|
char testdata[17] = "0123456789abcdef";
|
||||||
|
const char *objectname[] = {"s3test.txt"};
|
||||||
|
char path[PATH_MAX] = {0};
|
||||||
|
int ds_len = strlen(TD_DIRSEP);
|
||||||
|
int tmp_len = strlen(tsTempDir);
|
||||||
|
|
||||||
|
(void)snprintf(path, PATH_MAX, "%s", tsTempDir);
|
||||||
|
if (strncmp(tsTempDir + tmp_len - ds_len, TD_DIRSEP, ds_len) != 0) {
|
||||||
|
(void)snprintf(path + tmp_len, PATH_MAX - tmp_len, "%s", TD_DIRSEP);
|
||||||
|
(void)snprintf(path + tmp_len + ds_len, PATH_MAX - tmp_len - ds_len, "%s", objectname[0]);
|
||||||
|
} else {
|
||||||
|
(void)snprintf(path + tmp_len, PATH_MAX - tmp_len, "%s", objectname[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *pBlock = NULL;
|
||||||
|
int c_offset = 10;
|
||||||
|
int c_len = 6;
|
||||||
|
char buf[7] = {0};
|
||||||
|
|
||||||
|
TdFilePtr fp = taosOpenFile(path, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_READ | TD_FILE_TRUNC);
|
||||||
|
if (!fp) {
|
||||||
|
(void)fprintf(stderr, "failed to open test file: %s.\n", path);
|
||||||
|
// uError("ERROR: %s Failed to open %s", __func__, path);
|
||||||
|
TAOS_CHECK_GOTO(terrno, &lino, _next);
|
||||||
|
}
|
||||||
|
if (taosWriteFile(fp, testdata, strlen(testdata)) < 0) {
|
||||||
|
(void)fprintf(stderr, "failed to write test file: %s.\n", path);
|
||||||
|
TAOS_CHECK_GOTO(terrno, &lino, _next);
|
||||||
|
}
|
||||||
|
if (taosFsyncFile(fp) < 0) {
|
||||||
|
(void)fprintf(stderr, "failed to fsync test file: %s.\n", path);
|
||||||
|
TAOS_CHECK_GOTO(terrno, &lino, _next);
|
||||||
|
}
|
||||||
|
(void)taosCloseFile(&fp);
|
||||||
|
|
||||||
|
(void)fprintf(stderr, "\nstart to put object: %s, file: %s content: %s\n", objectname[0], path, testdata);
|
||||||
|
code = azPutObjectFromFileOffset(path, objectname[0], 0, 16);
|
||||||
|
if (code != 0) {
|
||||||
|
(void)fprintf(stderr, "put object %s : failed.\n", objectname[0]);
|
||||||
|
TAOS_CHECK_GOTO(code, &lino, _next);
|
||||||
|
}
|
||||||
|
(void)fprintf(stderr, "put object %s: success.\n\n", objectname[0]);
|
||||||
|
|
||||||
|
// list buckets
|
||||||
|
(void)fprintf(stderr, "start to list bucket %s by prefix s3.\n", tsS3BucketName);
|
||||||
|
// code = s3ListBucketByEp(tsS3BucketName, i);
|
||||||
|
code = azListBucket(tsS3BucketName);
|
||||||
|
if (code != 0) {
|
||||||
|
(void)fprintf(stderr, "listing bucket %s : failed.\n", tsS3BucketName);
|
||||||
|
TAOS_CHECK_GOTO(code, &lino, _next);
|
||||||
|
}
|
||||||
|
(void)fprintf(stderr, "listing bucket %s: success.\n\n", tsS3BucketName);
|
||||||
|
|
||||||
|
// test range get
|
||||||
|
(void)fprintf(stderr, "start to range get object %s offset: %d len: %d.\n", objectname[0], c_offset, c_len);
|
||||||
|
code = azGetObjectBlock(objectname[0], c_offset, c_len, true, &pBlock);
|
||||||
|
if (code != 0) {
|
||||||
|
(void)fprintf(stderr, "get object %s : failed.\n", objectname[0]);
|
||||||
|
TAOS_CHECK_GOTO(code, &lino, _next);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)memcpy(buf, pBlock, c_len);
|
||||||
|
taosMemoryFree(pBlock);
|
||||||
|
(void)fprintf(stderr, "object content: %s\n", buf);
|
||||||
|
(void)fprintf(stderr, "get object %s: success.\n\n", objectname[0]);
|
||||||
|
|
||||||
|
// delete test object
|
||||||
|
(void)fprintf(stderr, "start to delete object: %s.\n", objectname[0]);
|
||||||
|
// code = azDeleteObjectsByPrefix(objectname[0]);
|
||||||
|
azDeleteObjectsByPrefix(objectname[0]);
|
||||||
|
/*
|
||||||
|
if (code != 0) {
|
||||||
|
(void)fprintf(stderr, "delete object %s : failed.\n", objectname[0]);
|
||||||
|
TAOS_CHECK_GOTO(code, &lino, _next);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
(void)fprintf(stderr, "delete object %s: success.\n\n", objectname[0]);
|
||||||
|
|
||||||
|
_next:
|
||||||
|
if (fp) {
|
||||||
|
(void)taosCloseFile(&fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TSDB_CODE_SUCCESS != code) {
|
||||||
|
(void)fprintf(stderr, "s3 check failed, code: %d, line: %d, index: %d.\n", code, lino, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)fprintf(stdout, "=================================================================\n");
|
||||||
|
//}
|
||||||
|
|
||||||
|
// azEnd();
|
||||||
|
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t azPutObjectFromFileOffset(const char *file, const char *object_name, int64_t offset, int64_t size) {
|
||||||
|
int32_t code = 0;
|
||||||
|
|
||||||
|
std::string endpointUrl = tsS3Hostname[0]; // GetEndpointUrl();
|
||||||
|
std::string accountName = tsS3AccessKeyId[0]; // GetAccountName();
|
||||||
|
std::string accountKey = tsS3AccessKeySecret[0]; // GetAccountKey();
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto sharedKeyCredential = std::make_shared<StorageSharedKeyCredential>(accountName, accountKey);
|
||||||
|
|
||||||
|
std::string accountURL = tsS3Hostname[0];
|
||||||
|
StorageSharedKeyCredential *pSharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
|
||||||
|
|
||||||
|
accountURL = "https://" + accountURL;
|
||||||
|
BlobServiceClient blobServiceClient(accountURL, sharedKeyCredential);
|
||||||
|
|
||||||
|
std::string containerName = tsS3BucketName;
|
||||||
|
auto containerClient = blobServiceClient.GetBlobContainerClient(containerName);
|
||||||
|
|
||||||
|
// Create the container if it does not exist
|
||||||
|
// std::cout << "Creating container: " << containerName << std::endl;
|
||||||
|
// containerClient.CreateIfNotExists();
|
||||||
|
|
||||||
|
std::string blobName = "blob.txt";
|
||||||
|
uint8_t blobContent[] = "Hello Azure!";
|
||||||
|
// Create the block blob client
|
||||||
|
// BlockBlobClient blobClient = containerClient.GetBlockBlobClient(blobName);
|
||||||
|
// TDBlockBlobClient blobClient(containerClient.GetBlobClient(blobName));
|
||||||
|
TDBlockBlobClient blobClient(containerClient.GetBlobClient(object_name));
|
||||||
|
|
||||||
|
// Upload the blob
|
||||||
|
// std::cout << "Uploading blob: " << blobName << std::endl;
|
||||||
|
// blobClient.UploadFrom(blobContent, sizeof(blobContent));
|
||||||
|
blobClient.UploadFrom(file, offset, size);
|
||||||
|
//(void)_azUploadFrom(blobClient, file, offset, size);
|
||||||
|
/*
|
||||||
|
auto blockBlobClient = BlockBlobClient(endpointUrl, sharedKeyCredential);
|
||||||
|
|
||||||
|
// Create some data to upload into the blob.
|
||||||
|
std::vector<uint8_t> data = {1, 2, 3, 4};
|
||||||
|
Azure::Core::IO::MemoryBodyStream stream(data);
|
||||||
|
|
||||||
|
Azure::Response<Models::UploadBlockBlobResult> response = blockBlobClient.Upload(stream);
|
||||||
|
|
||||||
|
Models::UploadBlockBlobResult model = response.Value;
|
||||||
|
std::cout << "Last modified date of uploaded blob: " << model.LastModified.ToString()
|
||||||
|
<< std::endl;
|
||||||
|
*/
|
||||||
|
} catch (const Azure::Core::RequestFailedException &e) {
|
||||||
|
/*
|
||||||
|
std::cout << "Status Code: " << static_cast<int>(e.StatusCode) << ", Reason Phrase: " << e.ReasonPhrase
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << e.what() << std::endl;
|
||||||
|
*/
|
||||||
|
code = TAOS_SYSTEM_ERROR(EIO);
|
||||||
|
uError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t azGetObjectBlock(const char *object_name, int64_t offset, int64_t size, bool check, uint8_t **ppBlock) {
|
||||||
|
int32_t code = TSDB_CODE_SUCCESS;
|
||||||
|
std::string accountName = tsS3AccessKeyId[0];
|
||||||
|
std::string accountKey = tsS3AccessKeySecret[0];
|
||||||
|
std::string accountURL = tsS3Hostname[0];
|
||||||
|
accountURL = "https://" + accountURL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto sharedKeyCredential = std::make_shared<StorageSharedKeyCredential>(accountName, accountKey);
|
||||||
|
|
||||||
|
StorageSharedKeyCredential *pSharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
|
||||||
|
|
||||||
|
BlobServiceClient blobServiceClient(accountURL, sharedKeyCredential);
|
||||||
|
|
||||||
|
std::string containerName = tsS3BucketName;
|
||||||
|
auto containerClient = blobServiceClient.GetBlobContainerClient(containerName);
|
||||||
|
|
||||||
|
TDBlockBlobClient blobClient(containerClient.GetBlobClient(object_name));
|
||||||
|
|
||||||
|
uint8_t *buf = (uint8_t *)taosMemoryCalloc(1, size);
|
||||||
|
if (!buf) {
|
||||||
|
return terrno;
|
||||||
|
}
|
||||||
|
|
||||||
|
Blobs::DownloadBlobToOptions options;
|
||||||
|
// options.TransferOptions.Concurrency = concurrency;
|
||||||
|
// if (offset.HasValue() || length.HasValue()) {
|
||||||
|
options.Range = Azure::Core::Http::HttpRange();
|
||||||
|
options.Range.Value().Offset = offset;
|
||||||
|
options.Range.Value().Length = size;
|
||||||
|
//}
|
||||||
|
/*
|
||||||
|
if (initialChunkSize.HasValue()) {
|
||||||
|
options.TransferOptions.InitialChunkSize = initialChunkSize.Value();
|
||||||
|
}
|
||||||
|
if (chunkSize.HasValue()) {
|
||||||
|
options.TransferOptions.ChunkSize = chunkSize.Value();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
auto res = blobClient.DownloadTo(buf, size, options);
|
||||||
|
if (check && res.Value.ContentRange.Length.Value() != size) {
|
||||||
|
code = TAOS_SYSTEM_ERROR(EIO);
|
||||||
|
uError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppBlock = buf;
|
||||||
|
} catch (const Azure::Core::RequestFailedException &e) {
|
||||||
|
uError("%s failed at line %d since %d(%s)", __func__, __LINE__, static_cast<int>(e.StatusCode),
|
||||||
|
e.ReasonPhrase.c_str());
|
||||||
|
code = TAOS_SYSTEM_ERROR(EIO);
|
||||||
|
uError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
TAOS_RETURN(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void azDeleteObjectsByPrefix(const char *prefix) {
|
||||||
|
const std::string delimiter = "/";
|
||||||
|
std::string accountName = tsS3AccessKeyId[0];
|
||||||
|
std::string accountKey = tsS3AccessKeySecret[0];
|
||||||
|
std::string accountURL = tsS3Hostname[0];
|
||||||
|
accountURL = "https://" + accountURL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto sharedKeyCredential = std::make_shared<StorageSharedKeyCredential>(accountName, accountKey);
|
||||||
|
|
||||||
|
StorageSharedKeyCredential *pSharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
|
||||||
|
|
||||||
|
BlobServiceClient blobServiceClient(accountURL, sharedKeyCredential);
|
||||||
|
|
||||||
|
std::string containerName = tsS3BucketName;
|
||||||
|
auto containerClient = blobServiceClient.GetBlobContainerClient(containerName);
|
||||||
|
|
||||||
|
Azure::Storage::Blobs::ListBlobsOptions options;
|
||||||
|
options.Prefix = prefix;
|
||||||
|
|
||||||
|
std::set<std::string> listBlobs;
|
||||||
|
for (auto pageResult = containerClient.ListBlobs(options); pageResult.HasPage(); pageResult.MoveToNextPage()) {
|
||||||
|
for (const auto &blob : pageResult.Blobs) {
|
||||||
|
listBlobs.insert(blob.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto blobName : listBlobs) {
|
||||||
|
auto blobClient = containerClient.GetAppendBlobClient(blobName);
|
||||||
|
blobClient.Delete();
|
||||||
|
}
|
||||||
|
} catch (const Azure::Core::RequestFailedException &e) {
|
||||||
|
uError("%s failed at line %d since %d(%s)", __func__, __LINE__, static_cast<int>(e.StatusCode),
|
||||||
|
e.ReasonPhrase.c_str());
|
||||||
|
// uError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(TAOS_SYSTEM_ERROR(EIO)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t azPutObjectFromFile2(const char *file, const char *object, int8_t withcp) { return 0; }
|
||||||
|
|
||||||
|
int32_t azGetObjectsByPrefix(const char *prefix, const char *path) { return 0; }
|
||||||
|
|
||||||
|
int32_t azGetObjectToFile(const char *object_name, const char *fileName) { return 0; }
|
||||||
|
|
||||||
|
int32_t azDeleteObjects(const char *object_name[], int nobject) { return 0; }
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int32_t azBegin() { return TSDB_CODE_SUCCESS; }
|
||||||
|
|
||||||
|
void azEnd() {}
|
||||||
|
|
||||||
|
int32_t azCheckCfg() { return TSDB_CODE_SUCCESS; }
|
||||||
|
|
||||||
|
int32_t azPutObjectFromFileOffset(const char *file, const char *object_name, int64_t offset, int64_t size) {
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t azGetObjectBlock(const char *object_name, int64_t offset, int64_t size, bool check, uint8_t **ppBlock) {
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void azDeleteObjectsByPrefix(const char *prefix) {}
|
||||||
|
|
||||||
|
int32_t azPutObjectFromFile2(const char *file, const char *object, int8_t withcp) { return 0; }
|
||||||
|
|
||||||
|
int32_t azGetObjectsByPrefix(const char *prefix, const char *path) { return 0; }
|
||||||
|
|
||||||
|
int32_t azGetObjectToFile(const char *object_name, const char *fileName) { return 0; }
|
||||||
|
|
||||||
|
int32_t azDeleteObjects(const char *object_name[], int nobject) { return 0; }
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,625 @@
|
||||||
|
#if defined(USE_S3)
|
||||||
|
|
||||||
|
#include "td_block_blob_client.hpp"
|
||||||
|
|
||||||
|
#include <azure/core/platform.hpp>
|
||||||
|
|
||||||
|
#if defined(AZ_PLATFORM_WINDOWS)
|
||||||
|
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#if !defined(NOMINMAX)
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "./avro_parser.hpp"
|
||||||
|
|
||||||
|
#include <azure/core/io/body_stream.hpp>
|
||||||
|
#include <azure/storage/common/crypt.hpp>
|
||||||
|
#include <azure/storage/common/internal/concurrent_transfer.hpp>
|
||||||
|
#include <azure/storage/common/internal/constants.hpp>
|
||||||
|
#include <azure/storage/common/internal/file_io.hpp>
|
||||||
|
#include <azure/storage/common/internal/storage_switch_to_secondary_policy.hpp>
|
||||||
|
#include <azure/storage/common/storage_common.hpp>
|
||||||
|
#include <azure/storage/common/storage_exception.hpp>
|
||||||
|
|
||||||
|
namespace Azure {
|
||||||
|
namespace Storage {
|
||||||
|
namespace Blobs {
|
||||||
|
|
||||||
|
TDBlockBlobClient TDBlockBlobClient::CreateFromConnectionString(const std::string& connectionString,
|
||||||
|
const std::string& blobContainerName,
|
||||||
|
const std::string& blobName,
|
||||||
|
const BlobClientOptions& options) {
|
||||||
|
TDBlockBlobClient newClient(
|
||||||
|
BlobClient::CreateFromConnectionString(connectionString, blobContainerName, blobName, options));
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
TDBlockBlobClient::TDBlockBlobClient(const std::string& blobUrl, std::shared_ptr<StorageSharedKeyCredential> credential,
|
||||||
|
const BlobClientOptions& options)
|
||||||
|
: BlobClient(blobUrl, std::move(credential), options) {}
|
||||||
|
|
||||||
|
TDBlockBlobClient::TDBlockBlobClient(const std::string& blobUrl,
|
||||||
|
std::shared_ptr<Core::Credentials::TokenCredential> credential,
|
||||||
|
const BlobClientOptions& options)
|
||||||
|
: BlobClient(blobUrl, std::move(credential), options) {}
|
||||||
|
|
||||||
|
TDBlockBlobClient::TDBlockBlobClient(const std::string& blobUrl, const BlobClientOptions& options)
|
||||||
|
: BlobClient(blobUrl, options) {}
|
||||||
|
|
||||||
|
TDBlockBlobClient::TDBlockBlobClient(BlobClient blobClient) : BlobClient(std::move(blobClient)) {}
|
||||||
|
|
||||||
|
TDBlockBlobClient TDBlockBlobClient::WithSnapshot(const std::string& snapshot) const {
|
||||||
|
TDBlockBlobClient newClient(*this);
|
||||||
|
if (snapshot.empty()) {
|
||||||
|
newClient.m_blobUrl.RemoveQueryParameter(_internal::HttpQuerySnapshot);
|
||||||
|
} else {
|
||||||
|
newClient.m_blobUrl.AppendQueryParameter(_internal::HttpQuerySnapshot,
|
||||||
|
_internal::UrlEncodeQueryParameter(snapshot));
|
||||||
|
}
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
TDBlockBlobClient TDBlockBlobClient::WithVersionId(const std::string& versionId) const {
|
||||||
|
TDBlockBlobClient newClient(*this);
|
||||||
|
if (versionId.empty()) {
|
||||||
|
newClient.m_blobUrl.RemoveQueryParameter(_internal::HttpQueryVersionId);
|
||||||
|
} else {
|
||||||
|
newClient.m_blobUrl.AppendQueryParameter(_internal::HttpQueryVersionId,
|
||||||
|
_internal::UrlEncodeQueryParameter(versionId));
|
||||||
|
}
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::UploadBlockBlobResult> TDBlockBlobClient::Upload(Azure::Core::IO::BodyStream& content,
|
||||||
|
const UploadBlockBlobOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_detail::BlockBlobClient::UploadBlockBlobOptions protocolLayerOptions;
|
||||||
|
if (options.TransactionalContentHash.HasValue()) {
|
||||||
|
if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Md5) {
|
||||||
|
protocolLayerOptions.TransactionalContentMD5 = options.TransactionalContentHash.Value().Value;
|
||||||
|
} else if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Crc64) {
|
||||||
|
protocolLayerOptions.TransactionalContentCrc64 = options.TransactionalContentHash.Value().Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protocolLayerOptions.BlobContentType = options.HttpHeaders.ContentType;
|
||||||
|
protocolLayerOptions.BlobContentEncoding = options.HttpHeaders.ContentEncoding;
|
||||||
|
protocolLayerOptions.BlobContentLanguage = options.HttpHeaders.ContentLanguage;
|
||||||
|
protocolLayerOptions.BlobContentMD5 = options.HttpHeaders.ContentHash.Value;
|
||||||
|
protocolLayerOptions.BlobContentDisposition = options.HttpHeaders.ContentDisposition;
|
||||||
|
protocolLayerOptions.BlobCacheControl = options.HttpHeaders.CacheControl;
|
||||||
|
protocolLayerOptions.Metadata = std::map<std::string, std::string>(options.Metadata.begin(), options.Metadata.end());
|
||||||
|
protocolLayerOptions.BlobTagsString = _detail::TagsToString(options.Tags);
|
||||||
|
protocolLayerOptions.Tier = options.AccessTier;
|
||||||
|
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||||
|
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
|
||||||
|
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
|
||||||
|
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
|
||||||
|
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
|
||||||
|
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
|
||||||
|
if (m_customerProvidedKey.HasValue()) {
|
||||||
|
protocolLayerOptions.EncryptionKey = m_customerProvidedKey.Value().Key;
|
||||||
|
protocolLayerOptions.EncryptionKeySha256 = m_customerProvidedKey.Value().KeyHash;
|
||||||
|
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
|
||||||
|
}
|
||||||
|
protocolLayerOptions.EncryptionScope = m_encryptionScope;
|
||||||
|
if (options.ImmutabilityPolicy.HasValue()) {
|
||||||
|
protocolLayerOptions.ImmutabilityPolicyExpiry = options.ImmutabilityPolicy.Value().ExpiresOn;
|
||||||
|
protocolLayerOptions.ImmutabilityPolicyMode = options.ImmutabilityPolicy.Value().PolicyMode;
|
||||||
|
}
|
||||||
|
protocolLayerOptions.LegalHold = options.HasLegalHold;
|
||||||
|
|
||||||
|
return _detail::BlockBlobClient::Upload(*m_pipeline, m_blobUrl, content, protocolLayerOptions, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromResult> TDBlockBlobClient::UploadFrom(
|
||||||
|
const uint8_t* buffer, size_t bufferSize, const UploadBlockBlobFromOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
constexpr int64_t DefaultStageBlockSize = 4 * 1024 * 1024ULL;
|
||||||
|
constexpr int64_t MaxStageBlockSize = 4000 * 1024 * 1024ULL;
|
||||||
|
constexpr int64_t MaxBlockNumber = 50000;
|
||||||
|
constexpr int64_t BlockGrainSize = 1 * 1024 * 1024;
|
||||||
|
|
||||||
|
if (static_cast<uint64_t>(options.TransferOptions.SingleUploadThreshold) > (std::numeric_limits<size_t>::max)()) {
|
||||||
|
throw Azure::Core::RequestFailedException("Single upload threshold is too big");
|
||||||
|
}
|
||||||
|
if (bufferSize <= static_cast<size_t>(options.TransferOptions.SingleUploadThreshold)) {
|
||||||
|
Azure::Core::IO::MemoryBodyStream contentStream(buffer, bufferSize);
|
||||||
|
UploadBlockBlobOptions uploadBlockBlobOptions;
|
||||||
|
uploadBlockBlobOptions.HttpHeaders = options.HttpHeaders;
|
||||||
|
uploadBlockBlobOptions.Metadata = options.Metadata;
|
||||||
|
uploadBlockBlobOptions.Tags = options.Tags;
|
||||||
|
uploadBlockBlobOptions.AccessTier = options.AccessTier;
|
||||||
|
uploadBlockBlobOptions.ImmutabilityPolicy = options.ImmutabilityPolicy;
|
||||||
|
uploadBlockBlobOptions.HasLegalHold = options.HasLegalHold;
|
||||||
|
return Upload(contentStream, uploadBlockBlobOptions, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t chunkSize;
|
||||||
|
if (options.TransferOptions.ChunkSize.HasValue()) {
|
||||||
|
chunkSize = options.TransferOptions.ChunkSize.Value();
|
||||||
|
} else {
|
||||||
|
int64_t minChunkSize = (bufferSize + MaxBlockNumber - 1) / MaxBlockNumber;
|
||||||
|
minChunkSize = (minChunkSize + BlockGrainSize - 1) / BlockGrainSize * BlockGrainSize;
|
||||||
|
chunkSize = (std::max)(DefaultStageBlockSize, minChunkSize);
|
||||||
|
}
|
||||||
|
if (chunkSize > MaxStageBlockSize) {
|
||||||
|
throw Azure::Core::RequestFailedException("Block size is too big.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> blockIds;
|
||||||
|
auto getBlockId = [](int64_t id) {
|
||||||
|
constexpr size_t BlockIdLength = 64;
|
||||||
|
std::string blockId = std::to_string(id);
|
||||||
|
blockId = std::string(BlockIdLength - blockId.length(), '0') + blockId;
|
||||||
|
return Azure::Core::Convert::Base64Encode(std::vector<uint8_t>(blockId.begin(), blockId.end()));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto uploadBlockFunc = [&](int64_t offset, int64_t length, int64_t chunkId, int64_t numChunks) {
|
||||||
|
Azure::Core::IO::MemoryBodyStream contentStream(buffer + offset, static_cast<size_t>(length));
|
||||||
|
StageBlockOptions chunkOptions;
|
||||||
|
auto blockInfo = StageBlock(getBlockId(chunkId), contentStream, chunkOptions, context);
|
||||||
|
if (chunkId == numChunks - 1) {
|
||||||
|
blockIds.resize(static_cast<size_t>(numChunks));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_internal::ConcurrentTransfer(0, bufferSize, chunkSize, options.TransferOptions.Concurrency, uploadBlockFunc);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < blockIds.size(); ++i) {
|
||||||
|
blockIds[i] = getBlockId(static_cast<int64_t>(i));
|
||||||
|
}
|
||||||
|
CommitBlockListOptions commitBlockListOptions;
|
||||||
|
commitBlockListOptions.HttpHeaders = options.HttpHeaders;
|
||||||
|
commitBlockListOptions.Metadata = options.Metadata;
|
||||||
|
commitBlockListOptions.Tags = options.Tags;
|
||||||
|
commitBlockListOptions.AccessTier = options.AccessTier;
|
||||||
|
commitBlockListOptions.ImmutabilityPolicy = options.ImmutabilityPolicy;
|
||||||
|
commitBlockListOptions.HasLegalHold = options.HasLegalHold;
|
||||||
|
auto commitBlockListResponse = CommitBlockList(blockIds, commitBlockListOptions, context);
|
||||||
|
|
||||||
|
Models::UploadBlockBlobFromResult ret;
|
||||||
|
ret.ETag = std::move(commitBlockListResponse.Value.ETag);
|
||||||
|
ret.LastModified = std::move(commitBlockListResponse.Value.LastModified);
|
||||||
|
ret.VersionId = std::move(commitBlockListResponse.Value.VersionId);
|
||||||
|
ret.IsServerEncrypted = commitBlockListResponse.Value.IsServerEncrypted;
|
||||||
|
ret.EncryptionKeySha256 = std::move(commitBlockListResponse.Value.EncryptionKeySha256);
|
||||||
|
ret.EncryptionScope = std::move(commitBlockListResponse.Value.EncryptionScope);
|
||||||
|
return Azure::Response<Models::UploadBlockBlobFromResult>(std::move(ret),
|
||||||
|
std::move(commitBlockListResponse.RawResponse));
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromResult> TDBlockBlobClient::UploadFrom(
|
||||||
|
const std::string& fileName, const UploadBlockBlobFromOptions& options, const Azure::Core::Context& context) const {
|
||||||
|
constexpr int64_t DefaultStageBlockSize = 4 * 1024 * 1024ULL;
|
||||||
|
constexpr int64_t MaxStageBlockSize = 4000 * 1024 * 1024ULL;
|
||||||
|
constexpr int64_t MaxBlockNumber = 50000;
|
||||||
|
constexpr int64_t BlockGrainSize = 1 * 1024 * 1024;
|
||||||
|
|
||||||
|
{
|
||||||
|
Azure::Core::IO::FileBodyStream contentStream(fileName);
|
||||||
|
|
||||||
|
if (contentStream.Length() <= options.TransferOptions.SingleUploadThreshold) {
|
||||||
|
UploadBlockBlobOptions uploadBlockBlobOptions;
|
||||||
|
uploadBlockBlobOptions.HttpHeaders = options.HttpHeaders;
|
||||||
|
uploadBlockBlobOptions.Metadata = options.Metadata;
|
||||||
|
uploadBlockBlobOptions.Tags = options.Tags;
|
||||||
|
uploadBlockBlobOptions.AccessTier = options.AccessTier;
|
||||||
|
uploadBlockBlobOptions.ImmutabilityPolicy = options.ImmutabilityPolicy;
|
||||||
|
uploadBlockBlobOptions.HasLegalHold = options.HasLegalHold;
|
||||||
|
return Upload(contentStream, uploadBlockBlobOptions, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> blockIds;
|
||||||
|
auto getBlockId = [](int64_t id) {
|
||||||
|
constexpr size_t BlockIdLength = 64;
|
||||||
|
std::string blockId = std::to_string(id);
|
||||||
|
blockId = std::string(BlockIdLength - blockId.length(), '0') + blockId;
|
||||||
|
return Azure::Core::Convert::Base64Encode(std::vector<uint8_t>(blockId.begin(), blockId.end()));
|
||||||
|
};
|
||||||
|
|
||||||
|
_internal::FileReader fileReader(fileName);
|
||||||
|
|
||||||
|
auto uploadBlockFunc = [&](int64_t offset, int64_t length, int64_t chunkId, int64_t numChunks) {
|
||||||
|
Azure::Core::IO::_internal::RandomAccessFileBodyStream contentStream(fileReader.GetHandle(), offset, length);
|
||||||
|
StageBlockOptions chunkOptions;
|
||||||
|
auto blockInfo = StageBlock(getBlockId(chunkId), contentStream, chunkOptions, context);
|
||||||
|
if (chunkId == numChunks - 1) {
|
||||||
|
blockIds.resize(static_cast<size_t>(numChunks));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int64_t chunkSize;
|
||||||
|
if (options.TransferOptions.ChunkSize.HasValue()) {
|
||||||
|
chunkSize = options.TransferOptions.ChunkSize.Value();
|
||||||
|
} else {
|
||||||
|
int64_t minChunkSize = (fileReader.GetFileSize() + MaxBlockNumber - 1) / MaxBlockNumber;
|
||||||
|
minChunkSize = (minChunkSize + BlockGrainSize - 1) / BlockGrainSize * BlockGrainSize;
|
||||||
|
chunkSize = (std::max)(DefaultStageBlockSize, minChunkSize);
|
||||||
|
}
|
||||||
|
if (chunkSize > MaxStageBlockSize) {
|
||||||
|
throw Azure::Core::RequestFailedException("Block size is too big.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_internal::ConcurrentTransfer(0, fileReader.GetFileSize(), chunkSize, options.TransferOptions.Concurrency,
|
||||||
|
uploadBlockFunc);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < blockIds.size(); ++i) {
|
||||||
|
blockIds[i] = getBlockId(static_cast<int64_t>(i));
|
||||||
|
}
|
||||||
|
CommitBlockListOptions commitBlockListOptions;
|
||||||
|
commitBlockListOptions.HttpHeaders = options.HttpHeaders;
|
||||||
|
commitBlockListOptions.Metadata = options.Metadata;
|
||||||
|
commitBlockListOptions.Tags = options.Tags;
|
||||||
|
commitBlockListOptions.AccessTier = options.AccessTier;
|
||||||
|
commitBlockListOptions.ImmutabilityPolicy = options.ImmutabilityPolicy;
|
||||||
|
commitBlockListOptions.HasLegalHold = options.HasLegalHold;
|
||||||
|
auto commitBlockListResponse = CommitBlockList(blockIds, commitBlockListOptions, context);
|
||||||
|
|
||||||
|
Models::UploadBlockBlobFromResult result;
|
||||||
|
result.ETag = commitBlockListResponse.Value.ETag;
|
||||||
|
result.LastModified = commitBlockListResponse.Value.LastModified;
|
||||||
|
result.VersionId = commitBlockListResponse.Value.VersionId;
|
||||||
|
result.IsServerEncrypted = commitBlockListResponse.Value.IsServerEncrypted;
|
||||||
|
result.EncryptionKeySha256 = commitBlockListResponse.Value.EncryptionKeySha256;
|
||||||
|
result.EncryptionScope = commitBlockListResponse.Value.EncryptionScope;
|
||||||
|
return Azure::Response<Models::UploadBlockBlobFromResult>(std::move(result),
|
||||||
|
std::move(commitBlockListResponse.RawResponse));
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromResult> TDBlockBlobClient::UploadFrom(
|
||||||
|
const std::string& fileName, int64_t offset, int64_t size, const UploadBlockBlobFromOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_internal::FileReader fileReader(fileName);
|
||||||
|
|
||||||
|
{
|
||||||
|
Azure::Core::IO::_internal::RandomAccessFileBodyStream contentStream(fileReader.GetHandle(), offset, size);
|
||||||
|
|
||||||
|
if (size <= options.TransferOptions.SingleUploadThreshold) {
|
||||||
|
UploadBlockBlobOptions uploadBlockBlobOptions;
|
||||||
|
uploadBlockBlobOptions.HttpHeaders = options.HttpHeaders;
|
||||||
|
uploadBlockBlobOptions.Metadata = options.Metadata;
|
||||||
|
uploadBlockBlobOptions.Tags = options.Tags;
|
||||||
|
uploadBlockBlobOptions.AccessTier = options.AccessTier;
|
||||||
|
uploadBlockBlobOptions.ImmutabilityPolicy = options.ImmutabilityPolicy;
|
||||||
|
uploadBlockBlobOptions.HasLegalHold = options.HasLegalHold;
|
||||||
|
return Upload(contentStream, uploadBlockBlobOptions, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> blockIds;
|
||||||
|
auto getBlockId = [](int64_t id) {
|
||||||
|
constexpr size_t BlockIdLength = 64;
|
||||||
|
std::string blockId = std::to_string(id);
|
||||||
|
blockId = std::string(BlockIdLength - blockId.length(), '0') + blockId;
|
||||||
|
return Azure::Core::Convert::Base64Encode(std::vector<uint8_t>(blockId.begin(), blockId.end()));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto uploadBlockFunc = [&](int64_t offset, int64_t length, int64_t chunkId, int64_t numChunks) {
|
||||||
|
Azure::Core::IO::_internal::RandomAccessFileBodyStream contentStream(fileReader.GetHandle(), offset, length);
|
||||||
|
StageBlockOptions chunkOptions;
|
||||||
|
auto blockInfo = StageBlock(getBlockId(chunkId), contentStream, chunkOptions, context);
|
||||||
|
if (chunkId == numChunks - 1) {
|
||||||
|
blockIds.resize(static_cast<size_t>(numChunks));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int64_t DefaultStageBlockSize = 4 * 1024 * 1024ULL;
|
||||||
|
constexpr int64_t MaxStageBlockSize = 4000 * 1024 * 1024ULL;
|
||||||
|
constexpr int64_t MaxBlockNumber = 50000;
|
||||||
|
constexpr int64_t BlockGrainSize = 1 * 1024 * 1024;
|
||||||
|
|
||||||
|
int64_t chunkSize;
|
||||||
|
if (options.TransferOptions.ChunkSize.HasValue()) {
|
||||||
|
chunkSize = options.TransferOptions.ChunkSize.Value();
|
||||||
|
} else {
|
||||||
|
int64_t minChunkSize = (size + MaxBlockNumber - 1) / MaxBlockNumber;
|
||||||
|
minChunkSize = (minChunkSize + BlockGrainSize - 1) / BlockGrainSize * BlockGrainSize;
|
||||||
|
chunkSize = (std::max)(DefaultStageBlockSize, minChunkSize);
|
||||||
|
}
|
||||||
|
if (chunkSize > MaxStageBlockSize) {
|
||||||
|
throw Azure::Core::RequestFailedException("Block size is too big.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_internal::ConcurrentTransfer(offset, size, chunkSize, options.TransferOptions.Concurrency, uploadBlockFunc);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < blockIds.size(); ++i) {
|
||||||
|
blockIds[i] = getBlockId(static_cast<int64_t>(i));
|
||||||
|
}
|
||||||
|
CommitBlockListOptions commitBlockListOptions;
|
||||||
|
commitBlockListOptions.HttpHeaders = options.HttpHeaders;
|
||||||
|
commitBlockListOptions.Metadata = options.Metadata;
|
||||||
|
commitBlockListOptions.Tags = options.Tags;
|
||||||
|
commitBlockListOptions.AccessTier = options.AccessTier;
|
||||||
|
commitBlockListOptions.ImmutabilityPolicy = options.ImmutabilityPolicy;
|
||||||
|
commitBlockListOptions.HasLegalHold = options.HasLegalHold;
|
||||||
|
auto commitBlockListResponse = CommitBlockList(blockIds, commitBlockListOptions, context);
|
||||||
|
|
||||||
|
Models::UploadBlockBlobFromResult result;
|
||||||
|
result.ETag = commitBlockListResponse.Value.ETag;
|
||||||
|
result.LastModified = commitBlockListResponse.Value.LastModified;
|
||||||
|
result.VersionId = commitBlockListResponse.Value.VersionId;
|
||||||
|
result.IsServerEncrypted = commitBlockListResponse.Value.IsServerEncrypted;
|
||||||
|
result.EncryptionKeySha256 = commitBlockListResponse.Value.EncryptionKeySha256;
|
||||||
|
result.EncryptionScope = commitBlockListResponse.Value.EncryptionScope;
|
||||||
|
return Azure::Response<Models::UploadBlockBlobFromResult>(std::move(result),
|
||||||
|
std::move(commitBlockListResponse.RawResponse));
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::UploadBlockBlobFromUriResult> TDBlockBlobClient::UploadFromUri(
|
||||||
|
const std::string& sourceUri, const UploadBlockBlobFromUriOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_detail::BlockBlobClient::UploadBlockBlobFromUriOptions protocolLayerOptions;
|
||||||
|
protocolLayerOptions.CopySource = sourceUri;
|
||||||
|
protocolLayerOptions.CopySourceBlobProperties = options.CopySourceBlobProperties;
|
||||||
|
protocolLayerOptions.BlobContentType = options.HttpHeaders.ContentType;
|
||||||
|
protocolLayerOptions.BlobContentEncoding = options.HttpHeaders.ContentEncoding;
|
||||||
|
protocolLayerOptions.BlobContentLanguage = options.HttpHeaders.ContentLanguage;
|
||||||
|
protocolLayerOptions.BlobContentMD5 = options.HttpHeaders.ContentHash.Value;
|
||||||
|
protocolLayerOptions.BlobCacheControl = options.HttpHeaders.CacheControl;
|
||||||
|
protocolLayerOptions.BlobContentDisposition = options.HttpHeaders.ContentDisposition;
|
||||||
|
protocolLayerOptions.Metadata = std::map<std::string, std::string>(options.Metadata.begin(), options.Metadata.end());
|
||||||
|
protocolLayerOptions.BlobTagsString = _detail::TagsToString(options.Tags);
|
||||||
|
protocolLayerOptions.Tier = options.AccessTier;
|
||||||
|
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||||
|
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
|
||||||
|
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
|
||||||
|
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
|
||||||
|
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
|
||||||
|
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
|
||||||
|
protocolLayerOptions.SourceIfMatch = options.SourceAccessConditions.IfMatch;
|
||||||
|
protocolLayerOptions.SourceIfNoneMatch = options.SourceAccessConditions.IfNoneMatch;
|
||||||
|
protocolLayerOptions.SourceIfModifiedSince = options.SourceAccessConditions.IfModifiedSince;
|
||||||
|
protocolLayerOptions.SourceIfUnmodifiedSince = options.SourceAccessConditions.IfUnmodifiedSince;
|
||||||
|
protocolLayerOptions.SourceIfTags = options.SourceAccessConditions.TagConditions;
|
||||||
|
if (options.TransactionalContentHash.HasValue()) {
|
||||||
|
if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Md5) {
|
||||||
|
protocolLayerOptions.SourceContentMD5 = options.TransactionalContentHash.Value().Value;
|
||||||
|
} else if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Crc64) {
|
||||||
|
protocolLayerOptions.SourceContentcrc64 = options.TransactionalContentHash.Value().Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_customerProvidedKey.HasValue()) {
|
||||||
|
protocolLayerOptions.EncryptionKey = m_customerProvidedKey.Value().Key;
|
||||||
|
protocolLayerOptions.EncryptionKeySha256 = m_customerProvidedKey.Value().KeyHash;
|
||||||
|
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
|
||||||
|
}
|
||||||
|
protocolLayerOptions.EncryptionScope = m_encryptionScope;
|
||||||
|
protocolLayerOptions.CopySourceTags = options.CopySourceTagsMode;
|
||||||
|
if (!options.SourceAuthorization.empty()) {
|
||||||
|
protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _detail::BlockBlobClient::UploadFromUri(*m_pipeline, m_blobUrl, protocolLayerOptions, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::StageBlockResult> TDBlockBlobClient::StageBlock(const std::string& blockId,
|
||||||
|
Azure::Core::IO::BodyStream& content,
|
||||||
|
const StageBlockOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_detail::BlockBlobClient::StageBlockBlobBlockOptions protocolLayerOptions;
|
||||||
|
protocolLayerOptions.BlockId = blockId;
|
||||||
|
if (options.TransactionalContentHash.HasValue()) {
|
||||||
|
if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Md5) {
|
||||||
|
protocolLayerOptions.TransactionalContentMD5 = options.TransactionalContentHash.Value().Value;
|
||||||
|
} else if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Crc64) {
|
||||||
|
protocolLayerOptions.TransactionalContentCrc64 = options.TransactionalContentHash.Value().Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||||
|
if (m_customerProvidedKey.HasValue()) {
|
||||||
|
protocolLayerOptions.EncryptionKey = m_customerProvidedKey.Value().Key;
|
||||||
|
protocolLayerOptions.EncryptionKeySha256 = m_customerProvidedKey.Value().KeyHash;
|
||||||
|
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
|
||||||
|
}
|
||||||
|
protocolLayerOptions.EncryptionScope = m_encryptionScope;
|
||||||
|
return _detail::BlockBlobClient::StageBlock(*m_pipeline, m_blobUrl, content, protocolLayerOptions, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::StageBlockFromUriResult> TDBlockBlobClient::StageBlockFromUri(
|
||||||
|
const std::string& blockId, const std::string& sourceUri, const StageBlockFromUriOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_detail::BlockBlobClient::StageBlockBlobBlockFromUriOptions protocolLayerOptions;
|
||||||
|
protocolLayerOptions.BlockId = blockId;
|
||||||
|
protocolLayerOptions.SourceUrl = sourceUri;
|
||||||
|
if (options.SourceRange.HasValue()) {
|
||||||
|
std::string rangeStr = "bytes=" + std::to_string(options.SourceRange.Value().Offset) + "-";
|
||||||
|
if (options.SourceRange.Value().Length.HasValue()) {
|
||||||
|
rangeStr += std::to_string(options.SourceRange.Value().Offset + options.SourceRange.Value().Length.Value() - 1);
|
||||||
|
}
|
||||||
|
protocolLayerOptions.SourceRange = rangeStr;
|
||||||
|
}
|
||||||
|
if (options.TransactionalContentHash.HasValue()) {
|
||||||
|
if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Md5) {
|
||||||
|
protocolLayerOptions.SourceContentMD5 = options.TransactionalContentHash.Value().Value;
|
||||||
|
} else if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Crc64) {
|
||||||
|
protocolLayerOptions.SourceContentcrc64 = options.TransactionalContentHash.Value().Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||||
|
protocolLayerOptions.SourceIfModifiedSince = options.SourceAccessConditions.IfModifiedSince;
|
||||||
|
protocolLayerOptions.SourceIfUnmodifiedSince = options.SourceAccessConditions.IfUnmodifiedSince;
|
||||||
|
protocolLayerOptions.SourceIfMatch = options.SourceAccessConditions.IfMatch;
|
||||||
|
protocolLayerOptions.SourceIfNoneMatch = options.SourceAccessConditions.IfNoneMatch;
|
||||||
|
if (m_customerProvidedKey.HasValue()) {
|
||||||
|
protocolLayerOptions.EncryptionKey = m_customerProvidedKey.Value().Key;
|
||||||
|
protocolLayerOptions.EncryptionKeySha256 = m_customerProvidedKey.Value().KeyHash;
|
||||||
|
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
|
||||||
|
}
|
||||||
|
protocolLayerOptions.EncryptionScope = m_encryptionScope;
|
||||||
|
if (!options.SourceAuthorization.empty()) {
|
||||||
|
protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _detail::BlockBlobClient::StageBlockFromUri(*m_pipeline, m_blobUrl, protocolLayerOptions, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::CommitBlockListResult> TDBlockBlobClient::CommitBlockList(
|
||||||
|
const std::vector<std::string>& blockIds, const CommitBlockListOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_detail::BlockBlobClient::CommitBlockBlobBlockListOptions protocolLayerOptions;
|
||||||
|
protocolLayerOptions.Blocks.Latest = blockIds;
|
||||||
|
protocolLayerOptions.BlobContentType = options.HttpHeaders.ContentType;
|
||||||
|
protocolLayerOptions.BlobContentEncoding = options.HttpHeaders.ContentEncoding;
|
||||||
|
protocolLayerOptions.BlobContentLanguage = options.HttpHeaders.ContentLanguage;
|
||||||
|
protocolLayerOptions.BlobContentMD5 = options.HttpHeaders.ContentHash.Value;
|
||||||
|
protocolLayerOptions.BlobContentDisposition = options.HttpHeaders.ContentDisposition;
|
||||||
|
protocolLayerOptions.BlobCacheControl = options.HttpHeaders.CacheControl;
|
||||||
|
protocolLayerOptions.Metadata = std::map<std::string, std::string>(options.Metadata.begin(), options.Metadata.end());
|
||||||
|
protocolLayerOptions.BlobTagsString = _detail::TagsToString(options.Tags);
|
||||||
|
protocolLayerOptions.Tier = options.AccessTier;
|
||||||
|
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||||
|
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
|
||||||
|
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
|
||||||
|
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
|
||||||
|
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
|
||||||
|
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
|
||||||
|
if (m_customerProvidedKey.HasValue()) {
|
||||||
|
protocolLayerOptions.EncryptionKey = m_customerProvidedKey.Value().Key;
|
||||||
|
protocolLayerOptions.EncryptionKeySha256 = m_customerProvidedKey.Value().KeyHash;
|
||||||
|
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
|
||||||
|
}
|
||||||
|
protocolLayerOptions.EncryptionScope = m_encryptionScope;
|
||||||
|
if (options.ImmutabilityPolicy.HasValue()) {
|
||||||
|
protocolLayerOptions.ImmutabilityPolicyExpiry = options.ImmutabilityPolicy.Value().ExpiresOn;
|
||||||
|
protocolLayerOptions.ImmutabilityPolicyMode = options.ImmutabilityPolicy.Value().PolicyMode;
|
||||||
|
}
|
||||||
|
protocolLayerOptions.LegalHold = options.HasLegalHold;
|
||||||
|
|
||||||
|
return _detail::BlockBlobClient::CommitBlockList(*m_pipeline, m_blobUrl, protocolLayerOptions, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Azure::Response<Models::GetBlockListResult> TDBlockBlobClient::GetBlockList(const GetBlockListOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_detail::BlockBlobClient::GetBlockBlobBlockListOptions protocolLayerOptions;
|
||||||
|
protocolLayerOptions.ListType = options.ListType;
|
||||||
|
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||||
|
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
|
||||||
|
return _detail::BlockBlobClient::GetBlockList(*m_pipeline, m_blobUrl, protocolLayerOptions,
|
||||||
|
_internal::WithReplicaStatus(context));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Azure::Response<Models::QueryBlobResult> TDBlockBlobClient::Query(const std::string& querySqlExpression,
|
||||||
|
const QueryBlobOptions& options,
|
||||||
|
const Azure::Core::Context& context) const {
|
||||||
|
_detail::BlobClient::QueryBlobOptions protocolLayerOptions;
|
||||||
|
protocolLayerOptions.QueryRequest.QueryType = Models::_detail::QueryRequestQueryType::SQL;
|
||||||
|
protocolLayerOptions.QueryRequest.Expression = querySqlExpression;
|
||||||
|
if (options.InputTextConfiguration.m_format == Models::_detail::QueryFormatType::Delimited) {
|
||||||
|
Models::_detail::DelimitedTextConfiguration c;
|
||||||
|
c.RecordSeparator = options.InputTextConfiguration.m_recordSeparator;
|
||||||
|
c.ColumnSeparator = options.InputTextConfiguration.m_columnSeparator;
|
||||||
|
c.FieldQuote = options.InputTextConfiguration.m_quotationCharacter;
|
||||||
|
c.EscapeChar = options.InputTextConfiguration.m_escapeCharacter;
|
||||||
|
c.HeadersPresent = options.InputTextConfiguration.m_hasHeaders;
|
||||||
|
Models::_detail::QuerySerialization q;
|
||||||
|
q.Format.Type = options.InputTextConfiguration.m_format;
|
||||||
|
q.Format.DelimitedTextConfiguration = std::move(c);
|
||||||
|
protocolLayerOptions.QueryRequest.InputSerialization = std::move(q);
|
||||||
|
} else if (options.InputTextConfiguration.m_format == Models::_detail::QueryFormatType::Json) {
|
||||||
|
Models::_detail::JsonTextConfiguration c;
|
||||||
|
c.RecordSeparator = options.InputTextConfiguration.m_recordSeparator;
|
||||||
|
Models::_detail::QuerySerialization q;
|
||||||
|
q.Format.Type = options.InputTextConfiguration.m_format;
|
||||||
|
q.Format.JsonTextConfiguration = std::move(c);
|
||||||
|
protocolLayerOptions.QueryRequest.InputSerialization = std::move(q);
|
||||||
|
} else if (options.InputTextConfiguration.m_format == Models::_detail::QueryFormatType::Parquet) {
|
||||||
|
Models::_detail::ParquetConfiguration c;
|
||||||
|
Models::_detail::QuerySerialization q;
|
||||||
|
q.Format.Type = options.InputTextConfiguration.m_format;
|
||||||
|
q.Format.ParquetTextConfiguration = std::move(c);
|
||||||
|
protocolLayerOptions.QueryRequest.InputSerialization = std::move(q);
|
||||||
|
} else if (options.InputTextConfiguration.m_format.ToString().empty()) {
|
||||||
|
} else {
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
}
|
||||||
|
if (options.OutputTextConfiguration.m_format == Models::_detail::QueryFormatType::Delimited) {
|
||||||
|
Models::_detail::DelimitedTextConfiguration c;
|
||||||
|
c.RecordSeparator = options.OutputTextConfiguration.m_recordSeparator;
|
||||||
|
c.ColumnSeparator = options.OutputTextConfiguration.m_columnSeparator;
|
||||||
|
c.FieldQuote = options.OutputTextConfiguration.m_quotationCharacter;
|
||||||
|
c.EscapeChar = options.OutputTextConfiguration.m_escapeCharacter;
|
||||||
|
c.HeadersPresent = options.OutputTextConfiguration.m_hasHeaders;
|
||||||
|
Models::_detail::QuerySerialization q;
|
||||||
|
q.Format.Type = options.OutputTextConfiguration.m_format;
|
||||||
|
q.Format.DelimitedTextConfiguration = std::move(c);
|
||||||
|
protocolLayerOptions.QueryRequest.OutputSerialization = std::move(q);
|
||||||
|
} else if (options.OutputTextConfiguration.m_format == Models::_detail::QueryFormatType::Json) {
|
||||||
|
Models::_detail::JsonTextConfiguration c;
|
||||||
|
c.RecordSeparator = options.OutputTextConfiguration.m_recordSeparator;
|
||||||
|
Models::_detail::QuerySerialization q;
|
||||||
|
q.Format.Type = options.OutputTextConfiguration.m_format;
|
||||||
|
q.Format.JsonTextConfiguration = std::move(c);
|
||||||
|
protocolLayerOptions.QueryRequest.OutputSerialization = std::move(q);
|
||||||
|
} else if (options.OutputTextConfiguration.m_format == Models::_detail::QueryFormatType::Parquet) {
|
||||||
|
Models::_detail::ParquetConfiguration c;
|
||||||
|
Models::_detail::QuerySerialization q;
|
||||||
|
q.Format.Type = options.OutputTextConfiguration.m_format;
|
||||||
|
q.Format.ParquetTextConfiguration = std::move(c);
|
||||||
|
protocolLayerOptions.QueryRequest.OutputSerialization = std::move(q);
|
||||||
|
} else if (options.OutputTextConfiguration.m_format == Models::_detail::QueryFormatType::Arrow) {
|
||||||
|
Models::_detail::ArrowConfiguration c;
|
||||||
|
c.Schema = options.OutputTextConfiguration.m_schema;
|
||||||
|
Models::_detail::QuerySerialization q;
|
||||||
|
q.Format.Type = options.OutputTextConfiguration.m_format;
|
||||||
|
q.Format.ArrowConfiguration = std::move(c);
|
||||||
|
protocolLayerOptions.QueryRequest.OutputSerialization = std::move(q);
|
||||||
|
} else if (options.InputTextConfiguration.m_format.ToString().empty()) {
|
||||||
|
} else {
|
||||||
|
AZURE_UNREACHABLE_CODE();
|
||||||
|
}
|
||||||
|
|
||||||
|
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||||
|
if (m_customerProvidedKey.HasValue()) {
|
||||||
|
protocolLayerOptions.EncryptionKey = m_customerProvidedKey.Value().Key;
|
||||||
|
protocolLayerOptions.EncryptionKeySha256 = m_customerProvidedKey.Value().KeyHash;
|
||||||
|
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
|
||||||
|
}
|
||||||
|
protocolLayerOptions.EncryptionScope = m_encryptionScope;
|
||||||
|
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
|
||||||
|
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
|
||||||
|
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
|
||||||
|
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
|
||||||
|
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
|
||||||
|
auto response =
|
||||||
|
_detail::BlobClient::Query(*m_pipeline, m_blobUrl, protocolLayerOptions, _internal::WithReplicaStatus(context));
|
||||||
|
|
||||||
|
const auto statusCode = response.RawResponse->GetStatusCode();
|
||||||
|
const auto reasonPhrase = response.RawResponse->GetReasonPhrase();
|
||||||
|
const auto requestId = response.RawResponse->GetHeaders().count(_internal::HttpHeaderRequestId) != 0
|
||||||
|
? response.RawResponse->GetHeaders().at(_internal::HttpHeaderRequestId)
|
||||||
|
: std::string();
|
||||||
|
|
||||||
|
const auto clientRequestId = response.RawResponse->GetHeaders().count(_internal::HttpHeaderClientRequestId) != 0
|
||||||
|
? response.RawResponse->GetHeaders().at(_internal::HttpHeaderClientRequestId)
|
||||||
|
: std::string();
|
||||||
|
|
||||||
|
auto defaultErrorHandler = [statusCode, reasonPhrase, requestId, clientRequestId](BlobQueryError e) {
|
||||||
|
if (e.IsFatal) {
|
||||||
|
StorageException exception("Fatal " + e.Name + " at " + std::to_string(e.Position));
|
||||||
|
exception.StatusCode = statusCode;
|
||||||
|
exception.ReasonPhrase = reasonPhrase;
|
||||||
|
exception.RequestId = requestId;
|
||||||
|
exception.ClientRequestId = clientRequestId;
|
||||||
|
exception.ErrorCode = e.Name;
|
||||||
|
exception.Message = e.Description;
|
||||||
|
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
response.Value.BodyStream =
|
||||||
|
std::make_unique<_detail::AvroStreamParser>(std::move(response.Value.BodyStream), options.ProgressHandler,
|
||||||
|
options.ErrorHandler ? options.ErrorHandler : defaultErrorHandler);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
} // namespace Blobs
|
||||||
|
} // namespace Storage
|
||||||
|
} // namespace Azure
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,18 @@
|
||||||
|
aux_source_directory(. AZ_TEST_SRC)
|
||||||
|
|
||||||
|
add_executable(azTest ${AZ_TEST_SRC})
|
||||||
|
target_include_directories(azTest
|
||||||
|
PUBLIC
|
||||||
|
"${TD_SOURCE_DIR}/include/libs/azure"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/../inc"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(azTest
|
||||||
|
az
|
||||||
|
gtest_main
|
||||||
|
)
|
||||||
|
enable_testing()
|
||||||
|
add_test(
|
||||||
|
NAME az_test
|
||||||
|
COMMAND azTest
|
||||||
|
)
|
|
@ -0,0 +1,457 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <queue>
|
||||||
|
/*
|
||||||
|
#include "walInt.h"
|
||||||
|
|
||||||
|
const char* ranStr = "tvapq02tcp";
|
||||||
|
const int ranStrLen = strlen(ranStr);
|
||||||
|
SWalSyncInfo syncMeta = {0};
|
||||||
|
|
||||||
|
class WalCleanEnv : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
int code = walInit(NULL);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestCase() { walCleanUp(); }
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
taosRemoveDir(pathName);
|
||||||
|
SWalCfg* pCfg = (SWalCfg*)taosMemoryMalloc(sizeof(SWalCfg));
|
||||||
|
memset(pCfg, 0, sizeof(SWalCfg));
|
||||||
|
pCfg->rollPeriod = -1;
|
||||||
|
pCfg->segSize = -1;
|
||||||
|
pCfg->retentionPeriod = 0;
|
||||||
|
pCfg->retentionSize = 0;
|
||||||
|
pCfg->level = TAOS_WAL_FSYNC;
|
||||||
|
pWal = walOpen(pathName, pCfg);
|
||||||
|
taosMemoryFree(pCfg);
|
||||||
|
ASSERT(pWal != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
walClose(pWal);
|
||||||
|
pWal = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWal* pWal = NULL;
|
||||||
|
const char* pathName = TD_TMP_DIR_PATH "wal_test";
|
||||||
|
};
|
||||||
|
|
||||||
|
class WalCleanDeleteEnv : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
int code = walInit(NULL);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestCase() { walCleanUp(); }
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
taosRemoveDir(pathName);
|
||||||
|
SWalCfg* pCfg = (SWalCfg*)taosMemoryMalloc(sizeof(SWalCfg));
|
||||||
|
memset(pCfg, 0, sizeof(SWalCfg));
|
||||||
|
pCfg->retentionPeriod = 0;
|
||||||
|
pCfg->retentionSize = 0;
|
||||||
|
pCfg->level = TAOS_WAL_FSYNC;
|
||||||
|
pWal = walOpen(pathName, pCfg);
|
||||||
|
taosMemoryFree(pCfg);
|
||||||
|
ASSERT(pWal != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
walClose(pWal);
|
||||||
|
pWal = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWal* pWal = NULL;
|
||||||
|
const char* pathName = TD_TMP_DIR_PATH "wal_test";
|
||||||
|
};
|
||||||
|
|
||||||
|
class WalKeepEnv : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
int code = walInit(NULL);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestCase() { walCleanUp(); }
|
||||||
|
|
||||||
|
void walResetEnv() {
|
||||||
|
TearDown();
|
||||||
|
taosRemoveDir(pathName);
|
||||||
|
SetUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
SWalCfg* pCfg = (SWalCfg*)taosMemoryMalloc(sizeof(SWalCfg));
|
||||||
|
memset(pCfg, 0, sizeof(SWalCfg));
|
||||||
|
pCfg->rollPeriod = -1;
|
||||||
|
pCfg->segSize = -1;
|
||||||
|
pCfg->retentionPeriod = 0;
|
||||||
|
pCfg->retentionSize = 0;
|
||||||
|
pCfg->level = TAOS_WAL_FSYNC;
|
||||||
|
pWal = walOpen(pathName, pCfg);
|
||||||
|
taosMemoryFree(pCfg);
|
||||||
|
ASSERT(pWal != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
walClose(pWal);
|
||||||
|
pWal = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWal* pWal = NULL;
|
||||||
|
const char* pathName = TD_TMP_DIR_PATH "wal_test";
|
||||||
|
};
|
||||||
|
|
||||||
|
class WalRetentionEnv : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
int code = walInit(NULL);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestCase() { walCleanUp(); }
|
||||||
|
|
||||||
|
void walResetEnv() {
|
||||||
|
TearDown();
|
||||||
|
taosRemoveDir(pathName);
|
||||||
|
SetUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
SWalCfg cfg;
|
||||||
|
cfg.rollPeriod = -1;
|
||||||
|
cfg.segSize = -1;
|
||||||
|
cfg.retentionPeriod = -1;
|
||||||
|
cfg.retentionSize = 0;
|
||||||
|
cfg.rollPeriod = 0;
|
||||||
|
cfg.vgId = 0;
|
||||||
|
cfg.level = TAOS_WAL_FSYNC;
|
||||||
|
pWal = walOpen(pathName, &cfg);
|
||||||
|
ASSERT(pWal != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
walClose(pWal);
|
||||||
|
pWal = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWal* pWal = NULL;
|
||||||
|
const char* pathName = TD_TMP_DIR_PATH "wal_test";
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(WalCleanEnv, createNew) {
|
||||||
|
walRollFileInfo(pWal);
|
||||||
|
ASSERT(pWal->fileInfoSet != NULL);
|
||||||
|
ASSERT_EQ(pWal->fileInfoSet->size, 1);
|
||||||
|
SWalFileInfo* pInfo = (SWalFileInfo*)taosArrayGetLast(pWal->fileInfoSet);
|
||||||
|
ASSERT_EQ(pInfo->firstVer, 0);
|
||||||
|
ASSERT_EQ(pInfo->lastVer, -1);
|
||||||
|
ASSERT_EQ(pInfo->closeTs, -1);
|
||||||
|
ASSERT_EQ(pInfo->fileSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalCleanEnv, serialize) {
|
||||||
|
int code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
ASSERT(pWal->fileInfoSet != NULL);
|
||||||
|
|
||||||
|
code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
char* ss = NULL;
|
||||||
|
code = walMetaSerialize(pWal, &ss);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
printf("%s\n", ss);
|
||||||
|
taosMemoryFree(ss);
|
||||||
|
code = walSaveMeta(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalCleanEnv, removeOldMeta) {
|
||||||
|
int code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
ASSERT(pWal->fileInfoSet != NULL);
|
||||||
|
code = walSaveMeta(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
code = walRollFileInfo(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
code = walSaveMeta(pWal);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalKeepEnv, readOldMeta) {
|
||||||
|
walResetEnv();
|
||||||
|
int code;
|
||||||
|
|
||||||
|
syncMeta.isWeek = -1;
|
||||||
|
syncMeta.seqNum = UINT64_MAX;
|
||||||
|
syncMeta.term = UINT64_MAX;
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
code = walAppendLog(pWal, i, i + 1, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, i);
|
||||||
|
code = walAppendLog(pWal, i + 2, i, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, TSDB_CODE_WAL_INVALID_VER);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, i);
|
||||||
|
}
|
||||||
|
char* oldss = NULL;
|
||||||
|
code = walMetaSerialize(pWal, &oldss);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
|
||||||
|
TearDown();
|
||||||
|
SetUp();
|
||||||
|
|
||||||
|
ASSERT_EQ(pWal->vers.firstVer, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 9);
|
||||||
|
|
||||||
|
char* newss = NULL;
|
||||||
|
code = walMetaSerialize(pWal, &newss);
|
||||||
|
ASSERT(code == 0);
|
||||||
|
|
||||||
|
int len = strlen(oldss);
|
||||||
|
ASSERT_EQ(len, strlen(newss));
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
EXPECT_EQ(oldss[i], newss[i]);
|
||||||
|
}
|
||||||
|
taosMemoryFree(oldss);
|
||||||
|
taosMemoryFree(newss);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalCleanEnv, write) {
|
||||||
|
int code;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
code = walAppendLog(pWal, i, i + 1, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, i);
|
||||||
|
code = walAppendLog(pWal, i + 2, i, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, TSDB_CODE_WAL_INVALID_VER);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, i);
|
||||||
|
}
|
||||||
|
code = walSaveMeta(pWal);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalCleanEnv, rollback) {
|
||||||
|
int code;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
code = walAppendLog(pWal, i, i + 1, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, i);
|
||||||
|
}
|
||||||
|
code = walRollback(pWal, 12);
|
||||||
|
ASSERT_NE(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 9);
|
||||||
|
code = walRollback(pWal, 9);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 8);
|
||||||
|
code = walRollback(pWal, 5);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 4);
|
||||||
|
code = walRollback(pWal, 3);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 2);
|
||||||
|
code = walSaveMeta(pWal);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalCleanEnv, rollbackMultiFile) {
|
||||||
|
int code;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
code = walAppendLog(pWal, i, i + 1, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, i);
|
||||||
|
if (i == 5) {
|
||||||
|
walBeginSnapshot(pWal, i, 0);
|
||||||
|
walEndSnapshot(pWal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code = walRollback(pWal, 12);
|
||||||
|
ASSERT_NE(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 9);
|
||||||
|
code = walRollback(pWal, 9);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 8);
|
||||||
|
code = walRollback(pWal, 6);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 5);
|
||||||
|
code = walRollback(pWal, 5);
|
||||||
|
ASSERT_NE(code, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 5);
|
||||||
|
|
||||||
|
code = walAppendLog(pWal, 6, 6, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 6);
|
||||||
|
|
||||||
|
code = walSaveMeta(pWal);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalCleanDeleteEnv, roll) {
|
||||||
|
int code;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 100; i++) {
|
||||||
|
code = walAppendLog(pWal, i, 0, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, i);
|
||||||
|
code = walCommit(pWal, i);
|
||||||
|
ASSERT_EQ(pWal->vers.commitVer, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
walBeginSnapshot(pWal, i - 1, 0);
|
||||||
|
ASSERT_EQ(pWal->vers.verInSnapshotting, i - 1);
|
||||||
|
walEndSnapshot(pWal);
|
||||||
|
ASSERT_EQ(pWal->vers.snapshotVer, i - 1);
|
||||||
|
ASSERT_EQ(pWal->vers.verInSnapshotting, -1);
|
||||||
|
|
||||||
|
code = walAppendLog(pWal, 5, 0, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_NE(code, 0);
|
||||||
|
|
||||||
|
for (; i < 200; i++) {
|
||||||
|
code = walAppendLog(pWal, i, 0, syncMeta, (void*)ranStr, ranStrLen);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
code = walCommit(pWal, i);
|
||||||
|
ASSERT_EQ(pWal->vers.commitVer, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
code = walBeginSnapshot(pWal, i - 1, 0);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
code = walEndSnapshot(pWal);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalKeepEnv, readHandleRead) {
|
||||||
|
walResetEnv();
|
||||||
|
int code;
|
||||||
|
SWalReader* pRead = walOpenReader(pWal, NULL, 0);
|
||||||
|
ASSERT(pRead != NULL);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 100; i++) {
|
||||||
|
char newStr[100];
|
||||||
|
sprintf(newStr, "%s-%d", ranStr, i);
|
||||||
|
int len = strlen(newStr);
|
||||||
|
code = walAppendLog(pWal, i, 0, syncMeta, newStr, len);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
int ver = taosRand() % 100;
|
||||||
|
code = walReadVer(pRead, ver);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
|
||||||
|
// printf("rrbody: \n");
|
||||||
|
// for(int i = 0; i < pRead->pHead->head.len; i++) {
|
||||||
|
// printf("%d ", pRead->pHead->head.body[i]);
|
||||||
|
//}
|
||||||
|
// printf("\n");
|
||||||
|
|
||||||
|
ASSERT_EQ(pRead->pHead->head.version, ver);
|
||||||
|
ASSERT_EQ(pRead->curVersion, ver + 1);
|
||||||
|
char newStr[100];
|
||||||
|
sprintf(newStr, "%s-%d", ranStr, ver);
|
||||||
|
int len = strlen(newStr);
|
||||||
|
ASSERT_EQ(pRead->pHead->head.bodyLen, len);
|
||||||
|
for (int j = 0; j < len; j++) {
|
||||||
|
EXPECT_EQ(newStr[j], pRead->pHead->head.body[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walCloseReader(pRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WalRetentionEnv, repairMeta1) {
|
||||||
|
walResetEnv();
|
||||||
|
int code;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 100; i++) {
|
||||||
|
char newStr[100];
|
||||||
|
sprintf(newStr, "%s-%d", ranStr, i);
|
||||||
|
int len = strlen(newStr);
|
||||||
|
code = walAppendLog(pWal, i, 0, syncMeta, newStr, len);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TearDown();
|
||||||
|
|
||||||
|
// getchar();
|
||||||
|
char buf[100];
|
||||||
|
sprintf(buf, "%s/meta-ver%d", pathName, 0);
|
||||||
|
taosRemoveFile(buf);
|
||||||
|
sprintf(buf, "%s/meta-ver%d", pathName, 1);
|
||||||
|
taosRemoveFile(buf);
|
||||||
|
SetUp();
|
||||||
|
// getchar();
|
||||||
|
|
||||||
|
ASSERT_EQ(pWal->vers.lastVer, 99);
|
||||||
|
|
||||||
|
SWalReader* pRead = walOpenReader(pWal, NULL, 0);
|
||||||
|
ASSERT(pRead != NULL);
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
int ver = taosRand() % 100;
|
||||||
|
code = walReadVer(pRead, ver);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
|
||||||
|
// printf("rrbody: \n");
|
||||||
|
// for(int i = 0; i < pRead->pHead->head.len; i++) {
|
||||||
|
// printf("%d ", pRead->pHead->head.body[i]);
|
||||||
|
//}
|
||||||
|
// printf("\n");
|
||||||
|
|
||||||
|
ASSERT_EQ(pRead->pHead->head.version, ver);
|
||||||
|
ASSERT_EQ(pRead->curVersion, ver + 1);
|
||||||
|
char newStr[100];
|
||||||
|
sprintf(newStr, "%s-%d", ranStr, ver);
|
||||||
|
int len = strlen(newStr);
|
||||||
|
ASSERT_EQ(pRead->pHead->head.bodyLen, len);
|
||||||
|
for (int j = 0; j < len; j++) {
|
||||||
|
EXPECT_EQ(newStr[j], pRead->pHead->head.body[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 100; i < 200; i++) {
|
||||||
|
char newStr[100];
|
||||||
|
sprintf(newStr, "%s-%d", ranStr, i);
|
||||||
|
int len = strlen(newStr);
|
||||||
|
code = walAppendLog(pWal, i, 0, syncMeta, newStr, len);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
int ver = taosRand() % 200;
|
||||||
|
code = walReadVer(pRead, ver);
|
||||||
|
ASSERT_EQ(code, 0);
|
||||||
|
|
||||||
|
// printf("rrbody: \n");
|
||||||
|
// for(int i = 0; i < pRead->pHead->head.len; i++) {
|
||||||
|
// printf("%d ", pRead->pHead->head.body[i]);
|
||||||
|
//}
|
||||||
|
// printf("\n");
|
||||||
|
|
||||||
|
ASSERT_EQ(pRead->pHead->head.version, ver);
|
||||||
|
ASSERT_EQ(pRead->curVersion, ver + 1);
|
||||||
|
char newStr[100];
|
||||||
|
sprintf(newStr, "%s-%d", ranStr, ver);
|
||||||
|
int len = strlen(newStr);
|
||||||
|
ASSERT_EQ(pRead->pHead->head.bodyLen, len);
|
||||||
|
for (int j = 0; j < len; j++) {
|
||||||
|
EXPECT_EQ(newStr[j], pRead->pHead->head.body[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walCloseReader(pRead);
|
||||||
|
}
|
||||||
|
*/
|
|
@ -9,7 +9,7 @@ target_include_directories(
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
tcs
|
tcs
|
||||||
# PUBLIC az
|
PUBLIC az
|
||||||
PUBLIC common
|
PUBLIC common
|
||||||
# PUBLIC cjson
|
# PUBLIC cjson
|
||||||
# PUBLIC os
|
# PUBLIC os
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "taoserror.h"
|
#include "taoserror.h"
|
||||||
#include "tglobal.h"
|
#include "tglobal.h"
|
||||||
|
|
||||||
//#include "az.h"
|
#include "az.h"
|
||||||
#include "cos.h"
|
#include "cos.h"
|
||||||
|
|
||||||
extern int8_t tsS3Ablob;
|
extern int8_t tsS3Ablob;
|
||||||
|
@ -68,7 +68,6 @@ int32_t tcsInit() {
|
||||||
tcs.DeleteObjects = s3DeleteObjects;
|
tcs.DeleteObjects = s3DeleteObjects;
|
||||||
tcs.GetObjectToFile = s3GetObjectToFile;
|
tcs.GetObjectToFile = s3GetObjectToFile;
|
||||||
} else if (TOS_PROTO_ABLOB == proto) {
|
} else if (TOS_PROTO_ABLOB == proto) {
|
||||||
/*
|
|
||||||
tcs.Begin = azBegin;
|
tcs.Begin = azBegin;
|
||||||
tcs.End = azEnd;
|
tcs.End = azEnd;
|
||||||
tcs.CheckCfg = azCheckCfg;
|
tcs.CheckCfg = azCheckCfg;
|
||||||
|
@ -82,7 +81,7 @@ int32_t tcsInit() {
|
||||||
tcs.GetObjectsByPrefix = azGetObjectsByPrefix;
|
tcs.GetObjectsByPrefix = azGetObjectsByPrefix;
|
||||||
tcs.DeleteObjects = azDeleteObjects;
|
tcs.DeleteObjects = azDeleteObjects;
|
||||||
tcs.GetObjectToFile = azGetObjectToFile;
|
tcs.GetObjectToFile = azGetObjectToFile;
|
||||||
*/
|
|
||||||
} else {
|
} else {
|
||||||
code = TSDB_CODE_INVALID_PARA;
|
code = TSDB_CODE_INVALID_PARA;
|
||||||
return code;
|
return code;
|
||||||
|
|
Loading…
Reference in New Issue