Changed for new deploy algorithm

This commit is contained in:
WolverinDEV 2020-04-14 11:49:05 +02:00
parent ee7f26b7ed
commit 707736d896
14 changed files with 483 additions and 114 deletions

View File

@ -270,14 +270,16 @@ if(BUILD_TESTS)
target_link_libraries(RingTest ${TEST_LIBRARIES}) target_link_libraries(RingTest ${TEST_LIBRARIES})
if(NOT WIN32) if(NOT WIN32)
add_executable(CommandTest ${SOURCE_FILES} ${HEADER_FILES} test/CommandTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) add_executable(CommandTest test/CommandTest.cpp src/query/command3.cpp src/query/Command.cpp src/query/escape.cpp src/converters/converter.cpp src/Variable.cpp)
target_link_libraries(CommandTest ${TEST_LIBRARIES}) target_link_libraries(CommandTest DataPipes::core::shared jsoncpp_lib ${glib20_DIR}/lib/x86_64-linux-gnu/libffi.so.7 ${nice_DIR}/lib/libnice.so.10)
add_executable(WebsocketTest ${SOURCE_FILES} ${HEADER_FILES} test/WSSTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) add_executable(WebsocketTest ${SOURCE_FILES} ${HEADER_FILES} test/WSSTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h)
target_link_libraries(WebsocketTest ${TEST_LIBRARIES}) target_link_libraries(WebsocketTest ${TEST_LIBRARIES})
add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) #add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h)
target_link_libraries(SQLTest ${TEST_LIBRARIES}) #target_link_libraries(SQLTest ${TEST_LIBRARIES})
add_executable(SQL2Test test/SQL2Test.cpp src/Variable.cpp)
target_link_libraries(SQL2Test sqlite3)
add_executable(ChannelTest ${SOURCE_FILES} ${HEADER_FILES} test/ChannelTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) add_executable(ChannelTest ${SOURCE_FILES} ${HEADER_FILES} test/ChannelTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h)
target_link_libraries(ChannelTest ${TEST_LIBRARIES}) target_link_libraries(ChannelTest ${TEST_LIBRARIES})

View File

@ -935,6 +935,7 @@ inline void init_mapping() {
if(teamspeak::unmapping.empty()) teamspeak::unmapping = build_unmapping(); if(teamspeak::unmapping.empty()) teamspeak::unmapping = build_unmapping();
} }
/*
template <typename T> template <typename T>
inline deque<T> operator+(const deque<T>& a, const deque<T>& b) { inline deque<T> operator+(const deque<T>& a, const deque<T>& b) {
deque<T> result; deque<T> result;
@ -942,6 +943,7 @@ inline deque<T> operator+(const deque<T>& a, const deque<T>& b) {
result.insert(result.end(), b.begin(), b.end()); result.insert(result.end(), b.begin(), b.end());
return result; return result;
} }
*/
inline deque<string> mmget(teamspeak::MapType& map, teamspeak::GroupType type, const std::string& key) { inline deque<string> mmget(teamspeak::MapType& map, teamspeak::GroupType type, const std::string& key) {
return map[type].count(key) > 0 ? map[type].find(key)->second : deque<string>{}; return map[type].count(key) > 0 ? map[type].find(key)->second : deque<string>{};
@ -953,16 +955,29 @@ inline std::deque<std::string> map_entry(std::string key, teamspeak::GroupType t
if(key.find("_needed_modify_power_") == 1) { if(key.find("_needed_modify_power_") == 1) {
key = key.substr(strlen("x_needed_modify_power_")); key = key.substr(strlen("x_needed_modify_power_"));
deque<string> result;
if(map_table[type].count("i_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("i_" + key) > 0) result = mmget(map_table, type, "i_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "i_" + key); std::deque<std::string> result{};
else if(map_table[type].count("b_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("b_" + key) > 0) result = mmget(map_table, type, "b_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "b_" + key); auto mapped_type = mmget(map_table, type, "i_" + key);
else result = {"x_" + key}; result.insert(result.end(), mapped_type.begin(), mapped_type.end());
if(type != teamspeak::GroupType::GENERAL) {
auto mapped_general = mmget(map_table, teamspeak::GroupType::GENERAL, "i_" + key);
result.insert(result.end(), mapped_general.begin(), mapped_general.end());
}
for(auto& entry : result) for(auto& entry : result)
entry = "i_needed_modify_power_" + entry.substr(2); entry = "i_needed_modify_power_" + entry.substr(2);
return result; return result;
} }
if(map_table[type].count(key) > 0 || map_table[teamspeak::GroupType::GENERAL].count(key) > 0) return mmget(map_table, type, key) + mmget(map_table, teamspeak::GroupType::GENERAL, key); if(map_table[type].count(key) > 0 || map_table[teamspeak::GroupType::GENERAL].count(key) > 0) {
std::deque<std::string> result{};
auto mapped_type = mmget(map_table, type, key);
result.insert(result.end(), mapped_type.begin(), mapped_type.end());
if(type != teamspeak::GroupType::GENERAL) {
auto mapped_general = mmget(map_table, teamspeak::GroupType::GENERAL, key);
result.insert(result.end(), mapped_general.begin(), mapped_general.end());
}
return result;
}
return {key}; return {key};
} }

View File

@ -813,11 +813,11 @@ namespace ts {
}; };
struct PermissionFlaggedValue { struct PermissionFlaggedValue {
PermissionValue value = permNotGranted; PermissionValue value{permNotGranted};
bool has_value = false; bool has_value{false};
constexpr bool has_power() const { return this->has_value && (this->value > 0 || this->value == -1); } [[nodiscard]] constexpr bool has_power() const { return this->has_value && (this->value > 0 || this->value == -1); }
constexpr bool has_infinite_power() const { return this->has_value && this->value == -1; } [[nodiscard]] constexpr bool has_infinite_power() const { return this->has_value && this->value == -1; }
inline bool operator==(const PermissionFlaggedValue& other) const { return other.value == this->value && other.has_value == this->has_value; } inline bool operator==(const PermissionFlaggedValue& other) const { return other.value == this->value && other.has_value == this->has_value; }
inline bool operator!=(const PermissionFlaggedValue& other) const { return !(*this == other); } inline bool operator!=(const PermissionFlaggedValue& other) const { return !(*this == other); }

View File

@ -149,11 +149,11 @@ namespace ts {
VIRTUALSERVER_HOSTMESSAGE, //available when connected, not updated while connected VIRTUALSERVER_HOSTMESSAGE, //available when connected, not updated while connected
VIRTUALSERVER_HOSTMESSAGE_MODE, //available when connected, not updated while connected VIRTUALSERVER_HOSTMESSAGE_MODE, //available when connected, not updated while connected
VIRTUALSERVER_FILEBASE, //not available to clients, stores the folder used for file transfers VIRTUALSERVER_FILEBASE, //not available to clients, stores the folder used for file transfers
VIRTUALSERVER_DEFAULT_SERVER_GROUP, //the manager permissions server group that a new manager gets assigned VIRTUALSERVER_DEFAULT_SERVER_GROUP, //the client permissions server group that a new client gets assigned
VIRTUALSERVER_DEFAULT_MUSIC_GROUP, //the manager permissions server group that a new manager gets assigned VIRTUALSERVER_DEFAULT_MUSIC_GROUP, //the client permissions server group that a new client gets assigned
VIRTUALSERVER_DEFAULT_CHANNEL_GROUP, //the channel permissions group that a new manager gets assigned when joining a channel VIRTUALSERVER_DEFAULT_CHANNEL_GROUP, //the channel permissions group that a new client gets assigned when joining a channel
VIRTUALSERVER_FLAG_PASSWORD, //only available on request (=> requestServerVariables) VIRTUALSERVER_FLAG_PASSWORD, //only available on request (=> requestServerVariables)
VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP, //the channel permissions group that a manager gets assigned when creating a channel VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP, //the channel permissions group that a client gets assigned when creating a channel
VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH, //only available on request (=> requestServerVariables) VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH, //only available on request (=> requestServerVariables)
VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH, //only available on request (=> requestServerVariables) VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH, //only available on request (=> requestServerVariables)
VIRTUALSERVER_HOSTBANNER_URL, //available when connected, always up-to-date VIRTUALSERVER_HOSTBANNER_URL, //available when connected, always up-to-date
@ -238,7 +238,7 @@ namespace ts {
CHANNEL_NAME, //Available for all channels that are "in view", always up-to-date CHANNEL_NAME, //Available for all channels that are "in view", always up-to-date
CHANNEL_TOPIC, //Available for all channels that are "in view", always up-to-date CHANNEL_TOPIC, //Available for all channels that are "in view", always up-to-date
CHANNEL_DESCRIPTION, //Must be requested (=> requestChannelDescription) CHANNEL_DESCRIPTION, //Must be requested (=> requestChannelDescription)
CHANNEL_PASSWORD, //not available manager side CHANNEL_PASSWORD, //not available client side
CHANNEL_CODEC, //Available for all channels that are "in view", always up-to-date CHANNEL_CODEC, //Available for all channels that are "in view", always up-to-date
CHANNEL_CODEC_QUALITY, //Available for all channels that are "in view", always up-to-date CHANNEL_CODEC_QUALITY, //Available for all channels that are "in view", always up-to-date
CHANNEL_MAXCLIENTS, //Available for all channels that are "in view", always up-to-date CHANNEL_MAXCLIENTS, //Available for all channels that are "in view", always up-to-date
@ -250,13 +250,13 @@ namespace ts {
CHANNEL_FLAG_PASSWORD, //Available for all channels that are "in view", always up-to-date CHANNEL_FLAG_PASSWORD, //Available for all channels that are "in view", always up-to-date
CHANNEL_CODEC_LATENCY_FACTOR, //Available for all channels that are "in view", always up-to-date CHANNEL_CODEC_LATENCY_FACTOR, //Available for all channels that are "in view", always up-to-date
CHANNEL_CODEC_IS_UNENCRYPTED, //Available for all channels that are "in view", always up-to-date CHANNEL_CODEC_IS_UNENCRYPTED, //Available for all channels that are "in view", always up-to-date
CHANNEL_SECURITY_SALT, //Not available manager side, not used in teamspeak, only SDK. Sets the options+salt for security hash. CHANNEL_SECURITY_SALT, //Not available client side, not used in teamspeak, only SDK. Sets the options+salt for security hash.
CHANNEL_DELETE_DELAY, //How many seconds to wait before deleting this channel CHANNEL_DELETE_DELAY, //How many seconds to wait before deleting this channel
CHANNEL_FLAG_MAXCLIENTS_UNLIMITED, //Available for all channels that are "in view", always up-to-date CHANNEL_FLAG_MAXCLIENTS_UNLIMITED, //Available for all channels that are "in view", always up-to-date
CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED,//Available for all channels that are "in view", always up-to-date CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED,//Available for all channels that are "in view", always up-to-date
CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED,//Available for all channels that are "in view", always up-to-date CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED,//Available for all channels that are "in view", always up-to-date
CHANNEL_FLAG_ARE_SUBSCRIBED, //Only available manager side, stores whether we are subscribed to this channel CHANNEL_FLAG_ARE_SUBSCRIBED, //Only available client side, stores whether we are subscribed to this channel
CHANNEL_FILEPATH, //not available manager side, the folder used for file-transfers for this channel CHANNEL_FILEPATH, //not available client side, the folder used for file-transfers for this channel
CHANNEL_NEEDED_TALK_POWER, //Available for all channels that are "in view", always up-to-date CHANNEL_NEEDED_TALK_POWER, //Available for all channels that are "in view", always up-to-date
CHANNEL_FORCED_SILENCE, //Available for all channels that are "in view", always up-to-date CHANNEL_FORCED_SILENCE, //Available for all channels that are "in view", always up-to-date
CHANNEL_NAME_PHONETIC, //Available for all channels that are "in view", always up-to-date CHANNEL_NAME_PHONETIC, //Available for all channels that are "in view", always up-to-date
@ -289,62 +289,62 @@ namespace ts {
enum ClientProperties { enum ClientProperties {
CLIENT_UNDEFINED, CLIENT_UNDEFINED,
CLIENT_BEGINMARKER, CLIENT_BEGINMARKER,
CLIENT_UNIQUE_IDENTIFIER = CLIENT_BEGINMARKER, //automatically up-to-date for any manager "in view", can be used to identify this particular manager installation CLIENT_UNIQUE_IDENTIFIER = CLIENT_BEGINMARKER, //automatically up-to-date for any client "in view", can be used to identify this particular client installation
CLIENT_NICKNAME, //automatically up-to-date for any manager "in view" CLIENT_NICKNAME, //automatically up-to-date for any client "in view"
CLIENT_VERSION, //for other clients than ourself, this needs to be requested (=> requestClientVariables) CLIENT_VERSION, //for other clients than ourself, this needs to be requested (=> requestClientVariables)
CLIENT_PLATFORM, //for other clients than ourself, this needs to be requested (=> requestClientVariables) CLIENT_PLATFORM, //for other clients than ourself, this needs to be requested (=> requestClientVariables)
CLIENT_FLAG_TALKING, //automatically up-to-date for any manager that can be heard (in room / whisper) CLIENT_FLAG_TALKING, //automatically up-to-date for any client that can be heard (in room / whisper)
CLIENT_INPUT_MUTED, //automatically up-to-date for any manager "in view", this clients microphone mute status CLIENT_INPUT_MUTED, //automatically up-to-date for any client "in view", this clients microphone mute status
CLIENT_OUTPUT_MUTED, //automatically up-to-date for any manager "in view", this clients headphones/speakers/mic combined mute status CLIENT_OUTPUT_MUTED, //automatically up-to-date for any client "in view", this clients headphones/speakers/mic combined mute status
CLIENT_OUTPUTONLY_MUTED, //automatically up-to-date for any manager "in view", this clients headphones/speakers only mute status CLIENT_OUTPUTONLY_MUTED, //automatically up-to-date for any client "in view", this clients headphones/speakers only mute status
CLIENT_INPUT_HARDWARE, //automatically up-to-date for any manager "in view", this clients microphone hardware status (is the capture device opened?) CLIENT_INPUT_HARDWARE, //automatically up-to-date for any client "in view", this clients microphone hardware status (is the capture device opened?)
CLIENT_OUTPUT_HARDWARE, //automatically up-to-date for any manager "in view", this clients headphone/speakers hardware status (is the playback device opened?) CLIENT_OUTPUT_HARDWARE, //automatically up-to-date for any client "in view", this clients headphone/speakers hardware status (is the playback device opened?)
CLIENT_DEFAULT_CHANNEL, //only usable for ourself, the default channel we used to connect on our last connection attempt CLIENT_DEFAULT_CHANNEL, //only usable for ourself, the default channel we used to connect on our last connection attempt
CLIENT_DEFAULT_CHANNEL_PASSWORD, //internal use CLIENT_DEFAULT_CHANNEL_PASSWORD, //internal use
CLIENT_SERVER_PASSWORD, //internal use CLIENT_SERVER_PASSWORD, //internal use
CLIENT_META_DATA, //automatically up-to-date for any manager "in view", not used by TeamSpeak, free storage for sdk users CLIENT_META_DATA, //automatically up-to-date for any client "in view", not used by TeamSpeak, free storage for sdk users
CLIENT_IS_RECORDING, //automatically up-to-date for any manager "in view" CLIENT_IS_RECORDING, //automatically up-to-date for any client "in view"
CLIENT_VERSION_SIGN, //sign CLIENT_VERSION_SIGN, //sign
CLIENT_SECURITY_HASH, //SDK use, not used by teamspeak. Hash is provided by an outside source. A channel will use the security salt + other manager data to calculate a hash, which must be the same as the one provided here. CLIENT_SECURITY_HASH, //SDK use, not used by teamspeak. Hash is provided by an outside source. A channel will use the security salt + other client data to calculate a hash, which must be the same as the one provided here.
//Rare properties //Rare properties
CLIENT_KEY_OFFSET, //internal use CLIENT_KEY_OFFSET, //internal use
CLIENT_LOGIN_NAME, //used for serverquery clients, makes no sense on normal clients currently CLIENT_LOGIN_NAME, //used for serverquery clients, makes no sense on normal clients currently
CLIENT_LOGIN_PASSWORD, //used for serverquery clients, makes no sense on normal clients currently CLIENT_LOGIN_PASSWORD, //used for serverquery clients, makes no sense on normal clients currently
CLIENT_DATABASE_ID, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id CLIENT_DATABASE_ID, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds database client id
CLIENT_ID, //clid! CLIENT_ID, //clid!
CLIENT_HARDWARE_ID, //hwid! CLIENT_HARDWARE_ID, //hwid!
CLIENT_CHANNEL_GROUP_ID, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id CLIENT_CHANNEL_GROUP_ID, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds database client id
CLIENT_SERVERGROUPS, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds all servergroups manager belongs too CLIENT_SERVERGROUPS, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds all servergroups client belongs too
CLIENT_CREATED, //this needs to be requested (=> requestClientVariables), first time this manager connected to this server CLIENT_CREATED, //this needs to be requested (=> requestClientVariables), first time this client connected to this server
CLIENT_LASTCONNECTED, //this needs to be requested (=> requestClientVariables), last time this manager connected to this server CLIENT_LASTCONNECTED, //this needs to be requested (=> requestClientVariables), last time this client connected to this server
CLIENT_TOTALCONNECTIONS, //this needs to be requested (=> requestClientVariables), how many times this manager connected to this server CLIENT_TOTALCONNECTIONS, //this needs to be requested (=> requestClientVariables), how many times this client connected to this server
CLIENT_AWAY, //automatically up-to-date for any manager "in view", this clients away status CLIENT_AWAY, //automatically up-to-date for any client "in view", this clients away status
CLIENT_AWAY_MESSAGE, //automatically up-to-date for any manager "in view", this clients away message CLIENT_AWAY_MESSAGE, //automatically up-to-date for any client "in view", this clients away message
CLIENT_TYPE, //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection CLIENT_TYPE, //automatically up-to-date for any client "in view", determines if this is a real client or a server-query connection
CLIENT_TYPE_EXACT, //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection CLIENT_TYPE_EXACT, //automatically up-to-date for any client "in view", determines if this is a real client or a server-query connection
CLIENT_FLAG_AVATAR, //automatically up-to-date for any manager "in view", this manager got an avatar CLIENT_FLAG_AVATAR, //automatically up-to-date for any client "in view", this client got an avatar
CLIENT_TALK_POWER, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id CLIENT_TALK_POWER, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds database client id
CLIENT_TALK_REQUEST, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds timestamp where manager requested to talk CLIENT_TALK_REQUEST, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds timestamp where client requested to talk
CLIENT_TALK_REQUEST_MSG, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds matter for the request CLIENT_TALK_REQUEST_MSG, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds matter for the request
CLIENT_DESCRIPTION, //automatically up-to-date for any manager "in view" CLIENT_DESCRIPTION, //automatically up-to-date for any client "in view"
CLIENT_IS_TALKER, //automatically up-to-date for any manager "in view" CLIENT_IS_TALKER, //automatically up-to-date for any client "in view"
CLIENT_MONTH_BYTES_UPLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_MONTH_BYTES_UPLOADED, //this needs to be requested (=> requestClientVariables)
CLIENT_MONTH_BYTES_DOWNLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_MONTH_BYTES_DOWNLOADED, //this needs to be requested (=> requestClientVariables)
CLIENT_TOTAL_BYTES_UPLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_TOTAL_BYTES_UPLOADED, //this needs to be requested (=> requestClientVariables)
CLIENT_TOTAL_BYTES_DOWNLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_TOTAL_BYTES_DOWNLOADED, //this needs to be requested (=> requestClientVariables)
CLIENT_TOTAL_ONLINE_TIME, CLIENT_TOTAL_ONLINE_TIME,
CLIENT_MONTH_ONLINE_TIME, CLIENT_MONTH_ONLINE_TIME,
CLIENT_IS_PRIORITY_SPEAKER, //automatically up-to-date for any manager "in view" CLIENT_IS_PRIORITY_SPEAKER, //automatically up-to-date for any client "in view"
CLIENT_UNREAD_MESSAGES, //automatically up-to-date for any manager "in view" CLIENT_UNREAD_MESSAGES, //automatically up-to-date for any client "in view"
CLIENT_NICKNAME_PHONETIC, //automatically up-to-date for any manager "in view" CLIENT_NICKNAME_PHONETIC, //automatically up-to-date for any client "in view"
CLIENT_NEEDED_SERVERQUERY_VIEW_POWER, //automatically up-to-date for any manager "in view" CLIENT_NEEDED_SERVERQUERY_VIEW_POWER, //automatically up-to-date for any client "in view"
CLIENT_DEFAULT_TOKEN, //only usable for ourself, the default token we used to connect on our last connection attempt CLIENT_DEFAULT_TOKEN, //only usable for ourself, the default token we used to connect on our last connection attempt
CLIENT_ICON_ID, //automatically up-to-date for any manager "in view" CLIENT_ICON_ID, //automatically up-to-date for any client "in view"
CLIENT_IS_CHANNEL_COMMANDER, //automatically up-to-date for any manager "in view" CLIENT_IS_CHANNEL_COMMANDER, //automatically up-to-date for any client "in view"
CLIENT_COUNTRY, //automatically up-to-date for any manager "in view" CLIENT_COUNTRY, //automatically up-to-date for any client "in view"
CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, contains channel_id where the channel_group_id is set from CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, contains channel_id where the channel_group_id is set from
CLIENT_BADGES, //automatically up-to-date for any manager "in view", stores icons for partner badges CLIENT_BADGES, //automatically up-to-date for any client "in view", stores icons for partner badges
CLIENT_MYTEAMSPEAK_ID, CLIENT_MYTEAMSPEAK_ID,
CLIENT_INTEGRATIONS, CLIENT_INTEGRATIONS,
@ -374,11 +374,11 @@ namespace ts {
CONNECTION_PING = CONNECTION_BEGINMARKER, //average latency for a round trip through and back this connection CONNECTION_PING = CONNECTION_BEGINMARKER, //average latency for a round trip through and back this connection
CONNECTION_PING_DEVIATION, //standard deviation of the above average latency CONNECTION_PING_DEVIATION, //standard deviation of the above average latency
CONNECTION_CONNECTED_TIME, //how long the connection exists already CONNECTION_CONNECTED_TIME, //how long the connection exists already
CONNECTION_IDLE_TIME, //how long since the last action of this manager CONNECTION_IDLE_TIME, //how long since the last action of this client
CONNECTION_CLIENT_IP, //NEED DB SAVE! //IP of this manager (as seen from the server side) CONNECTION_CLIENT_IP, //NEED DB SAVE! //IP of this client (as seen from the server side)
CONNECTION_CLIENT_PORT, //Port of this manager (as seen from the server side) CONNECTION_CLIENT_PORT, //Port of this client (as seen from the server side)
CONNECTION_SERVER_IP, //IP of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side CONNECTION_SERVER_IP, //IP of the server (seen from the client side) - only available on yourself, not for remote clients, not available server side
CONNECTION_SERVER_PORT, //Port of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side CONNECTION_SERVER_PORT, //Port of the server (seen from the client side) - only available on yourself, not for remote clients, not available server side
CONNECTION_PACKETS_SENT_SPEECH, //how many Speech packets were sent through this connection CONNECTION_PACKETS_SENT_SPEECH, //how many Speech packets were sent through this connection
CONNECTION_PACKETS_SENT_KEEPALIVE, CONNECTION_PACKETS_SENT_KEEPALIVE,
CONNECTION_PACKETS_SENT_CONTROL, CONNECTION_PACKETS_SENT_CONTROL,
@ -399,7 +399,7 @@ namespace ts {
CONNECTION_PACKETLOSS_KEEPALIVE, CONNECTION_PACKETLOSS_KEEPALIVE,
CONNECTION_PACKETLOSS_CONTROL, CONNECTION_PACKETLOSS_CONTROL,
CONNECTION_PACKETLOSS_TOTAL, //the probability with which a packet round trip failed because a packet was lost CONNECTION_PACKETLOSS_TOTAL, //the probability with which a packet round trip failed because a packet was lost
CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH, //the probability with which a speech packet failed from the server to the manager CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH, //the probability with which a speech packet failed from the server to the client
CONNECTION_SERVER2CLIENT_PACKETLOSS_KEEPALIVE, CONNECTION_SERVER2CLIENT_PACKETLOSS_KEEPALIVE,
CONNECTION_SERVER2CLIENT_PACKETLOSS_CONTROL, CONNECTION_SERVER2CLIENT_PACKETLOSS_CONTROL,
CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL, CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL,
@ -858,7 +858,7 @@ namespace ts {
Properties(); Properties();
~Properties(); ~Properties();
Properties(const Properties&) = delete; Properties(const Properties&) = delete;
Properties(Properties&&) = delete; Properties(Properties&&) = default;
std::vector<PropertyWrapper> list_properties(property::flag_type flagMask = (property::flag_type) ~0UL, property::flag_type negatedFlagMask = 0); std::vector<PropertyWrapper> list_properties(property::flag_type flagMask = (property::flag_type) ~0UL, property::flag_type negatedFlagMask = 0);
std::vector<PropertyWrapper> all_properties(); std::vector<PropertyWrapper> all_properties();

View File

@ -58,8 +58,10 @@ class variable {
variable& operator=(const variable& ref); variable& operator=(const variable& ref);
variable& operator=(variable&& ref); variable& operator=(variable&& ref);
std::string key() const { return data->pair.first; } [[nodiscard]] std::string key() const { return this->r_key(); }
std::string value() const { return data->pair.second; } void set_key(const std::string_view& key) const { this->r_key() = key; }
std::string value() const { return this->r_value(); }
VariableType type() const { return data->_type; } VariableType type() const { return data->_type; }
variable clone(){ return variable(key(), value(), type()); } variable clone(){ return variable(key(), value(), type()); }

View File

@ -5,7 +5,6 @@
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <pipes/buffer.h> #include <pipes/buffer.h>
#include "log/LogUtils.h"
#include "escape.h" #include "escape.h"
using namespace std; using namespace std;

View File

@ -2,11 +2,12 @@
// Created by wolverindev on 25.01.20. // Created by wolverindev on 25.01.20.
// //
#include <iostream>
#include "command3.h" #include "command3.h"
using namespace ts; using namespace ts;
command_bulk command_parser::empty_bulk{std::string::npos, ""}; command_bulk command_parser::empty_bulk{std::string::npos, 0, ""};
bool command_parser::parse(bool command) { bool command_parser::parse(bool command) {
this->data = this->_command; this->data = this->_command;
@ -31,8 +32,22 @@ bool command_parser::parse(bool command) {
if(findex == std::string::npos) if(findex == std::string::npos)
findex = this->_command.size(); findex = this->_command.size();
this->_bulks.emplace_back(this->_bulks.size() - 1, this->data.substr(index, findex - index)); this->_bulks.emplace_back(this->_bulks.size() - 1, index, this->data.substr(index, findex - index));
index = findex + 1; index = findex + 1;
} }
return true; return true;
} }
std::optional<size_t> command_parser::next_bulk_containing(const std::string_view &key, size_t start) const {
if(start >= this->bulk_count()) return std::nullopt;
auto index = this->bulk(start).command_character_index();
auto next = this->data.find(key, index);
if(next == std::string::npos) return std::nullopt;
size_t upper_bulk{start + 1};
for(; upper_bulk < this->bulk_count(); upper_bulk++)
if(this->bulk(upper_bulk).command_character_index() > next)
break;
return upper_bulk - 1;
}

View File

@ -26,13 +26,16 @@ namespace ts {
findex += key_size; findex += key_size;
if(findex < max) { if(findex < max) {
if(findex < max && data[findex] != '=') { if(data[findex] == '=')
begin = findex + 1;
else if(data[findex] == ' ')
begin = findex; /* empty value */
else {
index = findex + key_size; index = findex + key_size;
continue; continue;
} }
begin = findex + 1; if(end) *end = data.find(' ', findex);
if(end) *end = data.find(' ', findex + 1);
return true; return true;
} else { } else {
begin = max; begin = max;
@ -46,6 +49,11 @@ namespace ts {
public: public:
[[nodiscard]] inline bool is_empty() const noexcept { return this->data.empty(); } [[nodiscard]] inline bool is_empty() const noexcept { return this->data.empty(); }
[[nodiscard]] inline bool has_key(const std::string_view& key) const {
size_t begin{0};
return value_raw_impl(this->data, key, begin, nullptr);
}
[[nodiscard]] inline std::string_view value_raw(const std::string_view& key) const { [[nodiscard]] inline std::string_view value_raw(const std::string_view& key) const {
bool tmp; bool tmp;
auto result = this->value_raw(key, tmp); auto result = this->value_raw(key, tmp);
@ -54,7 +62,7 @@ namespace ts {
} }
[[nodiscard]] inline std::string_view value_raw(const std::string_view& key, bool& has_been_found) const noexcept { [[nodiscard]] inline std::string_view value_raw(const std::string_view& key, bool& has_been_found) const noexcept {
size_t begin, end; size_t begin{0}, end;
has_been_found = value_raw_impl(this->data, key, begin, &end); has_been_found = value_raw_impl(this->data, key, begin, &end);
if(!has_been_found) return {}; if(!has_been_found) return {};
@ -68,6 +76,13 @@ namespace ts {
return query::unescape(std::string{value}, false); return query::unescape(std::string{value}, false);
} }
[[nodiscard]] inline std::string value(const std::string_view& key, bool& has_been_found) const noexcept {
const auto value = this->value_raw(key, has_been_found);
if(value.empty()) return std::string{};
return query::unescape(std::string{value}, false);
}
template <typename T> template <typename T>
[[nodiscard]] inline T value_as(const std::string_view& key) const { [[nodiscard]] inline T value_as(const std::string_view& key) const {
static_assert(converter<T>::supported, "Target type isn't supported!"); static_assert(converter<T>::supported, "Target type isn't supported!");
@ -76,16 +91,23 @@ namespace ts {
return converter<T>::from_string_view(this->value(key)); return converter<T>::from_string_view(this->value(key));
} }
[[nodiscard]] inline size_t command_character_index() const { return this->abs_index; }
[[nodiscard]] inline size_t key_command_character_index(const std::string_view& key) const {
size_t begin{0};
if(!value_raw_impl(this->data, key, begin, nullptr)) return this->abs_index;
return this->abs_index + begin;
}
protected: protected:
command_string_parser(size_t index, std::string_view data) : index{index}, data{std::move(data)} {} command_string_parser(size_t index, size_t abs_index, std::string_view data) : index{index}, abs_index{abs_index}, data{data} {}
size_t abs_index{};
size_t index{}; size_t index{};
std::string_view data{}; std::string_view data{};
}; };
} }
struct command_bulk : public impl::command_string_parser { struct command_bulk : public impl::command_string_parser {
command_bulk(size_t index, std::string_view data) : command_string_parser{index, std::move(data)} {} command_bulk(size_t index, size_t abs_index, std::string_view data) : command_string_parser{index, abs_index, data} {}
inline bool next_entry(size_t& index, std::string_view& key, std::string& value) const { inline bool next_entry(size_t& index, std::string_view& key, std::string& value) const {
auto next_key = this->data.find_first_not_of(' ', index); auto next_key = this->data.find_first_not_of(' ', index);
@ -119,7 +141,7 @@ namespace ts {
failed failed
}; };
explicit command_parser(std::string command) : _command{std::move(command)}, impl::command_string_parser{std::string::npos, this->_command} { } explicit command_parser(std::string command) : impl::command_string_parser{std::string::npos, 0, command}, _command{std::move(command)} { }
bool parse(bool /* contains identifier */); bool parse(bool /* contains identifier */);
@ -135,25 +157,23 @@ namespace ts {
[[nodiscard]] inline bool has_switch(const std::string_view& key) const noexcept { [[nodiscard]] inline bool has_switch(const std::string_view& key) const noexcept {
size_t index{0}; size_t index{0};
do { do {
index = this->_command_view.find(key, index); index = this->data.find(key, index);
index -= 1; index -= 1;
if(index > key.size()) return false; if(index > key.size()) return false;
if(this->_command_view[index] == '-') return index == 0 || this->_command_view[index - 1] == ' '; if(this->data[index] == '-') return index == 0 || this->data[index - 1] == ' ';
index += 2; index += 2;
} while(true); } while(true);
} }
[[nodiscard]] const std::vector<command_bulk>& bulks() const { return this->_bulks; } [[nodiscard]] const std::vector<command_bulk>& bulks() const { return this->_bulks; }
[[nodiscard]] std::optional<size_t> next_bulk_containing(const std::string_view& /* key */, size_t /* bulk offset */) const;
private: private:
static command_bulk empty_bulk; static command_bulk empty_bulk;
const std::string _command{}; const std::string _command{};
const std::string_view _command_view{};
std::string_view command_type{}; std::string_view command_type{};
std::vector<command_bulk> _bulks{}; std::vector<command_bulk> _bulks{};
std::vector<std::string_view> flags{};
}; };
class command_builder_bulk { class command_builder_bulk {

View File

@ -53,7 +53,7 @@ namespace sql {
return *this; return *this;
} }
std::string fmtStr(){ std::string fmtStr() const {
std::stringstream s; std::stringstream s;
operator<<(s, *this); operator<<(s, *this);
return s.str(); return s.str();
@ -285,12 +285,6 @@ namespace sql {
return *(SelfType*) this; return *(SelfType*) this;
} }
template <typename T>
SelfType& value(const std::initializer_list<T>& val) {
//this->_data->variables.push_back(val.begin());
return *(SelfType*) this;
}
template <typename T> template <typename T>
SelfType& value(const std::string& key, T&& value) { SelfType& value(const std::string& key, T&& value) {
this->_data->variables.push_back(variable{key, value}); this->_data->variables.push_back(variable{key, value});

153
src/sql/insert.h Normal file
View File

@ -0,0 +1,153 @@
#pragma once
namespace sql {
template <typename T>
struct Column {
constexpr explicit Column(const std::string_view& name) : name{name} { }
const std::string name;
};
struct BindingNameGenerator {
public:
[[nodiscard]] static std::string generate(size_t index) {
assert(index > 0);
std::string result{};
result.resize(std::max(index >> 4U, 1UL));
for(auto it = result.begin(); index > 0; index >>= 4U)
*(it++) = number_map[index & 0xFU];
return result;
}
private:
constexpr static std::array<char, 16> number_map{
'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p'
};
};
template <typename... ColTypes>
struct InsertQueryGenerator {
struct GenerateOptions {
bool enable_ignore{false};
sql::SqlType target{sql::SqlType::TYPE_SQLITE};
};
explicit InsertQueryGenerator(std::string target_table, Column<ColTypes>... columns) : table_name{std::move(target_table)}, columns{std::move(columns.name)...} { }
[[nodiscard]] constexpr inline auto column_count() const { return sizeof...(ColTypes); };
[[nodiscard]] inline std::string generate_query(size_t entry_count, sql::SqlType target = sql::SqlType::TYPE_SQLITE, bool enable_ignore = false) const {
if(entry_count == 0) return "-- No entries given";
std::string result{"INSERT "};
if(enable_ignore) {
if(target == sql::TYPE_MYSQL)
result += "IGNORE ";
else
result += "OR IGNORE ";
}
result += this->table_name + " (";
//"INSERT INTO " + this->table_name + " ("
for(auto it = this->columns.begin();;) {
result += "`" + *it + "`";
if(++it != this->columns.end()) {
result += ", ";
} else {
break;
}
}
result += ") VALUES (";
for(size_t index{1}; index <= this->column_count() * entry_count; index++)
result += ":" + BindingNameGenerator::generate(index) + ", ";
result = result.substr(0, result.length() - 2) + ");";
return result;
}
const std::string table_name{};
const std::array<std::string, sizeof...(ColTypes)> columns{};
};
template <typename... ColTypes>
struct InsertQuery : public InsertQueryGenerator<ColTypes...> {
struct ExecuteResult {
std::vector<std::tuple<size_t, sql::result>> failed_entries{};
[[nodiscard]] inline auto has_succeeded() const { return this->failed_entries.empty(); }
};
explicit InsertQuery(const std::string& target_table, Column<ColTypes>... columns) : InsertQueryGenerator<ColTypes...>{target_table, std::move(columns)...} { }
inline void reserve_entries(size_t amount) {
this->entries.reserve(amount);
}
inline void add_entry(const ColTypes&... values) {
this->entries.push_back(std::array<variable, sizeof...(ColTypes)>{variable{"", values}...});
}
inline ExecuteResult execute(sql::SqlManager* sql, bool ignore_fails = false) {
if(this->entries.empty()) return ExecuteResult{};
ExecuteResult result{};
const auto chunk_size = std::min(2UL, this->entries.size());
sql::model chunk_base{sql, this->generate_query(chunk_size, sql->getType(), ignore_fails)};
sql::model entry_model{sql, this->generate_query(1, sql->getType(), ignore_fails)};
for(size_t chunk{0}; chunk < this->entries.size() / chunk_size; chunk++) {
auto command = chunk_base.command();
size_t parameter_index{1};
for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) {
for(auto& var : this->entries[index]) {
var.set_key(":" + BindingNameGenerator::generate(parameter_index++));
command.value(var);
}
}
auto exec_result = command.execute();
if(!exec_result) {
/* try every entry 'till we've found the error one */
for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) {
parameter_index = 1;
auto entry_command = entry_model.command();
for(auto& var : this->entries[index]) {
var.set_key(":" + BindingNameGenerator::generate(parameter_index++));
entry_command.value(var);
}
exec_result = entry_command.execute();
if(!exec_result) {
result.failed_entries.emplace_back(index, std::move(exec_result));
if(!ignore_fails)
return result;
}
}
}
}
for(size_t index{(this->entries.size() / chunk_size) * chunk_size}; index < this->entries.size(); index++) {
size_t parameter_index{1};
auto entry_command = entry_model.command();
for(auto& var : this->entries[index]) {
var.set_key(":" + BindingNameGenerator::generate(parameter_index++));
entry_command.value(var);
}
auto exec_result = entry_command.execute();
if(!exec_result) {
result.failed_entries.emplace_back(index, std::move(exec_result));
if(!ignore_fails)
return result;
}
}
return result;
}
std::vector<std::array<variable, sizeof...(ColTypes)>> entries{};
};
}

View File

@ -49,7 +49,7 @@ inline result parse_url(const string& url, std::map<std::string, std::string>& c
index = idx + 1; index = idx + 1;
} while(index != 0); } while(index != 0);
} }
//TODO: Set CLIENT_MULTI_STATEMENTS
//if(!connect_map["hostName"].get<const char*>() || strcmp(*connect_map["hostName"].get<const char*>(), "")) //if(!connect_map["hostName"].get<const char*>() || strcmp(*connect_map["hostName"].get<const char*>(), ""))
connect_map["hostName"] = target_url; connect_map["hostName"] = target_url;
logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", "hostName", target_url); logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", "hostName", target_url);

View File

@ -54,8 +54,7 @@ std::shared_ptr<CommandData> SqliteManager::copyCommandData(std::shared_ptr<Comm
return __new; return __new;
} }
namespace sql { namespace sql::sqlite {
namespace sqlite {
inline void bindVariable(sqlite3_stmt* stmt, int& valueIndex, const variable& val){ inline void bindVariable(sqlite3_stmt* stmt, int& valueIndex, const variable& val){
valueIndex = sqlite3_bind_parameter_index(stmt, val.key().c_str()); valueIndex = sqlite3_bind_parameter_index(stmt, val.key().c_str());
if(valueIndex == 0){ //TODO maybe throw an exception if(valueIndex == 0){ //TODO maybe throw an exception
@ -81,7 +80,6 @@ namespace sql {
cerr << "Invalid bind. " << sqlite3_errmsg(sqlite3_db_handle(stmt)) << " Index: " << valueIndex << endl; //TODO throw exception cerr << "Invalid bind. " << sqlite3_errmsg(sqlite3_db_handle(stmt)) << " Index: " << valueIndex << endl; //TODO throw exception
} }
} }
}
} }
std::shared_ptr<sqlite3_stmt> SqliteManager::allocateStatement(const std::string& command) { std::shared_ptr<sqlite3_stmt> SqliteManager::allocateStatement(const std::string& command) {

View File

@ -132,10 +132,12 @@ int main() {
ts::command cmd("notify"); ts::command cmd("notify");
*/ */
auto command = ts::command_parser{"a a=c a=c2 -z |-? a=2 key_c=c"}; auto command = ts::command_parser{"a a=x |x b=x | c=x | a=x"};
if(!command.parse(true)) return 1; if(!command.parse(true)) return 1;
print_entries(command); print_entries(command);
auto next = command.next_bulk_containing("a", 0);
std::cout << (next.has_value() ? *next : -1) << "\n";
return 0; return 0;
std::cout << "Command v3:\n"; std::cout << "Command v3:\n";

169
test/SQL2Test.cpp Normal file
View File

@ -0,0 +1,169 @@
#include <string>
#include <array>
#include <utility>
#include <vector>
#include <tuple>
#include <iostream>
#include <cassert>
#include <sql/SqlQuery.h>
template <typename T>
struct Column {
constexpr explicit Column(const std::string_view& name) : name{name} { }
const std::string name;
};
struct BindingNameGenerator {
public:
[[nodiscard]] static std::string generate(size_t index) {
assert(index > 0);
std::string result{};
result.resize(std::max(index >> 4U, 1UL));
for(auto it = result.begin(); index > 0; index >>= 4U)
*(it++) = number_map[index & 0xFU];
return result;
}
private:
constexpr static std::array<char, 16> number_map{
'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p'
};
};
template <typename... ColTypes>
struct InsertQueryGenerator {
struct GenerateOptions {
bool enable_ignore{false};
sql::SqlType target{sql::SqlType::TYPE_SQLITE};
};
explicit InsertQueryGenerator(std::string target_table, Column<ColTypes>... columns) : table_name{std::move(target_table)}, columns{std::move(columns.name)...} { }
[[nodiscard]] constexpr inline auto column_count() const { return sizeof...(ColTypes); };
[[nodiscard]] inline std::string generate_query(size_t entry_count, sql::SqlType target = sql::SqlType::TYPE_SQLITE, bool enable_ignore = false) const {
if(entry_count == 0) return "-- No entries given";
std::string result{"INSERT "};
if(enable_ignore) {
if(target == sql::TYPE_MYSQL)
result += "IGNORE ";
else
result += "OR IGNORE ";
}
result += this->table_name + " (";
//"INSERT INTO " + this->table_name + " ("
for(auto it = this->columns.begin();;) {
result += "`" + *it + "`";
if(++it != this->columns.end()) {
result += ", ";
} else {
break;
}
}
result += ") VALUES (";
for(size_t index{1}; index <= this->column_count() * entry_count; index++)
result += ":" + BindingNameGenerator::generate(index) + ", ";
result = result.substr(0, result.length() - 2) + ");";
return result;
}
const std::string table_name{};
const std::array<std::string, sizeof...(ColTypes)> columns{};
};
template <typename... ColTypes>
struct InsertQuery : public InsertQueryGenerator<ColTypes...> {
struct ExecuteResult {
std::vector<std::tuple<size_t, sql::result>> failed_entries{};
[[nodiscard]] inline auto has_succeeded() const { return this->failed_entries.empty(); }
};
explicit InsertQuery(const std::string& target_table, Column<ColTypes>... columns) : InsertQueryGenerator<ColTypes...>{target_table, std::move(columns)...} { }
inline void reserve_entries(size_t amount) {
this->entries.reserve(amount);
}
inline void add_entry(const ColTypes&... values) {
this->entries.push_back(std::array<variable, sizeof...(ColTypes)>{variable{"", values}...});
}
inline ExecuteResult execute(sql::SqlManager* sql, bool ignore_fails = false) {
if(this->entries.empty()) return ExecuteResult{};
ExecuteResult result{};
const auto chunk_size = std::min(2UL, this->entries.size());
sql::model chunk_base{sql, this->generate_query(chunk_size, sql->getType(), ignore_fails)};
sql::model entry_model{sql, this->generate_query(1, sql->getType(), ignore_fails)};
for(size_t chunk{0}; chunk < this->entries.size() / chunk_size; chunk++) {
auto command = chunk_base.command();
size_t parameter_index{1};
for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) {
for(auto& var : this->entries[index]) {
var.set_key(":" + BindingNameGenerator::generate(parameter_index++));
command.value(var);
}
}
auto exec_result = command.execute();
if(!exec_result) {
/* try every entry 'till we've found the error one */
for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) {
parameter_index = 1;
auto entry_command = entry_model.command();
for(auto& var : this->entries[index]) {
var.set_key(":" + BindingNameGenerator::generate(parameter_index++));
entry_command.value(var);
}
exec_result = entry_command.execute();
if(!exec_result) {
result.failed_entries.emplace_back(index, std::move(exec_result));
if(!ignore_fails)
return result;
}
}
}
}
for(size_t index{(this->entries.size() / chunk_size) * chunk_size}; index < this->entries.size(); index++) {
size_t parameter_index{1};
auto entry_command = entry_model.command();
for(auto& var : this->entries[index]) {
var.set_key(":" + BindingNameGenerator::generate(parameter_index++));
entry_command.value(var);
}
auto exec_result = entry_command.execute();
if(!exec_result) {
result.failed_entries.emplace_back(index, std::move(exec_result));
if(!ignore_fails)
return result;
}
}
return result;
}
std::vector<std::array<variable, sizeof...(ColTypes)>> entries{};
};
int main() {
InsertQuery insert{"hello", Column<int>{"column_a"}, Column<std::string>{"column_ab"}};
for(int i{0}; i < 100; i++)
insert.add_entry(i, "X");
std::cout << "Result: " << insert.generate_query(2) << "\n";
std::string value{};
insert.add_entry(2, value);
insert.execute(nullptr);
}