From 0de9447650fe938f451e25beafda1fed1bad94db Mon Sep 17 00:00:00 2001 From: t-horikawa Date: Mon, 16 Dec 2024 18:36:38 +0900 Subject: [PATCH 1/5] implement request subcommands --- src/CMakeLists.txt | 42 ++ src/jogasaki/proto/sql/common.proto | 231 +++++++++ src/jogasaki/proto/sql/error.proto | 195 +++++++ src/jogasaki/proto/sql/request.proto | 483 ++++++++++++++++++ src/jogasaki/proto/sql/response.proto | 326 ++++++++++++ src/tateyama/monitor/constants.h | 141 +++++ src/tateyama/monitor/constants_request.h | 118 +++++ src/tateyama/monitor/monitor.cpp | 109 ++++ src/tateyama/monitor/monitor.h | 193 ++----- src/tateyama/monitor/monitor_request.cpp | 60 +++ src/tateyama/proto/request/diagnostics.proto | 45 ++ src/tateyama/proto/request/request.proto | 55 ++ src/tateyama/proto/request/response.proto | 84 +++ src/tateyama/request/base64.h | 43 ++ src/tateyama/request/request.cpp | 249 +++++++++ src/tateyama/request/request.h | 30 ++ src/tateyama/tgctl/tgctl.cpp | 28 + src/tateyama/transport/transport.h | 74 +++ test/CMakeLists.txt | 6 +- .../monitor/identify_sql_type_test.cpp | 47 ++ .../tateyama/monitor/monitor_request_test.cpp | 116 +++++ test/tateyama/request/request_test.cpp | 220 ++++++++ 22 files changed, 2734 insertions(+), 161 deletions(-) create mode 100644 src/jogasaki/proto/sql/common.proto create mode 100644 src/jogasaki/proto/sql/error.proto create mode 100644 src/jogasaki/proto/sql/request.proto create mode 100644 src/jogasaki/proto/sql/response.proto create mode 100644 src/tateyama/monitor/constants.h create mode 100644 src/tateyama/monitor/constants_request.h create mode 100644 src/tateyama/monitor/monitor.cpp create mode 100644 src/tateyama/monitor/monitor_request.cpp create mode 100644 src/tateyama/proto/request/diagnostics.proto create mode 100644 src/tateyama/proto/request/request.proto create mode 100644 src/tateyama/proto/request/response.proto create mode 100644 src/tateyama/request/base64.h create mode 100644 src/tateyama/request/request.cpp create mode 100644 src/tateyama/request/request.h create mode 100644 test/tateyama/monitor/identify_sql_type_test.cpp create mode 100644 test/tateyama/monitor/monitor_request_test.cpp create mode 100644 test/tateyama/request/request_test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59520f7..2f9eb1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,6 +92,13 @@ set(ProtoFiles ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/session/request.proto ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/session/response.proto ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/session/diagnostic.proto + ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/request/request.proto + ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/request/response.proto + ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/request/diagnostics.proto + ${CMAKE_CURRENT_SOURCE_DIR}/jogasaki/proto/sql/request.proto + ${CMAKE_CURRENT_SOURCE_DIR}/jogasaki/proto/sql/response.proto + ${CMAKE_CURRENT_SOURCE_DIR}/jogasaki/proto/sql/common.proto + ${CMAKE_CURRENT_SOURCE_DIR}/jogasaki/proto/sql/error.proto ) if (ENABLE_ALTIMETER) list(APPEND ProtoFiles @@ -111,12 +118,14 @@ set(GENERATED_PROTO_SRCS ${GENERATED_PROTO_SRCS} PARENT_SCOPE) file(GLOB TGCTL_SOURCES "tateyama/tgctl/*.cpp" "tateyama/authentication/authentication.cpp" + "tateyama/monitor/*.cpp" "tateyama/datastore/*.cpp" "tateyama/process/*.cpp" "tateyama/configuration/*.cpp" "tateyama/utils/*.cpp" "tateyama/session/*.cpp" "tateyama/metrics/*.cpp" + "tateyama/request/*.cpp" ) if (ENABLE_ALTIMETER) list(APPEND TGCTL_SOURCES "tateyama/altimeter/altimeter.cpp") @@ -164,3 +173,36 @@ target_link_libraries(tgctl set_compile_options(tgctl) install_custom(tgctl ${export_name}) + +# for tests +file(GLOB LIB_FOR_TESTS_SOURCES + "tateyama/monitor/*.cpp" +) + +add_library(lib_for_tests + ${GENERATED_PROTO_SRCS} + ${LIB_FOR_TESTS_SOURCES} +) +add_dependencies(lib_for_tests + build_protos +) +set_target_properties(${ENGINE} + PROPERTIES + OUTPUT_NAME "lib_for_tests" + ) +target_link_libraries(lib_for_tests + PRIVATE protobuf::libprotobuf +) +target_include_directories(lib_for_tests + PRIVATE ${CMAKE_BINARY_DIR}/src +) + +add_library(lib_for_tests-impl INTERFACE) + +target_include_directories(lib_for_tests-impl + INTERFACE ${CMAKE_BINARY_DIR}/src +) + +target_link_libraries(lib_for_tests-impl + INTERFACE lib_for_tests +) diff --git a/src/jogasaki/proto/sql/common.proto b/src/jogasaki/proto/sql/common.proto new file mode 100644 index 0000000..ea8c05e --- /dev/null +++ b/src/jogasaki/proto/sql/common.proto @@ -0,0 +1,231 @@ +syntax = "proto3"; + +package jogasaki.proto.sql.common; + +option java_multiple_files = false; +option java_package = "com.tsurugidb.sql.proto"; +option java_outer_classname = "SqlCommon"; + +/* + * Common. + */ + +/* For session handle. */ +message Session { + uint64 handle = 1; +} + +/* For transaction handle. */ +message Transaction { + uint64 handle = 1; +} + +/* For transaction referenceable id. */ +message TransactionId { + string id = 1; +} + +/* For prepared statement handle. */ +message PreparedStatement { + uint64 handle = 1; + bool has_result_records = 2; +} + +enum AtomType { + // unspecified type. + TYPE_UNSPECIFIED = 0; + + // boolean type. + BOOLEAN = 1; + + reserved 2, 3; + + // 32-bit signed integer. + INT4 = 4; + + // 64-bit signed integer. + INT8 = 5; + + // 32-bit floating point number. + FLOAT4 = 6; + + // 64-bit floating point number. + FLOAT8 = 7; + + // multi precision decimal number. + DECIMAL = 8; + + // character sequence. + CHARACTER = 9; + + reserved 10; + + // octet sequence. + OCTET = 11; + + reserved 12; + + // bit sequence. + BIT = 13; + + reserved 14; + + // date. + DATE = 15; + + // time of day. + TIME_OF_DAY = 16; + + // time point. + TIME_POINT = 17; + + // date-time interval. + DATETIME_INTERVAL = 18; + + // time of day with time zone. + TIME_OF_DAY_WITH_TIME_ZONE = 19; + + // time point with time zone. + TIME_POINT_WITH_TIME_ZONE = 20; + + // character large objects. + CLOB = 21; + + // binary large objects. + BLOB = 22; + + reserved 23 to 30; + + // unknown type. + UNKNOWN = 31; + + reserved 32 to 99; +} + +// the user defined type. +message UserType { + // the type name. + string name = 1; +} + +// the column of relation or row type. +message Column { + // the optional column name. + string name = 1; + + // the column type. + oneof type_info { + // the atom type. + AtomType atom_type = 2; + // the row type. + RowType row_type = 3; + // the user defined type. + UserType user_type = 4; + } + reserved 5 to 10; + + // the type dimension for array types. + uint32 dimension = 11; + + // FIXME type details (e.g. decimal precisions) +} + +// the row type. +message RowType { + // the columns of the row. + repeated Column columns = 1; +} + +// pseudo type structure to represent types. +message TypeInfo { + // the column type. + oneof type_info { + // the atom type. + AtomType atom_type = 1; + // the row type. + RowType row_type = 2; + // the user defined type. + UserType user_type = 3; + } + reserved 4 to 10; + + // the type dimension for array types. + uint32 dimension = 11; +} + +// the multi precision decimal number value. +message Decimal { + // the signed unscaled value (2's complement, big endian). + bytes unscaled_value = 1; + + // the exponent of the value (value = unscaled_value * 10^exponent). + int32 exponent = 2; +} + +// the bit value. +message Bit { + // packed bit sequence (little-endian, from LST to MSB). + bytes packed = 1; + // the number of bits. + uint64 size = 2; +} + +// the time-of-day-with-time-zone value. +message TimeOfDayWithTimeZone { + // offset nano-seconds from epoch (00:00:00) in the time zone. + uint64 offset_nanoseconds = 1; + // timezone offset in minute. + sint32 time_zone_offset = 2; +} + +// the time-point value. +message TimePoint { + // offset seconds from epoch (1970-01-01 00:00:00). + sint64 offset_seconds = 1; + // nano-seconds adjustment [0, 10^9-1]. + uint32 nano_adjustment = 2; +} + +// the time-point-with-time-zone value. +message TimePointWithTimeZone { + // offset seconds from epoch (1970-01-01 00:00:00) in the time zone. + sint64 offset_seconds = 1; + // nano-seconds adjustment [0, 10^9-1]. + uint32 nano_adjustment = 2; + // timezone offset in minute. + sint32 time_zone_offset = 3; +} + +// the date-time interval value. +message DateTimeInterval { + // years offset. + sint32 year = 1; + // months offset. + sint32 month = 2; + // days offset. + sint32 day = 3; + // nano-seconds offset. + sint32 time = 4; +} + +// the character large object value. +message Clob { + // FIXME impl +} + +// the binary large object value. +message Blob { + // FIXME impl +} + +// unit for time and timestamp value +enum TimeUnit { + // unit unspecified. + TIME_UNIT_UNSPECIFIED = 0; + // unit nano-second. + NANOSECOND = 1; + // unit micro-second. + MICROSECOND = 2; + // unit milli-second. + MILLISECOND = 3; +} diff --git a/src/jogasaki/proto/sql/error.proto b/src/jogasaki/proto/sql/error.proto new file mode 100644 index 0000000..9e5bcb0 --- /dev/null +++ b/src/jogasaki/proto/sql/error.proto @@ -0,0 +1,195 @@ +syntax = "proto3"; + +package jogasaki.proto.sql.error; + +option java_package = "com.tsurugidb.sql.proto"; +option java_outer_classname = "SqlError"; + +/* + * Definition of error information for Response. + */ + +// the error code for sql execution +enum Code { + // code not specified + CODE_UNSPECIFIED = 0; + + // SQL-01000 generic error in SQL service + SQL_SERVICE_EXCEPTION = 1; + + // SQL-02000 generic error in SQL execution + SQL_EXECUTION_EXCEPTION = 2; + + // SQL-02001 constraint Violation + CONSTRAINT_VIOLATION_EXCEPTION = 3; + + // SQL-02002 unique constraint violation + UNIQUE_CONSTRAINT_VIOLATION_EXCEPTION = 4; + + // SQL-02003 not-null constraint violation + NOT_NULL_CONSTRAINT_VIOLATION_EXCEPTION = 5; + + // SQL-02004 referential integrity constraint violation + REFERENTIAL_INTEGRITY_CONSTRAINT_VIOLATION_EXCEPTION = 6; + + // SQL-02005 check constraint violation + CHECK_CONSTRAINT_VIOLATION_EXCEPTION = 7; + + // SQL-02010 error in expression evaluation + EVALUATION_EXCEPTION = 8; + + // SQL-02011 error in value evaluation + VALUE_EVALUATION_EXCEPTION = 9; + + // SQL-02012 non-scalar results from scalar subquery + SCALAR_SUBQUERY_EVALUATION_EXCEPTION = 10; + + // SQL-02014 SQL operation target is not found + TARGET_NOT_FOUND_EXCEPTION = 11; + + // SQL-02016 target already exists for newly creation request + TARGET_ALREADY_EXISTS_EXCEPTION = 12; + + // SQL-02018 statement is inconsistent with the request + INCONSISTENT_STATEMENT_EXCEPTION = 13; + + // SQL-02020 restricted operation was requested + RESTRICTED_OPERATION_EXCEPTION = 14; + + // SQL-02021 deletion was requested for the object with dependencies on others + DEPENDENCIES_VIOLATION_EXCEPTION = 15; + + // SQL-02022 write operation was requested using RTX + WRITE_OPERATION_BY_RTX_EXCEPTION = 16; + + // SQL-02023 LTX write operation was requested outside of write preserve + LTX_WRITE_OPERATION_WITHOUT_WRITE_PRESERVE_EXCEPTION = 17; + + // SQL-02024 read operation was requested on restricted read area + READ_OPERATION_ON_RESTRICTED_READ_AREA_EXCEPTION = 18; + + // SQL-02025 operation was requested using transaction that had already committed or aborted + INACTIVE_TRANSACTION_EXCEPTION = 19; + + // SQL-02027 error on parameters or placeholders + PARAMETER_EXCEPTION = 20; + + // SQL-02028 requested statement has unresolved placeholders + UNRESOLVED_PLACEHOLDER_EXCEPTION = 21; + + // SQL-02030 error on files for load + LOAD_FILE_EXCEPTION = 22; + + // SQL-02031 target load file is not found + LOAD_FILE_NOT_FOUND_EXCEPTION = 23; + + // SQL-02032 unexpected load file format + LOAD_FILE_FORMAT_EXCEPTION = 24; + + // SQL-02033 error on files for dump + DUMP_FILE_EXCEPTION = 25; + + // SQL-02034 dump directory is not accessible + DUMP_DIRECTORY_INACCESSIBLE_EXCEPTION = 26; + + // SQL-02036 the requested operation reached the SQL limit + SQL_LIMIT_REACHED_EXCEPTION = 27; + + // SQL-02037 the number of running transactions exceeded the maximum limit allowed, and new transaction failed to start + TRANSACTION_EXCEEDED_LIMIT_EXCEPTION = 28; + + // SQL-02039 SQL request timed out + SQL_REQUEST_TIMEOUT_EXCEPTION = 29; + + // SQL-02041 detected data corruption + DATA_CORRUPTION_EXCEPTION = 30; + + // SQL-02042 detected secondary index data corruption + SECONDARY_INDEX_CORRUPTION_EXCEPTION = 31; + + // SQL-02044 request failed before starting processing (e.g. due to pre-condition not fulfilled) + REQUEST_FAILURE_EXCEPTION = 32; + + // SQL-02045 requested transaction is not found (or already released) + TRANSACTION_NOT_FOUND_EXCEPTION = 33; + + // SQL-02046 requested statement is not found (or already released) + STATEMENT_NOT_FOUND_EXCEPTION = 34; + + // SQL-02048 detected internal error + INTERNAL_EXCEPTION = 35; + + // SQL-02050 unsupported runtime feature was requested + UNSUPPORTED_RUNTIME_FEATURE_EXCEPTION = 36; + + // SQL-02052 tried to execute operations with priority to higher priority transactions + BLOCKED_BY_HIGH_PRIORITY_TRANSACTION_EXCEPTION = 37; + + // SQL-02054 invalid value was used in runtime + INVALID_RUNTIME_VALUE_EXCEPTION = 38; + + // SQL-02056 value out of allowed range was used + VALUE_OUT_OF_RANGE_EXCEPTION = 39; + + // SQL-02058 variable length value was used exceeding the allowed maximum length + VALUE_TOO_LONG_EXCEPTION = 40; + + // SQL-02060 used value was not valid for the decimal type + INVALID_DECIMAL_VALUE_EXCEPTION = 41; + + reserved 42 to 100; + + // SQL-03000 compile error + COMPILE_EXCEPTION = 101; + + // SQL-03001 syntax error + SYNTAX_EXCEPTION = 102; + + // SQL-03002 analyze error + ANALYZE_EXCEPTION = 103; + + // SQL-03003 error on types + TYPE_ANALYZE_EXCEPTION = 104; + + // SQL-03004 error on symbols + SYMBOL_ANALYZE_EXCEPTION = 105; + + // SQL-03005 error on values + VALUE_ANALYZE_EXCEPTION = 106; + + // SQL-03010 unsupported feature/syntax was requested + UNSUPPORTED_COMPILER_FEATURE_EXCEPTION = 107; + + reserved 108 to 200; + + // SQL-04000 error in CC serialization + CC_EXCEPTION = 201; + + // SQL-04001 OCC aborted + OCC_EXCEPTION = 202; + + // SQL-04010 OCC aborted due to its read + OCC_READ_EXCEPTION = 203; + + // SQL-04015 OCC (early) aborted because it read other LTX's write preserve + CONFLICT_ON_WRITE_PRESERVE_EXCEPTION = 204; + + // SQL-04011 OCC aborted due to its write + OCC_WRITE_EXCEPTION = 205; + + // SQL-04003 LTX aborted + LTX_EXCEPTION = 206; + + // SQL-04013 LTX aborted due to its read + LTX_READ_EXCEPTION = 207; + + // SQL-04014 LTX aborted due to its write + LTX_WRITE_EXCEPTION = 208; + + // SQL-04005 RTX aborted + RTX_EXCEPTION = 209; + + // SQL-04007 request was blocked by the other operations executed concurrently + BLOCKED_BY_CONCURRENT_OPERATION_EXCEPTION = 210; + +} diff --git a/src/jogasaki/proto/sql/request.proto b/src/jogasaki/proto/sql/request.proto new file mode 100644 index 0000000..cdd9579 --- /dev/null +++ b/src/jogasaki/proto/sql/request.proto @@ -0,0 +1,483 @@ +syntax = "proto3"; + +package jogasaki.proto.sql.request; + +option java_multiple_files = false; +option java_package = "com.tsurugidb.sql.proto"; +option java_outer_classname = "SqlRequest"; + +import "jogasaki/proto/sql/common.proto"; + +/* + * Definition of sub fields for Request. + */ + +// the placeholder for the prepared statements. +message Placeholder { + // the placeholder location. + oneof placement { + // the placeholder name. + string name = 2; + } + reserved 3 to 10; + + // the placeholder type. + oneof type_info { + // the atom type. + common.AtomType atom_type = 11; + // the row type. + common.RowType row_type = 12; + // the user defined type. + common.UserType user_type = 13; + } + reserved 14 to 19; + + // the type dimension for array types. + uint32 dimension = 20; +} + +// the placeholder replacements. +message Parameter { + // the placeholder location. + oneof placement { + // the placeholder name. + string name = 2; + } + reserved 3 to 10; + + // the replacement values (unset describes NULL). + oneof value { + // boolean type. + bool boolean_value = 11; + + // 32-bit signed integer. + sint32 int4_value = 14; + + // 64-bit signed integer. + sint64 int8_value = 15; + + // 32-bit floating point number. + float float4_value = 16; + + // 64-bit floating point number. + double float8_value = 17; + + // multi precision decimal number. + common.Decimal decimal_value = 18; + + // character sequence. + string character_value = 19; + + // octet sequence. + bytes octet_value = 21; + + // bit sequence. + common.Bit bit_value = 23; + + // date (number of days offset of epoch 1970-01-01). + sint64 date_value = 25; + + // time of day (nano-seconds since 00:00:00). + uint64 time_of_day_value = 26; + + // time point. + common.TimePoint time_point_value = 27; + + // date-time interval. + common.DateTimeInterval datetime_interval_value = 28; + + // time of day with time zone. + common.TimeOfDayWithTimeZone time_of_day_with_time_zone_value = 29; + + // time point with time zone. + common.TimePointWithTimeZone time_point_with_time_zone_value = 30; + + // reference column position (for load action). + uint64 reference_column_position = 51; + + // reference column name (for load action). + string reference_column_name = 52; + } + + reserved 12, 13, 20, 22, 24, 31 to 40, 41 to 50, 53 to 99; +} + +// the parameter set for the statement. +message ParameterSet { + // a list of parameters. + repeated Parameter elements = 1; +} + +// the transaction type. +enum TransactionType { + // use default transaction type. + TRANSACTION_TYPE_UNSPECIFIED = 0; + // short transactions (optimistic concurrency control). + SHORT = 1; + // long transactions (pessimistic concurrency control). + LONG = 2; + // read only transactions (may be abort-free). + READ_ONLY = 3; +} + +// the transaction priority. +enum TransactionPriority { + // use default transaction priority. + TRANSACTION_PRIORITY_UNSPECIFIED = 0; + // halts the running transactions immediately. + INTERRUPT = 1; + // prevents new transactions and waits for the running transactions will end. + WAIT = 2; + // halts the running transactions immediately, and keep lock-out until its end. + INTERRUPT_EXCLUDE = 3; + // prevents new transactions and waits for the running transactions will end, and keep lock-out until its end. + WAIT_EXCLUDE = 4; +} + +// individual write preservation entry. +message WritePreserve { + // the target table name to preserve for writes. + string table_name = 1; +} + +// individual read area entry. +message ReadArea { + // the target table name of read area, used by inclusive_read_areas and exclusive_read_areas. + string table_name = 1; +} + +// options for beginning transactions +message TransactionOption { + // the transaction type. + TransactionType type = 1; + + // the transaction priority. + TransactionPriority priority = 2; + + // the transaction label. + string label = 3; + + // flag indicating whether the operation will change the table definition or not. + bool modifies_definitions = 4; + + reserved 5 to 10; + + // write preservations for long transactions. + repeated WritePreserve write_preserves = 11; + + // inclusive read areas for long transactions. + repeated ReadArea inclusive_read_areas = 12; + + // exclusive read areas for long transactions. + repeated ReadArea exclusive_read_areas = 13; + + reserved 14 to 20; +} + +// the transaction commit status. +enum CommitStatus { + // the default commit status (rely on the database settings). + COMMIT_STATUS_UNSPECIFIED = 0; + // commit operation has accepted, and the transaction will never abort except system errors. + ACCEPTED = 10; + // commit data has been visible for others. + AVAILABLE = 20; + // commit data has been saved on the local disk. + STORED = 30; + // commit data has been propagated to the all suitable nodes. + PROPAGATED = 40; +} + +/* + * Each request message + */ + +/* For begin request. */ +message Begin { + TransactionOption option = 1; +} + +/* For prepare request. */ +message Prepare { + string sql = 1; + repeated Placeholder placeholders = 2; +} + +/* For execute statement request. */ +message ExecuteStatement { + common.Transaction transaction_handle = 1; + string sql = 2; +} + +/* For execute query request. */ +message ExecuteQuery { + common.Transaction transaction_handle = 1; + string sql = 2; +} + +/* For execute prepared statement request. */ +message ExecutePreparedStatement { + common.Transaction transaction_handle = 1; + common.PreparedStatement prepared_statement_handle = 2; + repeated Parameter parameters = 3; +} + +/* For execute prepared query request. */ +message ExecutePreparedQuery { + common.Transaction transaction_handle = 1; + common.PreparedStatement prepared_statement_handle = 2; + repeated Parameter parameters = 3; +} + +// execute a statement with 2-D parameter table. +message Batch { + reserved 1 to 10; + + // the transaction ID. + common.Transaction transaction_handle = 11; + + // the statement ID. + common.PreparedStatement prepared_statement_handle = 12; + + // the 2-D parameter table. + repeated ParameterSet parameter_sets = 13; +} + +// Option for output files on dump failure +enum DumpFailBehavior { + // use default behavior + DUMP_FAIL_BEHAVIOR_UNSPECIFIED = 0; + + // delete output files for the failed execution + DELETE_FILES = 1; + + // keep output files even if the dump execution fails + KEEP_FILES = 2; +} + +// individual columns settings of ParquetFileFormat. +message ParquetColumnFormat { + // the target column name. + string name = 1; + + // column compression codec name (overwrites the file format setting). + string codec = 2; + + // column compression type name (overwrites the file format setting). + string encoding = 3; +} + +// dump file format for Apache Parquet. +message ParquetFileFormat { + // the parquet file format version. + string parquet_version = 1; + + // the maximum number of rows in the same row group. + int64 record_batch_size = 2; + + // the approximately maximum row group size in bytes. + int64 record_batch_in_bytes = 3; + + // common compression codec name of the individual columns. + string codec = 4; + + // common encoding type of the individual columns. + string encoding = 5; + + reserved 6 to 10; + + // settings of each column. + repeated ParquetColumnFormat columns = 11; +} + +// CHAR column metadata type for Arrow files. +enum ArrowCharacterFieldType { + // use default metadata type for CHAR columns. + ARROW_CHARACTER_FIELD_TYPE_UNSPECIFIED = 0; + + // use StringBuilder for CHAR columns. + STRING = 1; + + // use FixedSizeBinaryBuilder for CHAR columns. + FIXED_SIZE_BINARY = 2; +} + +// dump file format for Apache Arrow. +message ArrowFileFormat { + // the metadata format version. + string metadata_version = 1; + + // the byte alignment of each values. + int32 alignment = 2; + + // the maximum number of records in record batch. + int64 record_batch_size = 3; + + // the approximately maximum size of each record batch in bytes. + int64 record_batch_in_bytes = 4; + + // compression codec name. + string codec = 5; + + // threshold for adopting compressed data. + double min_space_saving = 6; + + // CHAR column metadata type. + ArrowCharacterFieldType character_field_type = 7; +} + +// options for dump request +message DumpOption { + reserved 1 to 10; + + // Behavior on failure + DumpFailBehavior fail_behavior = 11; + + // record count limit for dump output file + uint64 max_record_count_per_file = 12; + + // time unit used for timestamp columns + common.TimeUnit timestamp_unit = 13; + + reserved 14 to 20; + + // dump output file format specification. + oneof file_format { + + // dump tables as Apache Parquet format. + ParquetFileFormat parquet = 21; + + // dump tables as Apache Arrow format. + ArrowFileFormat arrow = 22; + + } +} + +/* For execute dump request. */ +message ExecuteDump { + common.Transaction transaction_handle = 1; + common.PreparedStatement prepared_statement_handle = 2; + repeated Parameter parameters = 3; + string directory = 4; + DumpOption option = 5; +} + +/* For execute load request. */ +message ExecuteLoad { + // optional transaction handle (empty for non-transactional load). + oneof transaction_handle_opt { + common.Transaction transaction_handle = 1; + } + common.PreparedStatement prepared_statement_handle = 2; + repeated Parameter parameters = 3; + repeated string file = 4; +} + +/* For commit request. */ +message Commit { + common.Transaction transaction_handle = 1; + + // response will be returned after reaching the commit status. + CommitStatus notification_type = 2; + + // dispose the target transaction handle only if notifies a successfully commit to the client. + bool auto_dispose = 3; +} + +/* For rollback request. */ +message Rollback { + common.Transaction transaction_handle = 1; +} + +/* For dispose prepared sql. */ +message DisposePreparedStatement { + common.PreparedStatement prepared_statement_handle = 1; +} + +/* For explain text sql. */ +message ExplainByText { + string sql = 1; +} + +/* For explain prepared sql. */ +message Explain { + common.PreparedStatement prepared_statement_handle = 1; + repeated Parameter parameters = 2; +} + +// describe about the table. +message DescribeTable { + reserved 1 to 10; + + // the table name to describe. + string name = 11; +} + +// describe available table names in the database. +message ListTables { + reserved 1 to 10; +} + +// describe the current search path. +message GetSearchPath { + reserved 1 to 10; +} + +// retrieves error information of the transaction. +message GetErrorInfo { + // the target transaction handle. + common.Transaction transaction_handle = 1; +} + +// occurred when the client side transaction handle is disposed. +message DisposeTransaction { + // the target transaction handle. + common.Transaction transaction_handle = 1; +} + +// Requests to extract executing statement info in payload data. +message ExtractStatementInfo { + + // session ID + uint64 session_id = 1; + + // payload data as plain byte array. + bytes payload = 2; +} + +/* For request message to the SQL service. */ +message Request { + common.Session session_handle = 1; + oneof request { + Begin begin = 2; + Prepare prepare = 3; + ExecuteStatement execute_statement = 4; + ExecuteQuery execute_query = 5; + ExecutePreparedStatement execute_prepared_statement = 6; + ExecutePreparedQuery execute_prepared_query = 7; + Commit commit = 8; + Rollback rollback = 9; + DisposePreparedStatement dispose_prepared_statement = 10; + // Disconnect disconnect = 11; + Explain explain = 12; + ExecuteDump execute_dump = 13; + ExecuteLoad execute_load = 14; + DescribeTable describe_table = 15; + Batch batch = 16; + ListTables listTables = 17; + GetSearchPath getSearchPath = 18; + GetErrorInfo get_error_info = 19; + DisposeTransaction dispose_transaction = 20; + ExplainByText explain_by_text = 21; + ExtractStatementInfo extract_statement_info = 22; + } + + reserved 23 to 99; + + // service message version (major) + uint64 service_message_version_major = 100; + + // service message version (minor) + uint64 service_message_version_minor = 101; + +} diff --git a/src/jogasaki/proto/sql/response.proto b/src/jogasaki/proto/sql/response.proto new file mode 100644 index 0000000..821cb40 --- /dev/null +++ b/src/jogasaki/proto/sql/response.proto @@ -0,0 +1,326 @@ +syntax = "proto3"; + +package jogasaki.proto.sql.response; + +option java_multiple_files = false; +option java_package = "com.tsurugidb.sql.proto"; +option java_outer_classname = "SqlResponse"; + +import "jogasaki/proto/sql/common.proto"; +import "jogasaki/proto/sql/error.proto"; + +/* + * Definition of sub fields for Response. + */ + +/* For response of success when there is no data to return. */ +message Success { +} + +/* For response of error containing a error message. */ +message Error { + + reserved 1; + + // error message text + string detail = 2; + + // error code + error.Code code = 3; + + // supplemental text for debug purpose + string supplemental_text = 4; +} + + +/* + * Each response message + */ + +/* For response to ExecuteQuery, ExecutePreparedQuery, Commit, Rollback, +DisposePreparedStatement, DisposeTransaction and ExecuteDump. */ +message ResultOnly { + oneof result { + Success success = 1; + Error error = 2; + } +} + +/* For response to Begin. */ +message Begin { + + // request is successfully completed. + message Success { + + // the transaction handle. + common.Transaction transaction_handle = 1; + + // the transaction id for reference. + common.TransactionId transaction_id = 2; + } + + // the response body. + oneof result { + // request is successfully completed. + Success success = 1; + + // engine error occurred. + Error error = 2; + } +} + +/* For response to Prepare. */ +message Prepare { + oneof result { + common.PreparedStatement prepared_statement_handle = 1; + Error error = 2; + } +} + +/* For response to ExecuteQuery and/or ExecutePreparedQuery. */ +message ExecuteQuery { + string name = 1; /* The name of the channel to which the ResultSet set will be sent. */ + ResultSetMetadata record_meta = 2; +} + +/* For response to Explain. */ +message Explain { + + // request is successfully completed. + message Success { + + // the content format ID. + string format_id = 1; + + // the content format version. + uint64 format_version = 2; + + // the explain result contents. + string contents = 3; + + // the result set column information, or empty if it does not provided. + repeated common.Column columns = 4; + } + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + Error error = 2; + } +} + +// describe about a table. +message DescribeTable { + reserved 1 to 10; + + // request is successfully completed. + message Success { + + // the database name. + string database_name = 1; + + // the schema name. + string schema_name = 2; + + // the table name. + string table_name = 3; + + // the table column information. + repeated common.Column columns = 4; + } + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // engine error was occurred. + Error error = 12; + } +} + +message Identifier { + // the label. + string label = 1; +} + +message Name { + // the identfiers. + repeated Identifier identifiers = 1; +} + +// execute a list tables. +message ListTables { + reserved 2 to 10; + + // request is successfully completed. + message Success { + // the table path names. + repeated Name table_path_names = 1; + } + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // engine error was occurred. + Error error = 12; + } +} + +// execute a getSearchPath. +message GetSearchPath { + reserved 2 to 10; + // request is successfully completed. + + message Success { + // the search path. + repeated Name search_paths = 1; + } + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // engine error was occurred. + Error error = 12; + } +} + +// empty message. +message Void {} + +// retrieves error information of the transaction. +message GetErrorInfo { + // the response body. + oneof result { + // operation was successfully completed and error information was found. + Error success = 1; + + // operation was successfully completed but error information was absent. + Void error_not_found = 2; + + // engine error occurred while the retrieving existing error information. + Error error = 3; + } +} + +// execute a DisposeTransaction. +message DisposeTransaction { + // the response body. + oneof result { + // operation was successfully completed and error information was found. + Void success = 1; + + // engine error occurred while the retrieving existing error information. + Error error = 2; + } +} + +/* For response to ExecuteStatement, ExecutePreparedStatement and ExecuteLoad. */ +message ExecuteResult { + reserved 1 to 10; + + // request is successfully completed. + message Success { + + // group of counters during SQL execution. + repeated CounterEntry counters = 1; + } + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // engine error was occurred. + Error error = 12; + } + + // a counted item. + message CounterEntry { + // the counter type. + CounterType type = 1; + + // the count. + int64 value = 2; + } + + // a kind of execution counter. + enum CounterType { + // the un-categorized counter type. + COUNTER_TYPE_UNSPECIFIED = 0; + + // The number of rows inserted in the execution. + INSERTED_ROWS = 10; + + // The number of rows updated in the execution. + UPDATED_ROWS = 20; + + // The number of rows merged in the execution. + MERGED_ROWS = 30; + + // The number of rows deleted in the execution. + DELETED_ROWS = 40; + } +} + +/* For response message from the SQL service. */ +message Response { + oneof response { + ResultOnly result_only = 1; + Begin begin = 2; + Prepare prepare = 3; + ExecuteQuery execute_query = 4; + Explain explain = 5; + DescribeTable describe_table = 6; + // 7 is no longer used. + ListTables list_tables = 8; + GetSearchPath get_search_path = 9; + GetErrorInfo get_error_info = 10; + DisposeTransaction dispose_transaction = 11; + ExecuteResult execute_result = 12; + } +} + +// metadata of result sets. +message ResultSetMetadata { + + // the column information. + repeated common.Column columns = 1; +} + +// Response of ExtractStatementInfo. +message ExtractStatementInfo { + + reserved 1 to 10; + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // error was occurred. + Error error = 12; + } + + // request is successfully completed. + message Success { + + // the transaction ID for the statement. + oneof transaction_id_opt { + // the corresponding transaction ID. + jogasaki.proto.sql.common.TransactionId transaction_id = 1; + } + + // the executing SQL text of the statement. + oneof sql_opt { + // SQL statement string. + string sql = 2; + } + } +} diff --git a/src/tateyama/monitor/constants.h b/src/tateyama/monitor/constants.h new file mode 100644 index 0000000..d599df8 --- /dev/null +++ b/src/tateyama/monitor/constants.h @@ -0,0 +1,141 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +namespace tateyama::monitor { + +enum class status : std::int64_t { + stop = 0, + ready = 1, + activated = 2, + deactivating = 3, + deactivated = 4, + boot_error = 10, + + unknown = -1, +}; + +enum class reason : std::int64_t { + absent, // no error + + // old type reason + connection, + not_found, + ambiguous, + permission, + variable_not_defined, + variable_invalid_value, + + // general + authentication_failure, + connection_timeout, + connection_failure, + io, + server, + interrupted, + internal, + + // request + request_missing, + payload_broken, + sql_missing, + sql_unresolved, + + unknown = -1, +}; + +/** + * @brief returns string representation of the value. + * @param value the target value + * @return the corresponded string representation + */ +[[nodiscard]] constexpr inline std::string_view to_string_view(status value) noexcept { + using namespace std::string_view_literals; + switch (value) { + case status::stop:return "stop"sv; + case status::ready: return "starting"sv; + case status::activated: return "running"sv; + case status::deactivating: return "shutdown"sv; + case status::deactivated: return "shutdown"sv; + case status::boot_error: return "boot_error"sv; + case status::unknown: return "disconnected"sv; + } + return "illegal state"sv; +} + +/** + * @brief returns string representation of the value. + * @param value the target value + * @return the corresponded string representation + */ +[[nodiscard]] constexpr inline std::string_view to_string_view(reason value) noexcept { + using namespace std::string_view_literals; + switch (value) { + case reason::absent:return "absent"sv; + case reason::connection:return "connection"sv; + case reason::server:return "server"sv; + case reason::not_found:return "not_found"sv; + case reason::ambiguous:return "ambiguous"sv; + case reason::permission:return "permission"sv; + case reason::variable_not_defined:return "variable_not_defined"sv; + case reason::variable_invalid_value:return "variable_invalid_value"sv; + + // general + case reason::authentication_failure: return "authentication_failure"sv; + case reason::connection_timeout: return "connection_timeout"sv; + case reason::connection_failure: return "connection_failure"sv; + case reason::io: return "io"sv; + case reason::interrupted: return "interrupted"sv; + case reason::internal: return "internal"sv; + + // request + case reason::request_missing: return "request_missing"sv; + case reason::payload_broken: return "payload_broken"sv; + case reason::sql_missing: return "sql_missing"sv; + case reason::sql_unresolved: return "sql_unresolved"sv; + + case reason::unknown: return "unknown"sv; + } + return "illegal reason"sv; +} + +constexpr static std::string_view TIME_STAMP = R"("timestamp": )"; +constexpr static std::string_view KIND_START = R"("kind": "start")"; +constexpr static std::string_view KIND_FINISH = R"("kind": "finish")"; +constexpr static std::string_view KIND_PROGRESS = R"("kind": "progress")"; +constexpr static std::string_view KIND_DATA = R"("kind": "data")"; +constexpr static std::string_view PROGRESS = R"("progress": )"; +// status +constexpr static std::string_view FORMAT_STATUS = R"("format": "status")"; +constexpr static std::string_view STATUS = R"("status": ")"; +constexpr static std::string_view REASON = R"("reason": ")"; +// session info +constexpr static std::string_view FORMAT_SESSION_INFO = R"("format": "session-info")"; +constexpr static std::string_view SESSION_ID = R"("session_id": )"; +constexpr static std::string_view LABEL = R"("label": ")"; +constexpr static std::string_view APPLICATION = R"("application": ")"; +constexpr static std::string_view USER = R"("user": ")"; +constexpr static std::string_view START_AT = R"("start_at": ")"; +constexpr static std::string_view CONNECTION_TYPE = R"("connection_type": ")"; +constexpr static std::string_view CONNECTION_INFO = R"("connection_info": ")"; +// dbstats +constexpr static std::string_view FORMAT_DBSTATS_DESCRIPTION = R"("format": "dbstats_description")"; +constexpr static std::string_view FORMAT_DBSTATS = R"("format": "dbstats")"; +constexpr static std::string_view METRICS = R"("metrics": ")"; + +} // tateyama::monitor diff --git a/src/tateyama/monitor/constants_request.h b/src/tateyama/monitor/constants_request.h new file mode 100644 index 0000000..430b401 --- /dev/null +++ b/src/tateyama/monitor/constants_request.h @@ -0,0 +1,118 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +namespace tateyama::monitor { + +enum class type_of_sql : std::int64_t { + begin = 0, + commit = 1, + rollback = 2, + prepare = 3, + query = 4, + statement = 5, + explain = 6, + dump = 7, + load = 8, + others = 99, + + unknown = -1, +}; + +/** + * @brief returns string representation of the value. + * @param value the target value + * @return the corresponded string representation + */ +[[nodiscard]] constexpr inline std::string_view to_string_view(type_of_sql value) noexcept { + using namespace std::string_view_literals; + switch (value) { + case type_of_sql::begin:return "begin"sv; + case type_of_sql::commit:return "commit"sv; + case type_of_sql::rollback:return "rollback"sv; + case type_of_sql::prepare:return "prepare"sv; + case type_of_sql::query:return "query"sv; + case type_of_sql::statement:return "statement"sv; + case type_of_sql::explain:return "explain"sv; + case type_of_sql::dump:return "dump"sv; + case type_of_sql::load:return "load"sv; + case type_of_sql::others:return "others"sv; + case type_of_sql::unknown:return "unknown"sv; + } + return "illegal type_of_sql"sv; +} + +static inline bool begins_with(const std::string& sql, const std::string& pattern) { + return (strncasecmp(std::regex_replace(sql, std::regex("^\\s+"), "").c_str(), pattern.c_str(), pattern.length()) == 0); +} + +static inline type_of_sql identify_sql_type(const std::string& sql) { + using namespace std::literals::string_literals; + if (begins_with(sql, "begin"s)) { + return type_of_sql::begin; + } + if (begins_with(sql, "commit"s)) { + return type_of_sql::commit; + } + if (begins_with(sql, "rollback"s)) { + return type_of_sql::rollback; + } + if (begins_with(sql, "prepare"s)) { + return type_of_sql::prepare; + } + if (begins_with(sql, "select"s)) { + return type_of_sql::query; + } + if (begins_with(sql, "insert"s) || + begins_with(sql, "update"s) || + begins_with(sql, "delete"s)) { + return type_of_sql::statement; + } + if (begins_with(sql, "explain"s)) { + return type_of_sql::explain; + } + if (begins_with(sql, "dump"s)) { + return type_of_sql::dump; + } + if (begins_with(sql, "load"s)) { + return type_of_sql::load; + } + return type_of_sql::others; +} + +// request list +constexpr static std::string_view FORMAT_REQUEST_LIST = R"("format": "request_list")"; +constexpr static std::string_view REQUEST_ID = R"("request_id": )"; +constexpr static std::string_view SERVICE_ID = R"("service_id": )"; +constexpr static std::string_view PAYLOAD_SIZE = R"("payload_size": )"; +constexpr static std::string_view ELAPSED_TIME = R"("elapsed_time": )"; + +// request payload +constexpr static std::string_view FORMAT_REQUEST_PAYLOAD = R"("format": "request_payload")"; +constexpr static std::string_view PAYLOAD = R"("payload": ")"; + +// request extract-sql +constexpr static std::string_view FORMAT_REQUEST_EXTRACT_SQL = R"("format": "request_extract-sql")"; +constexpr static std::string_view TYPE = R"("type": ")"; +constexpr static std::string_view TRANSACTION_ID = R"("transaction_id": ")"; +constexpr static std::string_view SQL = R"("sql": ")"; + +} // tateyama::monitor diff --git a/src/tateyama/monitor/monitor.cpp b/src/tateyama/monitor/monitor.cpp new file mode 100644 index 0000000..a4d0241 --- /dev/null +++ b/src/tateyama/monitor/monitor.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "monitor.h" + +namespace tateyama::monitor { + +monitor::monitor(std::string& file_name) : strm_(fstrm_), is_filestream_(true) { + fstrm_.open(file_name, std::ios_base::out | std::ios_base::trunc); +} +monitor::monitor(std::ostream& os) : strm_(os), is_filestream_(false) { +} + +monitor::~monitor() { + if (is_filestream_) { + fstrm_.close(); + } +} + +void monitor::start() { + strm_ << "{ " << TIME_STAMP << time(nullptr) + << ", " << KIND_START << " }" << std::endl; + strm_.flush(); +} + +void monitor::finish(bool status) { + if (status) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_FINISH << ", " << STATUS << R"(success" })" << std::endl; + } else { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_FINISH << ", " << STATUS << R"(failure" })" << std::endl; + } + strm_.flush(); +} + +void monitor::finish(reason rc) { + if (rc == reason::absent) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_FINISH << ", " << STATUS << R"(success" })" << std::endl; + } else { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_FINISH << ", " << STATUS << R"(failure", )" + << REASON << to_string_view(rc) << R"(" })" << std::endl; + } + strm_.flush(); +} + +void monitor::progress(float ratio) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_PROGRESS << ", " << PROGRESS << ratio << " }" << std::endl; + strm_.flush(); +} + +void monitor::status(tateyama::monitor::status stat) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_DATA << ", " << FORMAT_STATUS << ", " << STATUS << to_string_view(stat) << "\" }" << std::endl; + strm_.flush(); +} + +void monitor::session_info(std::string_view session_id, + std::string_view label, + std::string_view application, + std::string_view user, + std::string_view start_at, + std::string_view connection_type, + std::string_view connection_info) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_DATA << ", " << FORMAT_SESSION_INFO << ", " + << SESSION_ID << "\":" << session_id << "\" , " + << LABEL << label << "\", " + << APPLICATION << application << "\", " + << USER << user << "\", " + << START_AT << start_at << "\", " + << CONNECTION_TYPE << connection_type << "\", " + << CONNECTION_INFO << connection_info << "\" }" << std::endl; + strm_.flush(); +} + +void monitor::dbstats_description(std::string_view data) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_DATA << ", " << FORMAT_DBSTATS_DESCRIPTION << ", " + << METRICS << data << "\" }" << std::endl; + strm_.flush(); +} + +void monitor::dbstats(std::string_view data) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_DATA << ", " << FORMAT_DBSTATS << ", " + << METRICS << data << "\" }" << std::endl; + strm_.flush(); +} + +} // tateyama::monitor diff --git a/src/tateyama/monitor/monitor.h b/src/tateyama/monitor/monitor.h index aef65d2..19fb6e4 100644 --- a/src/tateyama/monitor/monitor.h +++ b/src/tateyama/monitor/monitor.h @@ -15,184 +15,57 @@ */ #pragma once -#include -#include -#include +#include #include +#include +#include +#include -namespace tateyama::monitor { - -enum class status : std::int64_t { - stop = 0, - ready = 1, - activated = 2, - deactivating = 3, - deactivated = 4, - boot_error = 10, - - unknown = -1, -}; - -enum class reason : std::int64_t { - absent = 0, - connection = 1, - server = 2, - not_found = 3, - ambiguous = 4, - permission = 5, - variable_not_defined = 6, - variable_invalid_value = 7, - - unknown = -1, -}; +#include "constants.h" +#include "constants_request.h" -/** - * @brief returns string representation of the value. - * @param value the target value - * @return the corresponded string representation - */ -[[nodiscard]] constexpr inline std::string_view to_string_view(status value) noexcept { - using namespace std::string_view_literals; - switch (value) { - case status::stop:return "stop"sv; - case status::ready: return "starting"sv; - case status::activated: return "running"sv; - case status::deactivating: return "shutdown"sv; - case status::deactivated: return "shutdown"sv; - case status::boot_error: return "boot_error"sv; - case status::unknown: return "disconnected"sv; - } - return "illegal state"sv; -} - -/** - * @brief returns string representation of the value. - * @param value the target value - * @return the corresponded string representation - */ -[[nodiscard]] constexpr inline std::string_view to_string_view(reason value) noexcept { - using namespace std::string_view_literals; - switch (value) { - case reason::absent:return "absent"sv; - case reason::connection:return "connection"sv; - case reason::server:return "server"sv; - case reason::not_found:return "not_found"sv; - case reason::ambiguous:return "ambiguous"sv; - case reason::permission:return "permission"sv; - case reason::variable_not_defined:return "variable_not_defined"sv; - case reason::variable_invalid_value:return "variable_invalid_value"sv; - case reason::unknown: return "unknown"sv; - } - return "illegal reason"sv; -} +namespace tateyama::monitor { class monitor { - constexpr static std::string_view TIME_STAMP = R"("timestamp": )"; - constexpr static std::string_view KIND_START = R"("kind": "start")"; - constexpr static std::string_view KIND_FINISH = R"("kind": "finish")"; - constexpr static std::string_view KIND_PROGRESS = R"("kind": "progress")"; - constexpr static std::string_view KIND_DATA = R"("kind": "data")"; - constexpr static std::string_view PROGRESS = R"("progress": )"; - // status - constexpr static std::string_view FORMAT_STATUS = R"("format": "status")"; - constexpr static std::string_view STATUS = R"("status": ")"; - constexpr static std::string_view REASON = R"("reason": ")"; - // session info - constexpr static std::string_view FORMAT_SESSION_INFO = R"("format": "session-info")"; - constexpr static std::string_view SESSION_ID = R"("session_id": ":)"; - constexpr static std::string_view LABEL = R"("label": ")"; - constexpr static std::string_view APPLICATION = R"("application": ")"; - constexpr static std::string_view USER = R"("user": ")"; - constexpr static std::string_view START_AT = R"("start_at": ")"; - constexpr static std::string_view CONNECTION_TYPE = R"("connection_type": ")"; - constexpr static std::string_view CONNECTION_INFO = R"("connection_info": ")"; - // dbstats - constexpr static std::string_view FORMAT_DBSTATS_DESCRIPTION = R"("format": "dbstats_description")"; - constexpr static std::string_view FORMAT_DBSTATS = R"("format": "dbstats")"; - constexpr static std::string_view METRICS = R"("metrics": ")"; - public: - explicit monitor(std::string& file_name) { - strm_.open(file_name, std::ios_base::out | std::ios_base::trunc); - } - ~monitor() { - strm_.close(); - } + explicit monitor(std::string& file_name); + explicit monitor(std::ostream& os); + ~monitor(); monitor(monitor const& other) = delete; monitor& operator=(monitor const& other) = delete; monitor(monitor&& other) noexcept = delete; monitor& operator=(monitor&& other) noexcept = delete; - void start() { - strm_ << "{ " << TIME_STAMP << time(nullptr) - << ", " << KIND_START << " }" << std::endl; - strm_.flush(); - } - void finish(bool status) { - if (status) { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_FINISH << ", " << STATUS << R"(success" })" << std::endl; - } else { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_FINISH << ", " << STATUS << R"(failure" })" << std::endl; - } - strm_.flush(); - } - void finish(reason rc) { - if (rc == reason::absent) { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_FINISH << ", " << STATUS << R"(success" })" << std::endl; - } else { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_FINISH << ", " << STATUS << R"(failure", )" - << REASON << to_string_view(rc) << R"(" })" << std::endl; - } - strm_.flush(); - } - void progress(float ratio) { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_PROGRESS << ", " << PROGRESS << ratio << " }" << std::endl; - strm_.flush(); - } - void status(status stat) { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_DATA << ", " << FORMAT_STATUS << ", " << STATUS << to_string_view(stat) << "\" }" << std::endl; - strm_.flush(); - } + void start(); + void finish(bool status); + void finish(reason rc); + void progress(float ratio); + void status(status stat); void session_info(std::string_view session_id, std::string_view label, std::string_view application, - std::string_view user, + std::string_view user, std::string_view start_at, std::string_view connection_type, - std::string_view connection_info) { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_DATA << ", " << FORMAT_SESSION_INFO << ", " - << SESSION_ID << session_id << "\", " - << LABEL << label << "\", " - << APPLICATION << application << "\", " - << USER << user << "\", " - << START_AT << start_at << "\", " - << CONNECTION_TYPE << connection_type << "\", " - << CONNECTION_INFO << connection_info << "\" }" << std::endl; - strm_.flush(); - } - void dbstats_description(std::string_view data) { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_DATA << ", " << FORMAT_DBSTATS_DESCRIPTION << ", " - << METRICS << data << "\" }" << std::endl; - strm_.flush(); - } - void dbstats(std::string_view data) { - strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " - << KIND_DATA << ", " << FORMAT_DBSTATS << ", " - << METRICS << data << "\" }" << std::endl; - strm_.flush(); - } + std::string_view connection_info); + void dbstats_description(std::string_view data); + void dbstats(std::string_view data); + // request + void request_list(std::size_t session_id, + std::size_t request_id, + std::size_t service_id, + std::size_t payload_size, + std::size_t elapsed_time); + void request_payload(std::string_view payload); + void request_extract_sql(const std::optional& transacion_id, const std::optional& sql); + private: - std::ofstream strm_; + std::ostream& strm_; + bool is_filestream_; + + std::ofstream fstrm_{}; }; -} // tateyama::bootstrap::utils; +} // tateyama::monitor diff --git a/src/tateyama/monitor/monitor_request.cpp b/src/tateyama/monitor/monitor_request.cpp new file mode 100644 index 0000000..029fb83 --- /dev/null +++ b/src/tateyama/monitor/monitor_request.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "monitor.h" + +namespace tateyama::monitor { + +void monitor::request_list(std::size_t session_id, + std::size_t request_id, + std::size_t service_id, + std::size_t payload_size, + std::size_t elapsed_time) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_DATA << ", " << FORMAT_REQUEST_LIST << ", " + << SESSION_ID << session_id << ", " + << REQUEST_ID << request_id << ", " + << SERVICE_ID << service_id << ", " + << PAYLOAD_SIZE << payload_size << ", " + << ELAPSED_TIME << elapsed_time + << " }" << std::endl; + strm_.flush(); +} + +void monitor::request_payload(std::string_view payload) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_DATA << ", " << FORMAT_REQUEST_PAYLOAD << ", " + << PAYLOAD << payload + << "\" }" << std::endl; + strm_.flush(); +} + +void monitor::request_extract_sql(const std::optional& transacion_id, const std::optional& sql) { + strm_ << "{ " << TIME_STAMP << time(nullptr) << ", " + << KIND_DATA << ", " << FORMAT_REQUEST_EXTRACT_SQL; + if (transacion_id) { + strm_ << ", " << TRANSACTION_ID << transacion_id.value() << "\""; + } + if (sql) { + strm_ << ", " << SQL << sql.value() << "\", " << TYPE << to_string_view(identify_sql_type(sql.value())) << "\"";; + } + strm_ << " }" << std::endl; + strm_.flush(); +} + +} // tateyama::monitor diff --git a/src/tateyama/proto/request/diagnostics.proto b/src/tateyama/proto/request/diagnostics.proto new file mode 100644 index 0000000..7b162ad --- /dev/null +++ b/src/tateyama/proto/request/diagnostics.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; + +package tateyama.proto.request.diagnostics; + +// the error code. +enum Code { + // the error code is not set. + ERROR_CODE_NOT_SPECIFIED = 0; + + // the unknown error was occurred. + UNKNOWN = 1; + + // データベース接続時に認証エラーが発生した + AUTHENTICATION_FAILURE = 2; + + // 対象のデータベースへの接続要求がタイムアウトした + CONNECTION_TIMEOUT = 3; + + // 対象のデータベースへの接続に失敗した + CONNECTION_FAILURE = 4; + + // ハンドルできないI/Oエラーが発生した + IO = 5; + + // ハンドルできないサーバーエラーが発生した + SERVER = 6; + + // 割り込みが発生した + INTERRUPTED = 7; + + // アプリケーション側の問題による内部エラー + INTERNAL = 8; + + // 指定したセッションまたはリクエストが存在しない + REQUEST_MISSING = 9; + + // 指定されたペイロードの形式・または内容が正しくない + PAYLOAD_BROKEN = 10; + + // SQL 文字列が存在しない種類のリクエスト + SQL_MISSING = 11; + + // 利用不可能な prepared statement が含まれていた + SQL_UNRESOLVED = 12; +} diff --git a/src/tateyama/proto/request/request.proto b/src/tateyama/proto/request/request.proto new file mode 100644 index 0000000..b9b1bf6 --- /dev/null +++ b/src/tateyama/proto/request/request.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +package tateyama.proto.request.request; + +option java_multiple_files = false; +option java_package = "com.tsurugidb.request.proto"; +option java_outer_classname = "RequestRequest"; + +// Request to retrieve a list of ongoing requests. +message ListRequest { + + // retrieves requests in order of uptime, or all requests if not specified + oneof top_opt { + // only retrieves top-N uptime requests + uint32 top = 1; + } + + // optional filter by service ID + oneof service_filter { + // the service ID of requests to be retrieved + uint32 service_id = 2; + } +} + +// Request to retrieve payload data. +message GetPayload { + + // session ID + uint64 session_id = 1; + + // request local ID + uint64 request_id = 2; +} + +// the request message to request pseudo service. +message Request { + // service message version (major) + uint64 service_message_version_major = 1; + + // service message version (minor) + uint64 service_message_version_minor = 2; + + // reserved for system use + reserved 3 to 10; + + // the request command. + oneof command { + // Request to retrieve a list of ongoing requests. + ListRequest list_request = 11; + + // Request to retrieve payload data. + GetPayload get_payload = 12; + } + reserved 13 to 99; +} diff --git a/src/tateyama/proto/request/response.proto b/src/tateyama/proto/request/response.proto new file mode 100644 index 0000000..af97269 --- /dev/null +++ b/src/tateyama/proto/request/response.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; + +package tateyama.proto.request.response; + +option java_multiple_files = false; +option java_package = "com.tsurugidb.request.proto"; +option java_outer_classname = "RequestResponse"; + +import "tateyama/proto/request/diagnostics.proto"; + +// unknown error was occurred. +message Error { + // the error message. + string message = 1; + + // error code + diagnostics.Code code = 2; + + // supplemental text for debug purpose + string supplemental_text = 4; +} + +// Represents individual requests in the request list. +message RequestInfo { + + // session ID + uint64 session_id = 1; + + // request local ID (unique within a session) + uint64 request_id = 2; + + // destination service ID + uint64 service_id = 3; + + // payload size in bytes + uint64 payload_size = 4; + + // the request started time since UNIX epoch in milliseconds. + uint64 started_time = 5; +} + +// Response of ListRequest. +message ListRequest { + + reserved 1 to 10; + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // error was occurred. + Error error = 12; + } + + // request is successfully completed. + message Success { + + // list of retrieved requests. + repeated response.RequestInfo requests = 1; + } +} + +// Response of GetPayload. +message GetPayload { + + reserved 1 to 10; + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // error was occurred. + Error error = 12; + } + + // request is successfully completed. + message Success { + + // payload data as plain byte array. + bytes data = 1; + } +} diff --git a/src/tateyama/request/base64.h b/src/tateyama/request/base64.h new file mode 100644 index 0000000..0047c02 --- /dev/null +++ b/src/tateyama/request/base64.h @@ -0,0 +1,43 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +namespace tateyama::request { + +using namespace boost::archive::iterators; +using InputItr = std::istreambuf_iterator; +using OutputItr = std::ostream_iterator; +using EncodeItr = base64_from_binary>; +using DecodeItr = transform_width, 8, 6, char>; + +template +inline OutputStream& encode(InputStream& is, OutputStream& os) { + std::copy(static_cast(InputItr(is)), static_cast(InputItr()), OutputItr(os)); + return os; +} + +template +inline OutputStream& decode(InputStream& is, OutputStream& os) { + std::copy(static_cast(InputItr(is)), static_cast(InputItr()), OutputItr(os)); + return os; +} + +} diff --git a/src/tateyama/request/request.cpp b/src/tateyama/request/request.cpp new file mode 100644 index 0000000..57e087f --- /dev/null +++ b/src/tateyama/request/request.cpp @@ -0,0 +1,249 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include "tateyama/authentication/authentication.h" +#include +#include +#include +#include + +#include +#include + +#include "base64.h" +#include "request.h" + +DEFINE_int32(top, 0, "show top"); // NOLINT +DEFINE_int32(service, 3, "service id"); // NOLINT +DECLARE_bool(quiet); +DECLARE_string(monitor); + +namespace tateyama::request { + +tgctl::return_code request_list() { // NOLINT(readability-function-cognitive-complexity) + std::unique_ptr monitor_output{}; + + if (!FLAGS_monitor.empty()) { + monitor_output = std::make_unique(FLAGS_monitor); + monitor_output->start(); + } + + auto rtnv = tgctl::return_code::ok; + authentication::auth_options(); + try { + auto transport = std::make_unique(tateyama::framework::service_id_request); + ::tateyama::proto::request::request::Request request{}; + (void) request.mutable_list_request(); + auto response_opt = transport->send<::tateyama::proto::request::response::ListRequest>(request); + request.clear_list_request(); + + if (response_opt) { + auto response = response_opt.value(); + switch(response.result_case()) { + case ::tateyama::proto::request::response::ListRequest::ResultCase::kSuccess: + break; + case ::tateyama::proto::request::response::ListRequest::ResultCase::kError: + std::cerr << "ListRequest error: " << response.error().message() << std::endl; + rtnv = tgctl::return_code::err; + break; + default: + std::cerr << "ListRequest result_case() error: " << std::endl; + rtnv = tgctl::return_code::err; + } + + if (rtnv == tgctl::return_code::ok) { + auto tp = std::chrono::system_clock::now(); + auto secs = std::chrono::time_point_cast(tp); + auto ns = std::chrono::time_point_cast(tp) - std::chrono::time_point_cast(secs); + auto nms = secs.time_since_epoch().count() * 1000 + (ns.count() + 500000) / 1000000; + if (!FLAGS_quiet) { + // 10 10 10 12 17 + std::cout << "session-id request-id service-id payload-size elapsed-time (ms)" << std::endl; + std::cout << "---------- ---------- ---------- ------------ -----------------" << std::endl; + } + for (auto&& e : response.success().requests()) { + std::size_t elapsed = nms - e.started_time(); + if (!FLAGS_quiet) { + std::cout << std::setw(10) << e.session_id() + << std::setw(10 + 2) << e.request_id() + << std::setw(10 + 2) << e.service_id() + << std::setw(12 + 2) << e.payload_size() + << std::setw(17 + 2) << elapsed + << std::endl; + } + if (!FLAGS_monitor.empty()) { + monitor_output->request_list(e.session_id(), e.request_id(), e.service_id(), e.payload_size(), elapsed); + } + } + if (monitor_output) { + monitor_output->finish(true); + } + return rtnv; + } + } + } catch (std::runtime_error &ex) { + std::cerr << "could not connect to database with name '" << tateyama::bootstrap::wire::transport::database_name() << "'" << std::endl; + } + rtnv = tgctl::return_code::err; + + if (monitor_output) { + monitor_output->finish(false); + } + return rtnv; +} + +tgctl::return_code request_payload(std::size_t session_id, std::size_t request_id) { // NOLINT(readability-function-cognitive-complexity) + std::unique_ptr monitor_output{}; + + if (!FLAGS_monitor.empty()) { + monitor_output = std::make_unique(FLAGS_monitor); + monitor_output->start(); + } + + auto rtnv = tgctl::return_code::ok; + authentication::auth_options(); + try { + auto transport = std::make_unique(tateyama::framework::service_id_request); + ::tateyama::proto::request::request::Request request{}; + auto* get_payload = request.mutable_get_payload(); + get_payload->set_session_id(session_id); + get_payload->set_request_id(request_id); + auto response_opt = transport->send<::tateyama::proto::request::response::GetPayload>(request); + request.clear_get_payload(); + + if (response_opt) { + auto response = response_opt.value(); + switch(response.result_case()) { + case ::tateyama::proto::request::response::GetPayload::ResultCase::kSuccess: + break; + case ::tateyama::proto::request::response::GetPayload::ResultCase::kError: + std::cerr << "GetPayload error: " << response.error().message() << std::endl; + rtnv = tgctl::return_code::err; + break; + default: + std::cerr << "GetPayload result_case() error: " << std::endl; + rtnv = tgctl::return_code::err; + } + + if (rtnv == tgctl::return_code::ok) { + std::stringstream ssi; + std::stringstream sso; + + ssi << response.success().data(); + encode(ssi, sso); + if (!FLAGS_quiet) { + std::cout << sso.str(); + } + if (monitor_output) { + monitor_output->request_payload(sso.str()); + monitor_output->finish(true); + } + return rtnv; + } + } + } catch (std::runtime_error &ex) { + std::cerr << "could not connect to database with name '" << tateyama::bootstrap::wire::transport::database_name() << "'" << std::endl; + } + rtnv = tgctl::return_code::err; + + if (monitor_output) { + monitor_output->finish(false); + } + return rtnv; +} + +tgctl::return_code request_extract_sql(std::size_t session_id, std::string_view payload) { // NOLINT(readability-function-cognitive-complexity) + std::unique_ptr monitor_output{}; + + if (!FLAGS_monitor.empty()) { + monitor_output = std::make_unique(FLAGS_monitor); + monitor_output->start(); + } + + auto rtnv = tgctl::return_code::ok; + authentication::auth_options(); + try { + std::stringstream ssi; + std::stringstream sso; + ssi << payload; + decode(ssi, sso); + + auto transport = std::make_unique(tateyama::framework::service_id_sql); + ::jogasaki::proto::sql::request::Request request{}; + auto* extract_statement_info = request.mutable_extract_statement_info(); + extract_statement_info->set_session_id(session_id); + extract_statement_info->set_payload(sso.str()); + auto response_opt = transport->send<::jogasaki::proto::sql::response::ExtractStatementInfo>(request); + request.clear_extract_statement_info(); + + if (response_opt) { + auto response = response_opt.value(); + switch(response.result_case()) { + case ::jogasaki::proto::sql::response::ExtractStatementInfo::ResultCase::kSuccess: + break; + case ::jogasaki::proto::sql::response::ExtractStatementInfo::ResultCase::kError: + std::cerr << "ExtractStatementInfo error: " << response.error().detail() << std::endl; + rtnv = tgctl::return_code::err; + break; + default: + std::cerr << "ExtractStatementInfo result_case() error: " << std::endl; + rtnv = tgctl::return_code::err; + } + + if (rtnv == tgctl::return_code::ok) { + std::optional transaction_id{}; + std::optional sql{}; + + auto& success = response.success(); + + if (success.transaction_id_opt_case() == + jogasaki::proto::sql::response::ExtractStatementInfo_Success::TransactionIdOptCase::kTransactionId) { + transaction_id = success.transaction_id().id(); + } + if (success.sql_opt_case() == + jogasaki::proto::sql::response::ExtractStatementInfo_Success::SqlOptCase::kSql) { + sql = success.sql(); + if (!FLAGS_quiet) { + std::cout << sql.value() << std::endl; + } + } + if (monitor_output) { + monitor_output->request_extract_sql(transaction_id, sql); + monitor_output->finish(true); + } + return rtnv; + } + } + } catch (std::runtime_error &ex) { + std::cerr << "could not connect to database with name '" << tateyama::bootstrap::wire::transport::database_name() << "'" << std::endl; + } + rtnv = tgctl::return_code::err; + + if (monitor_output) { + monitor_output->finish(false); + } + return rtnv; +} + +} // tateyama::request diff --git a/src/tateyama/request/request.h b/src/tateyama/request/request.h new file mode 100644 index 0000000..09cafff --- /dev/null +++ b/src/tateyama/request/request.h @@ -0,0 +1,30 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "tateyama/tgctl/tgctl.h" + +namespace tateyama::request { + + tgctl::return_code request_list(); + tgctl::return_code request_payload(std::size_t session_id, std::size_t request_id); + tgctl::return_code request_extract_sql(std::size_t session_id, std::string_view payload); + +} diff --git a/src/tateyama/tgctl/tgctl.cpp b/src/tateyama/tgctl/tgctl.cpp index 5fa9582..cf78934 100644 --- a/src/tateyama/tgctl/tgctl.cpp +++ b/src/tateyama/tgctl/tgctl.cpp @@ -28,6 +28,7 @@ #ifdef ENABLE_ALTIMETER #include "tateyama/altimeter/altimeter.h" #endif +#include "tateyama/request/request.h" #include "help_text.h" @@ -269,6 +270,33 @@ int tgctl_main(const std::vector& args) { //NOLINT(readability-func return tateyama::tgctl::return_code::err; } #endif + + if (args.at(1) == "request") { + if (args.size() < 3) { + std::cerr << "need to specify request subcommand" << std::endl; + return tateyama::tgctl::return_code::err; + } + if (args.at(2) == "list") { + return tateyama::request::request_list(); + } + if (args.at(2) == "payload") { + if (args.size() < 5) { + std::cerr << "need to specify session-id and request-id" << std::endl; + return tateyama::tgctl::return_code::err; + } + return tateyama::request::request_payload(std::stol(args.at(3)), std::stol(args.at(4))); + } + if (args.at(2) == "extract-sql") { + if (args.size() < 5) { + std::cerr << "need to specify session-id and payload" << std::endl; + return tateyama::tgctl::return_code::err; + } + return tateyama::request::request_extract_sql(std::stol(args.at(3)), args.at(4)); + } + std::cerr << "unknown request-sub command '" << args.at(2) << "'" << std::endl; + return tateyama::tgctl::return_code::err; + } + std::cerr << "unknown command '" << args.at(1) << "'" << std::endl; return tateyama::tgctl::return_code::err; } diff --git a/src/tateyama/transport/transport.h b/src/tateyama/transport/transport.h index 8aea9a6..0ef9115 100644 --- a/src/tateyama/transport/transport.h +++ b/src/tateyama/transport/transport.h @@ -44,6 +44,10 @@ #include #include #endif +#include +#include +#include +#include #include "tateyama/configuration/bootstrap_configuration.h" #include "client_wire.h" #include "timer.h" @@ -68,6 +72,10 @@ constexpr static std::size_t METRICS_MESSAGE_VERSION_MINOR = 0; constexpr static std::size_t ALTIMETER_MESSAGE_VERSION_MAJOR = 0; constexpr static std::size_t ALTIMETER_MESSAGE_VERSION_MINOR = 0; #endif +constexpr static std::size_t REQUEST_MESSAGE_VERSION_MAJOR = 0; +constexpr static std::size_t REQUEST_MESSAGE_VERSION_MINOR = 0; +constexpr static std::size_t SQL_MESSAGE_VERSION_MAJOR = 1; +constexpr static std::size_t SQL_MESSAGE_VERSION_MINOR = 4; constexpr static std::int64_t EXPIRATION_SECONDS = 60; class transport { @@ -326,6 +334,72 @@ class transport { } #endif + // for request + template + std::optional send(::tateyama::proto::request::request::Request& request) { + std::stringstream sst{}; + if(auto res = tateyama::utils::SerializeDelimitedToOstream(header_, std::addressof(sst)); ! res) { + return std::nullopt; + } + request.set_service_message_version_major(REQUEST_MESSAGE_VERSION_MAJOR); + request.set_service_message_version_minor(REQUEST_MESSAGE_VERSION_MINOR); + if(auto res = tateyama::utils::SerializeDelimitedToOstream(request, std::addressof(sst)); ! res) { + return std::nullopt; + } + auto slot_index = wire_.search_slot(); + wire_.send(sst.str(), slot_index); + + std::string res_message{}; + wire_.receive(res_message, slot_index); + ::tateyama::proto::framework::response::Header header{}; + google::protobuf::io::ArrayInputStream ins{res_message.data(), static_cast(res_message.length())}; + if(auto res = tateyama::utils::ParseDelimitedFromZeroCopyStream(std::addressof(header), std::addressof(ins), nullptr); ! res) { + return std::nullopt; + } + std::string_view payload{}; + if (auto res = tateyama::utils::GetDelimitedBodyFromZeroCopyStream(std::addressof(ins), nullptr, payload); ! res) { + return std::nullopt; + } + T response{}; + if(auto res = response.ParseFromArray(payload.data(), payload.length()); ! res) { + return std::nullopt; + } + return response; + } + + // sql(ExtractStatementInfo) + template + std::optional send(::jogasaki::proto::sql::request::Request& request) { + std::stringstream sst{}; + if(auto res = tateyama::utils::SerializeDelimitedToOstream(header_, std::addressof(sst)); ! res) { + return std::nullopt; + } + request.set_service_message_version_major(SQL_MESSAGE_VERSION_MAJOR); + request.set_service_message_version_minor(SQL_MESSAGE_VERSION_MINOR); + if(auto res = tateyama::utils::SerializeDelimitedToOstream(request, std::addressof(sst)); ! res) { + return std::nullopt; + } + auto slot_index = wire_.search_slot(); + wire_.send(sst.str(), slot_index); + + std::string res_message{}; + wire_.receive(res_message, slot_index); + ::tateyama::proto::framework::response::Header header{}; + google::protobuf::io::ArrayInputStream ins{res_message.data(), static_cast(res_message.length())}; + if(auto res = tateyama::utils::ParseDelimitedFromZeroCopyStream(std::addressof(header), std::addressof(ins), nullptr); ! res) { + return std::nullopt; + } + std::string_view payload{}; + if (auto res = tateyama::utils::GetDelimitedBodyFromZeroCopyStream(std::addressof(ins), nullptr, payload); ! res) { + return std::nullopt; + } + T response{}; + if(auto res = response.ParseFromArray(payload.data(), payload.length()); ! res) { + return std::nullopt; + } + return response; + } + void close() { wire_.close(); closed_ = true; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c4b4841..f4171a9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,13 +15,13 @@ target_include_directories(${test_target} target_link_libraries(${test_target} PUBLIC gtest + PRIVATE lib_for_tests-impl PRIVATE glog::glog PRIVATE gflags::gflags PRIVATE rt PRIVATE Boost::thread PRIVATE Boost::filesystem PRIVATE ${tateyama_engine} - PRIVATE protobuf::libprotobuf ) function (add_test_executable source_file) @@ -29,6 +29,9 @@ function (add_test_executable source_file) target_sources(${test_target} PRIVATE ${source_file} ) + add_dependencies(${test_target} + build_protos + ) add_test( NAME ${test_name} COMMAND ${test_target} --gtest_filter=${test_name}.* --gtest_output=xml:${test_name}_gtest_result.xml @@ -43,6 +46,7 @@ file(GLOB SRCS "tateyama/version/*_test.cpp" "tateyama/session/*_test.cpp" "tateyama/metrics/*_test.cpp" + "tateyama/request/*_test.cpp" "tateyama/transport/*_test.cpp" ) if (ENABLE_ALTIMETER) diff --git a/test/tateyama/monitor/identify_sql_type_test.cpp b/test/tateyama/monitor/identify_sql_type_test.cpp new file mode 100644 index 0000000..bd4d360 --- /dev/null +++ b/test/tateyama/monitor/identify_sql_type_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2022-2022 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "test_root.h" + +#include "tateyama/monitor/constants_request.h" + +namespace tateyama::testing { + +class identify_sql_type_test : public ::testing::Test { +protected: + std::vector> test_patterns_ = { + { "SeLeCt 1", tateyama::monitor::type_of_sql::query }, + { " iNsErT INTO table (c1, c2) VALUES (11, 22)", tateyama::monitor::type_of_sql::statement }, + { "\tUpDaTe table SET c1=111, c2=222", tateyama::monitor::type_of_sql::statement }, + { "dElEtE FROM table WHERE c1=11", tateyama::monitor::type_of_sql::statement }, + { " BeGiN ", tateyama::monitor::type_of_sql::begin }, + { "cOmMiT ", tateyama::monitor::type_of_sql::commit }, + { "RoLlBaCk ", tateyama::monitor::type_of_sql::rollback }, + { "pRePaRe ", tateyama::monitor::type_of_sql::prepare }, + { "ExPlAiN ", tateyama::monitor::type_of_sql::explain }, + { "dUmP ", tateyama::monitor::type_of_sql::dump }, + { "LoAd ", tateyama::monitor::type_of_sql::load }, + }; +}; + +TEST_F(identify_sql_type_test, basic) { + for( auto&& e : test_patterns_ ) { + auto type = tateyama::monitor::identify_sql_type(e.first); + std::cout << e.first << " : " << tateyama::monitor::to_string_view(type) << std::endl; + EXPECT_EQ(e.second, type); + } +} + +} // namespace tateyama::testing diff --git a/test/tateyama/monitor/monitor_request_test.cpp b/test/tateyama/monitor/monitor_request_test.cpp new file mode 100644 index 0000000..3edcb78 --- /dev/null +++ b/test/tateyama/monitor/monitor_request_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2022-2022 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "test_root.h" + +#include "tateyama/monitor/monitor.h" + +namespace tateyama::testing { + +class monitor_request_test : public ::testing::Test { +public: + virtual void SetUp() { + monitor_ = std::make_unique(ss_); + } + + virtual void TearDown() { + } + +protected: + std::stringstream ss_{}; + std::unique_ptr monitor_{}; +}; + +TEST_F(monitor_request_test, list) { + monitor_->request_list(123, 456, 789, 135, 246); + + std::string result = ss_.str(); + std::cout << result << std::endl; + EXPECT_TRUE(validate_json(result)); + EXPECT_NE(std::string::npos, result.find(": 123,")); + EXPECT_NE(std::string::npos, result.find(": 456,")); + EXPECT_NE(std::string::npos, result.find(": 789,")); + EXPECT_NE(std::string::npos, result.find(": 135,")); + EXPECT_NE(std::string::npos, result.find(": 246 ")); +} + +TEST_F(monitor_request_test, payload) { + monitor_->request_payload("abcdef"); + + std::string result = ss_.str(); + std::cout << result << std::endl; + EXPECT_TRUE(validate_json(result)); + EXPECT_NE(std::string::npos, result.find(R"(: "abcdef" )")); +} + +TEST_F(monitor_request_test, sql_n_n) { + std::optional transaction_id{}; + std::optional sql{}; + + monitor_->request_extract_sql(transaction_id, sql); + std::string result = ss_.str(); + std::cout << result << std::endl; + EXPECT_TRUE(validate_json(result)); + EXPECT_EQ(std::string::npos, result.find(tateyama::monitor::TRANSACTION_ID)); + EXPECT_EQ(std::string::npos, result.find(tateyama::monitor::SQL)); +} + +TEST_F(monitor_request_test, sql_t_n) { + std::optional transaction_id{}; + std::optional sql{}; + + transaction_id = std::string("TID-xxxx"); + monitor_->request_extract_sql(transaction_id, sql); + std::string result = ss_.str(); + std::cout << result << std::endl; + EXPECT_TRUE(validate_json(result)); + EXPECT_NE(std::string::npos, result.find(tateyama::monitor::TRANSACTION_ID)); + EXPECT_NE(std::string::npos, result.find(R"( "TID-xxxx")")); + EXPECT_EQ(std::string::npos, result.find(tateyama::monitor::SQL)); +} + +TEST_F(monitor_request_test, sql_n_s) { + std::optional transaction_id{}; + std::optional sql{}; + + sql = std::string("select 1"); + monitor_->request_extract_sql(transaction_id, sql); + std::string result = ss_.str(); + std::cout << result << std::endl; + EXPECT_TRUE(validate_json(result)); + EXPECT_EQ(std::string::npos, result.find(tateyama::monitor::TRANSACTION_ID)); + EXPECT_NE(std::string::npos, result.find(tateyama::monitor::SQL)); + EXPECT_NE(std::string::npos, result.find(R"( "select 1")")); + EXPECT_NE(std::string::npos, result.find(R"( "query")")); +} + +TEST_F(monitor_request_test, sql_t_s) { + std::optional transaction_id{}; + std::optional sql{}; + + transaction_id = std::string("TID-xxxx"); + sql = std::string("select 1"); + monitor_->request_extract_sql(transaction_id, sql); + std::string result = ss_.str(); + std::cout << result << std::endl; + EXPECT_TRUE(validate_json(result)); + EXPECT_NE(std::string::npos, result.find(tateyama::monitor::TRANSACTION_ID)); + EXPECT_NE(std::string::npos, result.find(R"( "TID-xxxx")")); + EXPECT_NE(std::string::npos, result.find(tateyama::monitor::SQL)); + EXPECT_NE(std::string::npos, result.find(R"( "select 1")")); + EXPECT_NE(std::string::npos, result.find(R"( "query")")); +} + +} // namespace tateyama::testing diff --git a/test/tateyama/request/request_test.cpp b/test/tateyama/request/request_test.cpp new file mode 100644 index 0000000..41c3224 --- /dev/null +++ b/test/tateyama/request/request_test.cpp @@ -0,0 +1,220 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test_root.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include "tateyama/configuration/bootstrap_configuration.h" +#include "tateyama/test_utils/server_mock.h" + +namespace tateyama::test_utils { + +template<> +inline void server_mock::request_message(tateyama::proto::request::request::ListRequest& rq) { + tateyama::proto::request::request::Request r{}; + auto s = current_request(); + EXPECT_TRUE(r.ParseFromString(s)); + EXPECT_EQ(r.command_case(), tateyama::proto::request::request::Request::CommandCase::kListRequest); + rq = r.list_request(); +} + +template<> +inline void server_mock::request_message(tateyama::proto::request::request::GetPayload& rq) { + tateyama::proto::request::request::Request r{}; + auto s = current_request(); + EXPECT_TRUE(r.ParseFromString(s)); + EXPECT_EQ(r.command_case(), tateyama::proto::request::request::Request::CommandCase::kGetPayload); + rq = r.get_payload(); +} + +template<> +inline void server_mock::request_message(jogasaki::proto::sql::request::ExtractStatementInfo& rq) { + jogasaki::proto::sql::request::Request r{}; + auto s = current_request(); + EXPECT_TRUE(r.ParseFromString(s)); + EXPECT_EQ(r.request_case(), jogasaki::proto::sql::request::Request::RequestCase::kExtractStatementInfo); + rq = r.extract_statement_info(); +} + +} // tateyama::test_utils + + +namespace tateyama::request { +using namespace std::literals::string_literals; + +class request_test : public ::testing::Test { +public: + virtual void SetUp() { + helper_ = std::make_unique("request_test", 20401); + helper_->set_up(); + auto bst_conf = tateyama::configuration::bootstrap_configuration::create_bootstrap_configuration(helper_->conf_file_path()); + server_mock_ = std::make_unique("request_test", bst_conf.digest(), sync_); + sync_.wait(); + } + + virtual void TearDown() { + helper_->tear_down(); + } + +protected: + std::unique_ptr helper_{}; + std::unique_ptr server_mock_{}; + boost::barrier sync_{2}; + + std::string read_pipe(FILE* fp) { + std::stringstream ss{}; + int c{}; + while ((c = std::fgetc(fp)) != EOF) { + ss << static_cast(c); + } + return ss.str(); + } +}; + +TEST_F(request_test, request_list) { + std::array, 3> expected = { + { + {123, 1, 4, 13}, + {234, 2, 5, 24}, + {345, 3, 6, 36}, + }, + }; + + { + tateyama::proto::request::response::ListRequest response{}; + auto* success = response.mutable_success(); + + auto tp = std::chrono::system_clock::now(); + auto secs = std::chrono::time_point_cast(tp); + auto ns = std::chrono::time_point_cast(tp) - std::chrono::time_point_cast(secs); + + for (std::size_t i = 0; i < 3; i++) { + auto&& e = expected.at(i); + + tateyama::proto::request::response::RequestInfo info{}; + info.set_session_id(e.at(0)); + info.set_request_id(e.at(1)); + info.set_service_id(e.at(2)); + info.set_payload_size(e.at(3)); + info.set_started_time(secs.time_since_epoch().count() * 1000 + (ns.count() + 500000) / 1000000); + + *(success->add_requests()) = info; + } + server_mock_->push_response(response.SerializeAsString()); + } + + std::string command; + FILE *fp; + + command = "tgctl request list --conf "; + command += helper_->conf_file_path(); + std::cout << command << std::endl; + if((fp = popen(command.c_str(), "r")) == nullptr){ + std::cerr << "cannot tgctl request list" << std::endl; + } + auto result = read_pipe(fp); +// std::cout << result << std::flush; + EXPECT_NE(std::string::npos, result.find(" 123")); + EXPECT_NE(std::string::npos, result.find(" 1")); + EXPECT_NE(std::string::npos, result.find(" 4")); + EXPECT_NE(std::string::npos, result.find(" 13")); + + EXPECT_NE(std::string::npos, result.find(" 234")); + EXPECT_NE(std::string::npos, result.find(" 2")); + EXPECT_NE(std::string::npos, result.find(" 5")); + EXPECT_NE(std::string::npos, result.find(" 24")); + + EXPECT_NE(std::string::npos, result.find(" 345")); + EXPECT_NE(std::string::npos, result.find(" 3")); + EXPECT_NE(std::string::npos, result.find(" 6")); + EXPECT_NE(std::string::npos, result.find(" 36")); + + EXPECT_EQ(tateyama::framework::service_id_request, server_mock_->component_id()); + tateyama::proto::request::request::ListRequest rq{}; + server_mock_->request_message(rq); +} + +TEST_F(request_test, request_payload) { + { + tateyama::proto::request::response::GetPayload response{}; + auto* success = response.mutable_success(); + success->set_data("abcdefg"); + + server_mock_->push_response(response.SerializeAsString()); + } + + std::string command; + FILE *fp; + + command = "tgctl request payload 123456 987654 --conf "; + command += helper_->conf_file_path(); + std::cout << command << std::endl; + if((fp = popen(command.c_str(), "r")) == nullptr){ + std::cerr << "cannot tgctl request payload" << std::endl; + } + auto result = read_pipe(fp); +// std::cout << result << std::flush; + EXPECT_NE(std::string::npos, result.find("YWJjZGVmZw")); + + EXPECT_EQ(tateyama::framework::service_id_request, server_mock_->component_id()); + tateyama::proto::request::request::GetPayload rq{}; + server_mock_->request_message(rq); + EXPECT_EQ(rq.session_id(), 123456); + EXPECT_EQ(rq.request_id(), 987654); +} + +TEST_F(request_test, request_extract_sql) { + { + jogasaki::proto::sql::response::ExtractStatementInfo response{}; + auto* success = response.mutable_success(); + success->mutable_transaction_id()->set_id("transaction_id_for_test"); + success->set_sql("select 1"); + + server_mock_->push_response(response.SerializeAsString()); + } + + std::string command; + FILE *fp; + + command = "tgctl request extract-sql 123456 YWJjZGVmZw --conf "; + command += helper_->conf_file_path(); + std::cout << command << std::endl; + if((fp = popen(command.c_str(), "r")) == nullptr){ + std::cerr << "cannot tgctl request extract-sql" << std::endl; + } + auto result = read_pipe(fp); +// std::cout << result << std::flush; + EXPECT_NE(std::string::npos, result.find("select 1")); + + EXPECT_EQ(tateyama::framework::service_id_sql, server_mock_->component_id()); + jogasaki::proto::sql::request::ExtractStatementInfo rq{}; + server_mock_->request_message(rq); + EXPECT_EQ(rq.session_id(), 123456); + EXPECT_EQ(rq.payload(), "abcdefg"); +} + +} // namespace tateyama::request From 70ab37c9dd2c313f918d72b5044e2656474147b0 Mon Sep 17 00:00:00 2001 From: Ryoji Kurosawa Date: Tue, 7 Jan 2025 18:41:07 +0900 Subject: [PATCH 2/5] minor updates to sql/response.proto --- src/jogasaki/proto/sql/response.proto | 28 ++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/jogasaki/proto/sql/response.proto b/src/jogasaki/proto/sql/response.proto index 821cb40..2d0544e 100644 --- a/src/jogasaki/proto/sql/response.proto +++ b/src/jogasaki/proto/sql/response.proto @@ -37,7 +37,7 @@ message Error { * Each response message */ -/* For response to ExecuteQuery, ExecutePreparedQuery, Commit, Rollback, +/* For response to ExecuteQuery, ExecutePreparedQuery, Commit, Rollback, DisposePreparedStatement, DisposeTransaction and ExecuteDump. */ message ResultOnly { oneof result { @@ -284,6 +284,7 @@ message Response { GetErrorInfo get_error_info = 10; DisposeTransaction dispose_transaction = 11; ExecuteResult execute_result = 12; + ExtractStatementInfo extract_statement_info = 13; } } @@ -297,24 +298,13 @@ message ResultSetMetadata { // Response of ExtractStatementInfo. message ExtractStatementInfo { - reserved 1 to 10; - - // the response body. - oneof result { - // request is successfully completed. - Success success = 11; - - // error was occurred. - Error error = 12; - } - // request is successfully completed. message Success { // the transaction ID for the statement. oneof transaction_id_opt { // the corresponding transaction ID. - jogasaki.proto.sql.common.TransactionId transaction_id = 1; + common.TransactionId transaction_id = 1; } // the executing SQL text of the statement. @@ -323,4 +313,16 @@ message ExtractStatementInfo { string sql = 2; } } + + reserved 1 to 10; + + // the response body. + oneof result { + // request is successfully completed. + Success success = 11; + + // error was occurred. + Error error = 12; + } + } From af7fb078c1d1d50bf9db5bb70747e62234c6c7ee Mon Sep 17 00:00:00 2001 From: t-horikawa Date: Tue, 7 Jan 2025 18:43:48 +0900 Subject: [PATCH 3/5] revise help text --- src/tateyama/tgctl/help_text.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/tateyama/tgctl/help_text.h b/src/tateyama/tgctl/help_text.h index 79336ad..df77994 100644 --- a/src/tateyama/tgctl/help_text.h +++ b/src/tateyama/tgctl/help_text.h @@ -123,6 +123,38 @@ static constexpr std::string_view help_text { // NOLINT " none\n" " \n" " --format (metrics information display format) type: string default: \"json\"\n" +#ifdef ENABLE_ALTIMETER +"\n" +" altimeter {enable|disable} : {enable|disable} altimeter\n" +" \n" +" none\n" +"\n" +" altimeter set {event_level|audit_level} : set {event_level|audit_level}\n" +" \n" +" level : log level\n" +"\n" +" altimeter set statement_duration : set statement_duration\n" +" \n" +" duration : statement duration to be set\n" +"\n" +" altimeter rotate : invoke altimeter log rotation\n" +" \n" +" none\n" +#endif +"\n" +" request list : show ongoing request\n" +" \n" +" none\n" +"\n" +" request payload : display the request message in base64 encoding\n" +" \n" +" session-id : session id to specify the request\n" +" request-id : request id to specify the request\n" +"\n" +" request extract-sql : extract the sql command corresponding to the request message\n" +" \n" +" session-id : id of the session to which the request belongs\n" +" payload : the request message in base64 encoding\n" }; } // tateyama::tgctl From 25fcd9083c25af310da1481baae83c9a542cb3ba Mon Sep 17 00:00:00 2001 From: t-horikawa Date: Wed, 8 Jan 2025 09:25:34 +0900 Subject: [PATCH 4/5] match request/request.cpp with minor updates to sql/response.proto --- src/tateyama/request/request.cpp | 65 ++++++++++++++------------ test/tateyama/request/request_test.cpp | 5 +- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/tateyama/request/request.cpp b/src/tateyama/request/request.cpp index 57e087f..bbf486a 100644 --- a/src/tateyama/request/request.cpp +++ b/src/tateyama/request/request.cpp @@ -194,45 +194,50 @@ tgctl::return_code request_extract_sql(std::size_t session_id, std::string_view auto* extract_statement_info = request.mutable_extract_statement_info(); extract_statement_info->set_session_id(session_id); extract_statement_info->set_payload(sso.str()); - auto response_opt = transport->send<::jogasaki::proto::sql::response::ExtractStatementInfo>(request); + auto response_opt = transport->send<::jogasaki::proto::sql::response::Response>(request); request.clear_extract_statement_info(); if (response_opt) { auto response = response_opt.value(); - switch(response.result_case()) { - case ::jogasaki::proto::sql::response::ExtractStatementInfo::ResultCase::kSuccess: - break; - case ::jogasaki::proto::sql::response::ExtractStatementInfo::ResultCase::kError: - std::cerr << "ExtractStatementInfo error: " << response.error().detail() << std::endl; - rtnv = tgctl::return_code::err; - break; - default: - std::cerr << "ExtractStatementInfo result_case() error: " << std::endl; - rtnv = tgctl::return_code::err; - } + if (response.response_case() == ::jogasaki::proto::sql::response::Response::ResponseCase::kExtractStatementInfo) { + auto extract_statement_info = response.extract_statement_info(); + switch(extract_statement_info.result_case()) { + case ::jogasaki::proto::sql::response::ExtractStatementInfo::ResultCase::kSuccess: + break; + case ::jogasaki::proto::sql::response::ExtractStatementInfo::ResultCase::kError: + std::cerr << "ExtractStatementInfo error: " << extract_statement_info.error().detail() << std::endl; + rtnv = tgctl::return_code::err; + break; + default: + std::cerr << "ExtractStatementInfo result_case() error: " << std::endl; + rtnv = tgctl::return_code::err; + } - if (rtnv == tgctl::return_code::ok) { - std::optional transaction_id{}; - std::optional sql{}; + if (rtnv == tgctl::return_code::ok) { + std::optional transaction_id{}; + std::optional sql{}; - auto& success = response.success(); + auto& success = extract_statement_info.success(); - if (success.transaction_id_opt_case() == - jogasaki::proto::sql::response::ExtractStatementInfo_Success::TransactionIdOptCase::kTransactionId) { - transaction_id = success.transaction_id().id(); - } - if (success.sql_opt_case() == - jogasaki::proto::sql::response::ExtractStatementInfo_Success::SqlOptCase::kSql) { - sql = success.sql(); - if (!FLAGS_quiet) { - std::cout << sql.value() << std::endl; + if (success.transaction_id_opt_case() == + jogasaki::proto::sql::response::ExtractStatementInfo_Success::TransactionIdOptCase::kTransactionId) { + transaction_id = success.transaction_id().id(); } + if (success.sql_opt_case() == + jogasaki::proto::sql::response::ExtractStatementInfo_Success::SqlOptCase::kSql) { + sql = success.sql(); + if (!FLAGS_quiet) { + std::cout << sql.value() << std::endl; + } + } + if (monitor_output) { + monitor_output->request_extract_sql(transaction_id, sql); + monitor_output->finish(true); + } + return rtnv; } - if (monitor_output) { - monitor_output->request_extract_sql(transaction_id, sql); - monitor_output->finish(true); - } - return rtnv; + } else { + std::cerr << "the response type does not match with that expected" << std::endl; } } } catch (std::runtime_error &ex) { diff --git a/test/tateyama/request/request_test.cpp b/test/tateyama/request/request_test.cpp index 41c3224..0176815 100644 --- a/test/tateyama/request/request_test.cpp +++ b/test/tateyama/request/request_test.cpp @@ -189,8 +189,9 @@ TEST_F(request_test, request_payload) { TEST_F(request_test, request_extract_sql) { { - jogasaki::proto::sql::response::ExtractStatementInfo response{}; - auto* success = response.mutable_success(); + jogasaki::proto::sql::response::Response response{}; + auto* extract_statement_info = response.mutable_extract_statement_info(); + auto* success = extract_statement_info->mutable_success(); success->mutable_transaction_id()->set_id("transaction_id_for_test"); success->set_sql("select 1"); From d5a98675161327534b6371b4e4b6ed628c38ce4e Mon Sep 17 00:00:00 2001 From: t-horikawa Date: Wed, 8 Jan 2025 09:36:23 +0900 Subject: [PATCH 5/5] fix clang-tidy warning --- src/tateyama/request/request.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tateyama/request/request.cpp b/src/tateyama/request/request.cpp index bbf486a..e50ba6c 100644 --- a/src/tateyama/request/request.cpp +++ b/src/tateyama/request/request.cpp @@ -200,7 +200,7 @@ tgctl::return_code request_extract_sql(std::size_t session_id, std::string_view if (response_opt) { auto response = response_opt.value(); if (response.response_case() == ::jogasaki::proto::sql::response::Response::ResponseCase::kExtractStatementInfo) { - auto extract_statement_info = response.extract_statement_info(); + const auto& extract_statement_info = response.extract_statement_info(); switch(extract_statement_info.result_case()) { case ::jogasaki::proto::sql::response::ExtractStatementInfo::ResultCase::kSuccess: break;