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})
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})

View File

@ -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};
}

View File

@ -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); }

View File

@ -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();

View File

@ -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()); }

View File

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

View File

@ -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;
}

View File

@ -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 {

View File

@ -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
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;
} 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);

View File

@ -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
}
}
}

View File

@ -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
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);
}