27using namespace tinysql;
30constexpr std::string_view
kUsage = R
"(Usage: myTinySql [options]
32 --sqlfile=FILE run the statements in FILE ('-' for standard input)
33 --http start the browser server (default port 8080)
34 --port=N listen on port N instead of 8080
35 --allow=LIST comma-separated client IP prefixes the server accepts,
36 e.g. 127.,192.168. (default: everyone)
37 --data=DIR directory with SYSTEM_TABLE and the *_DATA files (default: data)
38 --web=DIR directory with the browser page's files (default: web)
41With no --sqlfile and no --http, statements are read from standard input.
48 std::optional<std::uint16_t>
port;
49 std::optional<std::vector<std::string>>
allow;
50 std::filesystem::path dataDir =
"data";
51 std::filesystem::path webDir =
"web";
57std::vector<std::string>
splitList(std::string_view text)
59 std::vector<std::string> items;
60 while (!text.empty()) {
61 const std::size_t comma = text.find(
',');
62 if (std::string_view item = text.substr(0, comma); !item.empty()) {
63 items.emplace_back(item);
65 text = comma == std::string_view::npos ? std::string_view{} : text.substr(comma + 1);
78 for (
int i = 1; i < argc; ++i) {
79 std::string_view arg = argv[i];
80 std::string_view name = arg;
81 std::optional<std::string_view> value;
82 if (
const std::size_t eq = arg.find(
'='); eq != std::string_view::npos) {
83 name = arg.substr(0, eq);
84 value = arg.substr(eq + 1);
86 auto requireValue = [&]() -> std::string_view {
91 throw std::invalid_argument(std::format(
"{} needs a value", name));
96 if (name ==
"--help" || name ==
"-h") {
99 }
else if (name ==
"--sqlfile") {
100 options.
sqlFile = std::string(requireValue());
101 }
else if (name ==
"--http") {
103 }
else if (name ==
"--port") {
104 const std::string_view text = requireValue();
105 std::uint16_t port = 0;
106 auto [ptr, ec] = std::from_chars(text.data(), text.data() + text.size(), port);
107 if (ec != std::errc{} || ptr != text.data() + text.size() || port == 0) {
108 throw std::invalid_argument(std::format(
"invalid port '{}'", text));
111 }
else if (name ==
"--allow") {
113 }
else if (name ==
"--data") {
114 options.
dataDir = std::string(requireValue());
115 }
else if (name ==
"--web") {
116 options.
webDir = std::string(requireValue());
118 throw std::invalid_argument(std::format(
"unknown option '{}'", arg));
122 throw std::invalid_argument(
"choose either --sqlfile or --http, not both");
133 std::stringstream script;
134 script << in.rdbuf();
151 while (std::cout <<
"tinysql> " << std::flush, std::getline(std::cin, line)) {
171 if (!std::filesystem::is_directory(options.dataDir)) {
172 std::cerr << std::format(
"data directory '{}' not found (use --data=DIR)\n", options.dataDir.string());
177 if (!std::filesystem::is_regular_file(options.webDir /
"index.html")) {
178 std::cerr << std::format(
"web page '{}' not found (use --web=DIR)\n",
179 (options.webDir /
"index.html").string());
183 .
port = options.port.value_or(8080),
184 .dataDir = options.dataDir,
185 .webDir = options.webDir,
186 .allowedClients = options.allow.value_or(std::vector<std::string>{}),
192 Session session(options.dataDir);
193 if (!options.sqlFile || *options.sqlFile ==
"-") {
196 std::ifstream file(*options.sqlFile);
198 std::cerr << std::format(
"cannot open '{}'\n", *options.sqlFile);
202 }
catch (
const std::invalid_argument& e) {
203 std::cerr <<
"myTinySql: " << e.what() <<
"\n\n" <<
kUsage;
205 }
catch (
const std::exception& e) {
206 std::cerr <<
"myTinySql: " << e.what() <<
'\n';
Turning statement results into console text or JSON for the browser page.
std::string renderText(const StatementResult &result)
Formats one result for the console.
The small HTTP server behind --http: the page and its JSON API.
Running statements, and the results they produce.
Runs statements against the tables in one data directory.
std::vector< StatementResult > executeScript(std::string_view script)
Runs every statement of a script, in order.
int main(int argc, char **argv)
Program entry point.
Options parseOptions(int argc, char **argv)
Parses the command line. Options take their value as --name=value or --name value.
int runInteractive(Session &session)
Reads statements at a tinysql> prompt until end of input (Ctrl+D).
constexpr std::string_view kUsage
The text printed by --help and after a wrong option.
int runScript(Session &session, std::istream &in)
Runs a whole script and prints every result.
std::vector< std::string > splitList(std::string_view text)
Splits a comma-separated list.
The command-line options, after parsing.
std::filesystem::path dataDir
--data: the directory holding the tables.
std::optional< std::uint16_t > port
--port: the server port, if not the default.
std::optional< std::vector< std::string > > allow
--allow: client IP prefixes, if given.
std::filesystem::path webDir
--web: the directory holding the browser page.
bool http
--http: start the browser server.
std::optional< std::string > sqlFile
--sqlfile: the script to run (- for standard input).
Settings of the browser server.
std::uint16_t port
TCP port to listen on.
What running one statement produced: a table, a message, or an error.