Changed for new deploy algorithm
This commit is contained in:
parent
ee7f26b7ed
commit
707736d896
@ -270,14 +270,16 @@ if(BUILD_TESTS)
|
||||
target_link_libraries(RingTest ${TEST_LIBRARIES})
|
||||
|
||||
if(NOT WIN32)
|
||||
add_executable(CommandTest ${SOURCE_FILES} ${HEADER_FILES} test/CommandTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h)
|
||||
target_link_libraries(CommandTest ${TEST_LIBRARIES})
|
||||
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 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)
|
||||
target_link_libraries(WebsocketTest ${TEST_LIBRARIES})
|
||||
|
||||
add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h)
|
||||
target_link_libraries(SQLTest ${TEST_LIBRARIES})
|
||||
#add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h)
|
||||
#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)
|
||||
target_link_libraries(ChannelTest ${TEST_LIBRARIES})
|
||||
|
@ -935,6 +935,7 @@ inline void init_mapping() {
|
||||
if(teamspeak::unmapping.empty()) teamspeak::unmapping = build_unmapping();
|
||||
}
|
||||
|
||||
/*
|
||||
template <typename T>
|
||||
inline deque<T> operator+(const deque<T>& a, const deque<T>& b) {
|
||||
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());
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
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>{};
|
||||
@ -953,16 +955,29 @@ inline std::deque<std::string> map_entry(std::string key, teamspeak::GroupType t
|
||||
if(key.find("_needed_modify_power_") == 1) {
|
||||
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);
|
||||
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);
|
||||
else result = {"x_" + key};
|
||||
|
||||
std::deque<std::string> result{};
|
||||
auto mapped_type = mmget(map_table, type, "i_" + 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)
|
||||
entry = "i_needed_modify_power_" + entry.substr(2);
|
||||
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};
|
||||
}
|
||||
|
||||
|
@ -813,11 +813,11 @@ namespace ts {
|
||||
};
|
||||
|
||||
struct PermissionFlaggedValue {
|
||||
PermissionValue value = permNotGranted;
|
||||
bool has_value = false;
|
||||
PermissionValue value{permNotGranted};
|
||||
bool has_value{false};
|
||||
|
||||
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_power() const { return this->has_value && (this->value > 0 || 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 !(*this == other); }
|
||||
|
102
src/Properties.h
102
src/Properties.h
@ -149,11 +149,11 @@ namespace ts {
|
||||
VIRTUALSERVER_HOSTMESSAGE, //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_DEFAULT_SERVER_GROUP, //the manager permissions server group that a new manager gets assigned
|
||||
VIRTUALSERVER_DEFAULT_MUSIC_GROUP, //the manager permissions server group that a new manager gets assigned
|
||||
VIRTUALSERVER_DEFAULT_CHANNEL_GROUP, //the channel permissions group that a new manager gets assigned when joining a channel
|
||||
VIRTUALSERVER_DEFAULT_SERVER_GROUP, //the client permissions server group that a new client 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 client gets assigned when joining a channel
|
||||
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_UPLOAD_TOTAL_BANDWIDTH, //only available on request (=> requestServerVariables)
|
||||
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_TOPIC, //Available for all channels that are "in view", always up-to-date
|
||||
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_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
|
||||
@ -250,13 +250,13 @@ namespace ts {
|
||||
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_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_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_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_FILEPATH, //not available manager side, the folder used for file-transfers for this channel
|
||||
CHANNEL_FLAG_ARE_SUBSCRIBED, //Only available client side, stores whether we are subscribed to 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_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
|
||||
@ -289,62 +289,62 @@ namespace ts {
|
||||
enum ClientProperties {
|
||||
CLIENT_UNDEFINED,
|
||||
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_NICKNAME, //automatically up-to-date for any manager "in view"
|
||||
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 client "in view"
|
||||
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_FLAG_TALKING, //automatically up-to-date for any manager 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_OUTPUT_MUTED, //automatically up-to-date for any manager "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_INPUT_HARDWARE, //automatically up-to-date for any manager "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_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 client "in view", this clients microphone 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 client "in view", this clients headphones/speakers only mute status
|
||||
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 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_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_IS_RECORDING, //automatically up-to-date for any manager "in view"
|
||||
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 client "in view"
|
||||
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
|
||||
CLIENT_KEY_OFFSET, //internal use
|
||||
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_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_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_SERVERGROUPS, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds all servergroups manager belongs too
|
||||
CLIENT_CREATED, //this needs to be requested (=> requestClientVariables), first time this manager connected to this server
|
||||
CLIENT_LASTCONNECTED, //this needs to be requested (=> requestClientVariables), last time this manager connected to this server
|
||||
CLIENT_TOTALCONNECTIONS, //this needs to be requested (=> requestClientVariables), how many times this manager connected to this server
|
||||
CLIENT_AWAY, //automatically up-to-date for any manager "in view", this clients away status
|
||||
CLIENT_AWAY_MESSAGE, //automatically up-to-date for any manager "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_EXACT, //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection
|
||||
CLIENT_FLAG_AVATAR, //automatically up-to-date for any manager "in view", this manager 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_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_MSG, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds matter for the request
|
||||
CLIENT_DESCRIPTION, //automatically up-to-date for any manager "in view"
|
||||
CLIENT_IS_TALKER, //automatically up-to-date for any manager "in view"
|
||||
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 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 client 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 client connected to this server
|
||||
CLIENT_AWAY, //automatically up-to-date for any client "in view", this clients away status
|
||||
CLIENT_AWAY_MESSAGE, //automatically up-to-date for any client "in view", this clients away message
|
||||
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 client "in view", determines if this is a real client or a server-query connection
|
||||
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 client "in view", only valid with PERMISSION feature, holds database client id
|
||||
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 client "in view", only valid with PERMISSION feature, holds matter for the request
|
||||
CLIENT_DESCRIPTION, //automatically up-to-date for any client "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_DOWNLOADED, //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_ONLINE_TIME,
|
||||
CLIENT_MONTH_ONLINE_TIME,
|
||||
CLIENT_IS_PRIORITY_SPEAKER, //automatically up-to-date for any manager "in view"
|
||||
CLIENT_UNREAD_MESSAGES, //automatically up-to-date for any manager "in view"
|
||||
CLIENT_NICKNAME_PHONETIC, //automatically up-to-date for any manager "in view"
|
||||
CLIENT_NEEDED_SERVERQUERY_VIEW_POWER, //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 client "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 client "in view"
|
||||
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_IS_CHANNEL_COMMANDER, //automatically up-to-date for any manager "in view"
|
||||
CLIENT_COUNTRY, //automatically up-to-date for any manager "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_BADGES, //automatically up-to-date for any manager "in view", stores icons for partner badges
|
||||
CLIENT_ICON_ID, //automatically up-to-date for any client "in view"
|
||||
CLIENT_IS_CHANNEL_COMMANDER, //automatically up-to-date for any client "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 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 client "in view", stores icons for partner badges
|
||||
|
||||
CLIENT_MYTEAMSPEAK_ID,
|
||||
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_DEVIATION, //standard deviation of the above average latency
|
||||
CONNECTION_CONNECTED_TIME, //how long the connection exists already
|
||||
CONNECTION_IDLE_TIME, //how long since the last action of this manager
|
||||
CONNECTION_CLIENT_IP, //NEED DB SAVE! //IP of this manager (as seen from the server side)
|
||||
CONNECTION_CLIENT_PORT, //Port of this manager (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_PORT, //Port of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side
|
||||
CONNECTION_IDLE_TIME, //how long since the last action of this client
|
||||
CONNECTION_CLIENT_IP, //NEED DB SAVE! //IP of this client (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 client 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_KEEPALIVE,
|
||||
CONNECTION_PACKETS_SENT_CONTROL,
|
||||
@ -399,7 +399,7 @@ namespace ts {
|
||||
CONNECTION_PACKETLOSS_KEEPALIVE,
|
||||
CONNECTION_PACKETLOSS_CONTROL,
|
||||
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_CONTROL,
|
||||
CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL,
|
||||
@ -858,7 +858,7 @@ namespace ts {
|
||||
Properties();
|
||||
~Properties();
|
||||
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> all_properties();
|
||||
|
@ -58,8 +58,10 @@ class variable {
|
||||
variable& operator=(const variable& ref);
|
||||
variable& operator=(variable&& ref);
|
||||
|
||||
std::string key() const { return data->pair.first; }
|
||||
std::string value() const { return data->pair.second; }
|
||||
[[nodiscard]] std::string key() const { return this->r_key(); }
|
||||
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; }
|
||||
variable clone(){ return variable(key(), value(), type()); }
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <pipes/buffer.h>
|
||||
#include "log/LogUtils.h"
|
||||
#include "escape.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -2,11 +2,12 @@
|
||||
// Created by wolverindev on 25.01.20.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include "command3.h"
|
||||
|
||||
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) {
|
||||
this->data = this->_command;
|
||||
@ -31,8 +32,22 @@ bool command_parser::parse(bool command) {
|
||||
if(findex == std::string::npos)
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
@ -26,13 +26,16 @@ namespace ts {
|
||||
|
||||
findex += key_size;
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
|
||||
begin = findex + 1;
|
||||
if(end) *end = data.find(' ', findex + 1);
|
||||
if(end) *end = data.find(' ', findex);
|
||||
return true;
|
||||
} else {
|
||||
begin = max;
|
||||
@ -46,6 +49,11 @@ namespace ts {
|
||||
public:
|
||||
[[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 {
|
||||
bool 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 {
|
||||
size_t begin, end;
|
||||
size_t begin{0}, end;
|
||||
has_been_found = value_raw_impl(this->data, key, begin, &end);
|
||||
if(!has_been_found) return {};
|
||||
|
||||
@ -68,6 +76,13 @@ namespace ts {
|
||||
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>
|
||||
[[nodiscard]] inline T value_as(const std::string_view& key) const {
|
||||
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));
|
||||
}
|
||||
|
||||
[[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:
|
||||
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{};
|
||||
std::string_view data{};
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
auto next_key = this->data.find_first_not_of(' ', index);
|
||||
@ -119,7 +141,7 @@ namespace ts {
|
||||
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 */);
|
||||
|
||||
@ -135,25 +157,23 @@ namespace ts {
|
||||
[[nodiscard]] inline bool has_switch(const std::string_view& key) const noexcept {
|
||||
size_t index{0};
|
||||
do {
|
||||
index = this->_command_view.find(key, index);
|
||||
index = this->data.find(key, index);
|
||||
index -= 1;
|
||||
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;
|
||||
} while(true);
|
||||
}
|
||||
|
||||
[[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:
|
||||
static command_bulk empty_bulk;
|
||||
|
||||
const std::string _command{};
|
||||
const std::string_view _command_view{};
|
||||
|
||||
std::string_view command_type{};
|
||||
|
||||
std::vector<command_bulk> _bulks{};
|
||||
std::vector<std::string_view> flags{};
|
||||
};
|
||||
|
||||
class command_builder_bulk {
|
||||
|
@ -53,7 +53,7 @@ namespace sql {
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string fmtStr(){
|
||||
std::string fmtStr() const {
|
||||
std::stringstream s;
|
||||
operator<<(s, *this);
|
||||
return s.str();
|
||||
@ -285,12 +285,6 @@ namespace sql {
|
||||
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>
|
||||
SelfType& value(const std::string& key, T&& value) {
|
||||
this->_data->variables.push_back(variable{key, value});
|
||||
|
153
src/sql/insert.h
Normal file
153
src/sql/insert.h
Normal 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{};
|
||||
};
|
||||
|
||||
}
|
@ -49,7 +49,7 @@ inline result parse_url(const string& url, std::map<std::string, std::string>& c
|
||||
index = idx + 1;
|
||||
} while(index != 0);
|
||||
}
|
||||
|
||||
//TODO: Set CLIENT_MULTI_STATEMENTS
|
||||
//if(!connect_map["hostName"].get<const char*>() || strcmp(*connect_map["hostName"].get<const char*>(), ""))
|
||||
connect_map["hostName"] = target_url;
|
||||
logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", "hostName", target_url);
|
||||
|
@ -54,32 +54,30 @@ std::shared_ptr<CommandData> SqliteManager::copyCommandData(std::shared_ptr<Comm
|
||||
return __new;
|
||||
}
|
||||
|
||||
namespace sql {
|
||||
namespace sqlite {
|
||||
inline void bindVariable(sqlite3_stmt* stmt, int& valueIndex, const variable& val){
|
||||
valueIndex = sqlite3_bind_parameter_index(stmt, val.key().c_str());
|
||||
if(valueIndex == 0){ //TODO maybe throw an exception
|
||||
//cerr << "Cant find variable '" + val.key() + "' -> '" + val.value() + "' in query '" + sqlite3_sql(stmt) + "'" << endl;
|
||||
return;
|
||||
}
|
||||
namespace sql::sqlite {
|
||||
inline void bindVariable(sqlite3_stmt* stmt, int& valueIndex, const variable& val){
|
||||
valueIndex = sqlite3_bind_parameter_index(stmt, val.key().c_str());
|
||||
if(valueIndex == 0){ //TODO maybe throw an exception
|
||||
//cerr << "Cant find variable '" + val.key() + "' -> '" + val.value() + "' in query '" + sqlite3_sql(stmt) + "'" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int resultState = 0;
|
||||
if(val.type() == VARTYPE_NULL)
|
||||
resultState = sqlite3_bind_null(stmt, valueIndex);
|
||||
else if(val.type() == VARTYPE_TEXT)
|
||||
resultState = sqlite3_bind_text(stmt, valueIndex, val.value().c_str(), val.value().length(), SQLITE_TRANSIENT);
|
||||
else if(val.type() == VARTYPE_INT || val.type() == VARTYPE_BOOLEAN)
|
||||
resultState = sqlite3_bind_int(stmt, valueIndex, val.as<int32_t>());
|
||||
else if(val.type() == VARTYPE_LONG)
|
||||
resultState = sqlite3_bind_int64(stmt, valueIndex, val.as<int64_t>());
|
||||
else if(val.type() == VARTYPE_DOUBLE || val.type() == VARTYPE_FLOAT)
|
||||
resultState = sqlite3_bind_double(stmt, valueIndex, val.as<double>());
|
||||
else cerr << "Invalid value type!" << endl; //TODO throw exception
|
||||
int resultState = 0;
|
||||
if(val.type() == VARTYPE_NULL)
|
||||
resultState = sqlite3_bind_null(stmt, valueIndex);
|
||||
else if(val.type() == VARTYPE_TEXT)
|
||||
resultState = sqlite3_bind_text(stmt, valueIndex, val.value().c_str(), val.value().length(), SQLITE_TRANSIENT);
|
||||
else if(val.type() == VARTYPE_INT || val.type() == VARTYPE_BOOLEAN)
|
||||
resultState = sqlite3_bind_int(stmt, valueIndex, val.as<int32_t>());
|
||||
else if(val.type() == VARTYPE_LONG)
|
||||
resultState = sqlite3_bind_int64(stmt, valueIndex, val.as<int64_t>());
|
||||
else if(val.type() == VARTYPE_DOUBLE || val.type() == VARTYPE_FLOAT)
|
||||
resultState = sqlite3_bind_double(stmt, valueIndex, val.as<double>());
|
||||
else cerr << "Invalid value type!" << endl; //TODO throw exception
|
||||
|
||||
if(resultState != SQLITE_OK){
|
||||
cerr << "Invalid bind. " << sqlite3_errmsg(sqlite3_db_handle(stmt)) << " Index: " << valueIndex << endl; //TODO throw exception
|
||||
}
|
||||
if(resultState != SQLITE_OK){
|
||||
cerr << "Invalid bind. " << sqlite3_errmsg(sqlite3_db_handle(stmt)) << " Index: " << valueIndex << endl; //TODO throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,10 +132,12 @@ int main() {
|
||||
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;
|
||||
|
||||
print_entries(command);
|
||||
auto next = command.next_bulk_containing("a", 0);
|
||||
std::cout << (next.has_value() ? *next : -1) << "\n";
|
||||
|
||||
return 0;
|
||||
std::cout << "Command v3:\n";
|
||||
|
169
test/SQL2Test.cpp
Normal file
169
test/SQL2Test.cpp
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user