255 lines
6.9 KiB
C++

/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2015 Daniel Vassdal <shutter@canternet.org>
* Copyright (C) 2013, 2017-2019, 2021-2022, 2024 Sadie Powell <sadie@witchery.services>
* Copyright (C) 2012 Robby <robby@chatbelgie.be>
* Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace SQL
{
class Error;
class Provider;
class Query;
class Result;
/** A list of parameter replacement values. */
typedef std::vector<std::string> ParamList;
/** A map of parameter replacement values. */
typedef std::map<std::string, std::string> ParamMap;
/** A single SQL field. */
typedef std::optional<std::string> Field;
/** A list of SQL fields from a specific row. */
typedef std::vector<Field> Row;
/** An enumeration of possible error codes. */
enum ErrorCode
{
/** No error has occurred. */
SUCCESS,
/** The database identifier is invalid. */
BAD_DBID,
/** The database connection has failed. */
BAD_CONN,
/** Executing the query failed. */
QSEND_FAIL,
/** Reading the response failed. */
QREPLY_FAIL
};
/** Populates a parameter map with information about a user.
* @param user The user to collect information from.
* @param userinfo The map to populate.
*/
void PopulateUserInfo(User* user, ParamMap& userinfo);
}
/** Represents the result of an SQL query. */
class SQL::Result
: public Cullable
{
public:
/**
* Return the number of rows in the result.
*
* Note that if you have performed an INSERT or UPDATE query or other
* query which will not return rows, this will return the number of
* affected rows. In this case you SHOULD NEVER access any of the result
* set rows, as there aren't any!
* @returns Number of rows in the result set.
*/
virtual int Rows() = 0;
/** Retrieves the next available row from the database.
* @param result A list to store the fields from this row in.
* @return True if a row could be retrieved; otherwise, false.
*/
virtual bool GetRow(Row& result) = 0;
/** Retrieves a list of SQL columns in the result.
* @param result A reference to the vector to store column names in.
*/
virtual void GetCols(std::vector<std::string>& result) = 0;
/**
* Check if there's a column with the specified name in the result
*
* @param column The column name.
* @param index The place to store the column index if it exists.
* @returns If the column exists then true; otherwise, false.
*/
virtual bool HasColumn(const std::string& column, size_t& index) = 0;
};
/** SQL::Error holds the error state of a request.
* The error string varies from database software to database software
* and should be used to display informational error messages to users.
*/
class SQL::Error final
{
private:
/** The custom error message if one has been specified. */
const std::string message;
public:
/** The code which represents this error. */
const ErrorCode code;
/** Initialize an SQL::Error from an error code.
* @param c A code which represents this error.
*/
Error(ErrorCode c)
: code(c)
{
}
/** Initialize an SQL::Error from an error code and a custom error message.
* @param c A code which represents this error.
* @param m A custom error message.
*/
Error(ErrorCode c, const std::string& m)
: message(m)
, code(c)
{
}
/** Retrieves the error message. */
const char* ToString() const
{
if (!message.empty())
return message.c_str();
switch (code)
{
case BAD_DBID:
return "Invalid database identifier";
case BAD_CONN:
return "Invalid connection";
case QSEND_FAIL:
return "Sending query failed";
case QREPLY_FAIL:
return "Getting query result failed";
default:
return "Unknown error";
}
}
};
/**
* Object representing an SQL query. This should be allocated on the heap and
* passed to an SQL::Provider, which will free it when the query is complete or
* when the querying module is unloaded.
*
* You should store whatever information is needed to have the callbacks work in
* this object (UID of user, channel name, etc).
*/
class SQL::Query
: public Cullable
{
protected:
/** Creates a new SQL query. */
Query(Module* Creator)
: creator(Creator)
{
}
public:
const ModuleRef creator;
/** Called when an SQL error happens.
* @param error The error that occurred.
*/
virtual void OnError(const SQL::Error& error) = 0;
/** Called when a SQL result is received.
* @param result The result of the SQL query.
*/
virtual void OnResult(SQL::Result& result) = 0;
};
/**
* Provider object for SQL servers
*/
class SQL::Provider
: public DataProvider
{
private:
/** The name of the database tag in the config. */
const std::string dbid;
public:
Provider(Module* Creator, const std::string& Name)
: DataProvider(Creator, "SQL/" + Name)
, dbid(Name)
{
}
/** Retrieves the name of the database tag in the config. */
const std::string& GetId() const { return dbid; }
/** Submit an asynchronous SQL query.
* @param callback The result reporting point
* @param query The hardcoded query string. If you have parameters to substitute, see below.
*/
virtual void Submit(Query* callback, const std::string& query) = 0;
/** Submit an asynchronous SQL query.
* @param callback The result reporting point
* @param format The simple parameterized query string ('?' parameters)
* @param p Parameters to fill in for the '?' entries
*/
virtual void Submit(Query* callback, const std::string& format, const SQL::ParamList& p) = 0;
/** Submit an asynchronous SQL query.
* @param callback The result reporting point
* @param format The parameterized query string ('$name' parameters)
* @param p Parameters to fill in for the '$name' entries
*/
virtual void Submit(Query* callback, const std::string& format, const ParamMap& p) = 0;
};
inline void SQL::PopulateUserInfo(User* user, ParamMap& userinfo)
{
userinfo.insert({
{ "address", user->GetAddress() },
{ "dhost", user->GetDisplayedHost() },
{ "duser", user->GetDisplayedUser() },
{ "host", user->GetRealHost() },
{ "nick", user->nick },
{ "real", user->GetRealName() },
{ "server", user->server->GetName() },
{ "sid", user->server->GetId() },
{ "user", user->GetRealUser() },
{ "uuid", user->uuid },
});
// Deprecated keys.
userinfo.insert({
{ "ident", userinfo["user"] },
{ "ip" , userinfo["address"] },
});
}