blob: 84a09fd160426ec9d095cfb77a0e5293f534980d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <array>
#include <cctype>
#include <cstdint>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include "third_party/sqlite/fuzz/icu_codes.pb.h"
#include "third_party/sqlite/fuzz/sql_queries.pb.h"
#include "third_party/sqlite/fuzz/sql_query_grammar.pb.h"
#include "third_party/sqlite/fuzz/sql_query_proto_to_string.h"
using namespace sql_query_grammar;
#define CONV_FN(TYPE, VAR_NAME) std::string TYPE##ToString(const TYPE& VAR_NAME)
#define RETURN_IF_DISABLED_QUERY(TYPE) \
if (disabled_queries_.count(#TYPE) != 0) \
return "";
namespace sql_fuzzer {
namespace {
constexpr uint32_t kMaxColumnNumber = 20;
#if !defined(FUZZ_FTS3)
constexpr uint32_t kMaxTableNumber = 8;
#endif
constexpr uint32_t kMaxSchemaNumber = 4;
constexpr uint32_t kMaxWindowNumber = 5;
constexpr uint32_t kMaxColumnConstraintNumber = 10;
constexpr uint32_t kMaxTableConstraintNumber = 10;
constexpr uint32_t kMaxIndexNumber = 10;
// should be less than kMaxTableNumber
constexpr uint32_t kMaxFTS3TableNumber = 2;
constexpr uint32_t kMaxStrLength =
200; // So these are readable and somewhat performant, keep a maximum
// string length......
#if !defined(FUZZ_OMIT_SAVEPOINT)
constexpr uint32_t kMaxSavePointNumber = 10;
#endif
constexpr uint32_t kMaxViewNumber = 5;
constexpr uint32_t kMaxTriggerNumber = 10;
std::set<std::string> disabled_queries_;
} // namespace
CONV_FN(Expr, expr);
CONV_FN(Select, select);
CONV_FN(TableOrSubquery, tos);
CONV_FN(FTS3Table, ft);
CONV_FN(FTS3NearQuery, fnq);
CONV_FN(FTS3AuxiliaryFn, faf);
CONV_FN(FTS3MatchFormat, fmf);
CONV_FN(DateAndTimeFn, sfn);
CONV_FN(ExprSchemaTableFn, estf);
// ~~~~Numbered values to string~~~
// WARNING does not include space at the end
CONV_FN(Column, col) {
if (col.has_rowid() && col.rowid())
return "rowid";
#if defined(FUZZ_FTS3)
if (col.has_fts3_docid() && col.fts3_docid())
return "docid";
if (col.has_fts3_table())
return FTS3TableToString(col.fts3_table());
#endif
std::string ret("Col");
ret += std::to_string(col.column() % kMaxColumnNumber);
return ret;
}
// WARNING does not include space at the end
CONV_FN(Table, table) {
std::string ret("Table");
#if defined(FUZZ_FTS3)
// only fuzzing FTS3 tables, clamp to the max FTS3 table num.
ret += std::to_string(table.table() & kMaxFTS3TableNumber);
if (table.fts3_content_table())
ret += "_content";
#else
ret += std::to_string(table.table() % kMaxTableNumber);
#endif
return ret;
}
// WARNING does not include space at the end
CONV_FN(Schema, schema) {
if (schema.main()) {
return "main";
}
if (schema.temp()) {
return "temp";
}
std::string ret("Schema");
ret += std::to_string(schema.schema() % kMaxSchemaNumber);
return ret;
}
// WARNING does not include space at the end
CONV_FN(WindowName, win) {
std::string ret("Window");
ret += std::to_string(win.window_name() % kMaxWindowNumber);
return ret;
}
// WARNING does not include space at the end
CONV_FN(ColumnConstraintName, cc) {
std::string ret("ColConstraint");
ret += std::to_string(cc.constraint_name() % kMaxColumnConstraintNumber);
return ret;
}
// WARNING does not include space at the end
CONV_FN(TableConstraintName, tc) {
std::string ret("TableConstraint");
ret += std::to_string(tc.constraint_name() % kMaxTableConstraintNumber);
return ret;
}
// WARNING does not include space at the end
CONV_FN(Index, index) {
std::string ret("Index");
ret += std::to_string(index.index() % kMaxIndexNumber);
return ret;
}
#if !defined(FUZZ_OMIT_SAVEPOINT)
CONV_FN(SavePoint, sp) {
std::string ret("SavePoint");
ret += std::to_string(sp.savepoint_num() % kMaxSavePointNumber);
return ret;
}
#endif
CONV_FN(View, v) {
std::string ret("View");
ret += std::to_string(v.view() % kMaxViewNumber);
return ret;
}
CONV_FN(Trigger, t) {
std::string ret("Trigger");
ret += std::to_string(t.trigger() % kMaxTriggerNumber);
return ret;
}
// ~~~~Utility functions~~~~
std::string AscDescToString(AscDesc a) {
switch (a) {
case ASCDESC_NONE:
return " ";
case ASCDESC_ASC:
return "ASC ";
case ASCDESC_DESC:
return "DESC ";
default:
return " ";
}
}
std::string StrToLower(std::string s) {
std::transform(
s.begin(), s.end(), s.begin(),
[](unsigned char c) -> unsigned char { return std::tolower(c); });
return s;
}
std::string StripTrailingUnderscores(std::string s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
[](unsigned char ch) { return ch != '_'; })
.base(),
s.end());
return s;
}
// Converts underscores to spaces in a string.
// This is because many enums like SET_NULL will be displayed at SET NULL in
// the query, and I want to use protobuf's enum to string function to save time.
std::string EnumStrReplaceUnderscores(std::string s) {
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) -> unsigned char {
if (c == '_')
return ' ';
return c;
});
return s;
}
// Takes garbage data and produces a string, with quotes escaped.
// Caps the number of bytes received from the protobuf at kMaxStrLength.
// The final string could be as much as kMaxStrLength*2 as we added an extra
// single quote for every single quote in the input string.
std::string ConvertToSqlString(const std::string& s) {
std::string ret;
ret.reserve(kMaxStrLength * 2);
for (size_t i = 0; i < s.length() && i < kMaxStrLength; i++) {
ret += s[i];
if (s[i] == '\'')
ret += '\'';
}
return ret;
}
// WARNING does not include space
std::string BytesToHex(const std::string& str) {
std::ostringstream ss;
ss << std::hex << std::setfill('0');
for (size_t i = 0; i < str.size() && i < kMaxStrLength; i++) {
ss << std::setw(2) << static_cast<int>(str[i]);
}
return ss.str();
}
// WARNING no space at end
CONV_FN(ExprSchemaTable, st) {
std::string ret;
if (st.has_schema_name()) {
ret += SchemaToString(st.schema_name());
ret += ".";
}
ret += TableToString(st.table_name());
return ret;
}
CONV_FN(ExprSchemaTableColumn, stc) {
std::string ret;
if (stc.has_schema()) {
ret += SchemaToString(stc.schema());
ret += ".";
}
if (stc.has_table()) {
ret += TableToString(stc.table());
ret += ".";
}
ret += ColumnToString(stc.col());
return ret;
}
// WARNING does not include parentheses, nor a space at the end
CONV_FN(ColumnList, cl) {
std::string ret = ColumnToString(cl.col());
for (int i = 0; i < cl.extra_cols_size(); i++) {
ret += ", ";
ret += ColumnToString(cl.extra_cols(i));
}
return ret;
}
CONV_FN(ExprList, me) {
std::string ret = ExprToString(me.expr());
for (int i = 0; i < me.extra_exprs_size(); i++) {
ret += ", ";
ret += ExprToString(me.extra_exprs(i));
}
return ret;
}
// WARNING does not include space
CONV_FN(CollateType, collate_type) {
std::string ct = CollateType_Name(collate_type);
ct.erase(0, std::string("COLLATE_").length());
return ct;
}
// WARNING does not include space
CONV_FN(IndexedColumn, ic) {
std::string ret;
if (ic.has_expr()) {
ret += ExprToString(ic.expr());
} else {
ret += ColumnToString(ic.col());
}
ret += " ";
if (ic.has_collate_type()) {
ret += "COLLATE ";
ret += CollateTypeToString(ic.collate_type());
ret += " ";
}
ret += AscDescToString(ic.asc_desc());
return ret;
}
CONV_FN(IndexedColumnList, ic_list) {
std::string ret;
ret += IndexedColumnToString(ic_list.indexed_col());
for (int i = 0; i < ic_list.extra_indexed_cols_size(); i++) {
ret += ", ";
ret += IndexedColumnToString(ic_list.extra_indexed_cols(i));
}
return ret;
}
CONV_FN(SchemaTableAsAlias, staa) {
std::string ret;
ret += ExprSchemaTableToString(staa.schema_table());
ret += " ";
if (staa.has_as_table_alias()) {
ret += "AS ";
ret += TableToString(staa.as_table_alias());
ret += " ";
}
return ret;
}
// ~~~~Expression stuff~~~~
// WARNING does not include space
CONV_FN(NumericLiteral, nl) {
static constexpr char hex_digits[] = "0123456789ABCDEF";
static constexpr char digits[] = "0123456789";
std::string ret;
if (nl.hex_digits_size() > 0) {
ret += "0x";
for (int i = 0; i < nl.hex_digits_size(); i++) {
ret += hex_digits[nl.hex_digits(i) % sizeof(hex_digits)];
}
return ret;
}
for (int i = 0; i < nl.digits_size(); i++) {
ret += digits[nl.digits(i) % sizeof(digits)];
}
if (nl.decimal_point()) {
ret += ".";
if (nl.dec_digits_size() == 0) {
ret += "0";
} else {
for (int i = 0; i < nl.dec_digits_size(); i++) {
ret += digits[nl.dec_digits(i) % sizeof(digits)];
}
}
}
if (nl.exp_digits_size() > 0) {
ret += "E";
if (nl.negative_exp())
ret += "-";
if (nl.exp_digits_size() == 0) {
ret += "0";
} else {
for (int i = 0; i < nl.exp_digits_size(); i++) {
ret += digits[nl.exp_digits(i) % sizeof(digits)];
}
}
}
return ret;
}
// WARNING does not include space
CONV_FN(LiteralValue, lit_val) {
std::string ret;
using LitValType = LiteralValue::LitValOneofCase;
switch (lit_val.lit_val_oneof_case()) {
case LitValType::kNumLit:
return std::to_string(lit_val.num_lit());
case LitValType::kStringLit:
ret += '\'';
ret += ConvertToSqlString(lit_val.string_lit());
ret += '\'';
return ret;
case LitValType::kBlobLit:
ret += "x\'";
ret += BytesToHex(lit_val.blob_lit());
ret += '\'';
return ret;
case LitValType::kSpecialVal:
// special case for NULL, TRUE, FALSE
if (lit_val.special_val() == LiteralValue::VAL_NULL)
return "NULL";
if (lit_val.special_val() == LiteralValue::VAL_TRUE)
return "TRUE";
if (lit_val.special_val() == LiteralValue::VAL_FALSE)
return "FALSE";
// do not remove underscores
return LiteralValue_SpecialVal_Name(lit_val.special_val());
case LitValType::kNumericLit:
return NumericLiteralToString(lit_val.numeric_lit());
default:
return "1";
}
}
CONV_FN(UnaryExpr, uexpr) {
std::string ret;
switch (uexpr.unary_op()) {
case UNOP_MINUS:
ret += "-";
break;
case UNOP_PLUS:
ret += "+";
break;
case UNOP_TILDE:
ret += "~";
break;
case UNOP_NOT:
ret += "NOT ";
break;
default:
break;
}
ret += ExprToString(uexpr.expr());
return ret;
}
CONV_FN(BinaryOperator, bop) {
switch (bop) {
case BINOP_CONCAT:
return " || ";
break;
case BINOP_STAR:
return " * ";
break;
case BINOP_SLASH:
return " / ";
break;
case BINOP_PERCENT:
return " % ";
break;
case BINOP_PLUS:
return " + ";
break;
case BINOP_MINUS:
return " - ";
break;
case BINOP_LELE:
return " << ";
break;
case BINOP_GRGR:
return " >> ";
break;
case BINOP_AMPERSAND:
return " & ";
break;
case BINOP_PIPE:
return " | ";
break;
case BINOP_LE:
return " < ";
break;
case BINOP_LEQ:
return " <= ";
break;
case BINOP_GR:
return " > ";
break;
case BINOP_GREQ:
return " >= ";
break;
case BINOP_EQ:
return " = ";
break;
case BINOP_EQEQ:
return " == ";
break;
case BINOP_NOTEQ:
return " != ";
break;
case BINOP_LEGR:
return " <> ";
break;
case BINOP_IS:
return " IS ";
break;
case BINOP_ISNOT:
return " IS NOT ";
break;
case BINOP_IN:
return " IN "; // CORPUS specialize?
break;
case BINOP_LIKE:
return " LIKE "; // CORPUS specialize?
break;
case BINOP_GLOB:
return " GLOB "; // CORPUS
break;
case BINOP_MATCH:
return " MATCH "; // CORPUS
break;
case BINOP_REGEXP:
return " REGEXP "; // CORPUS
break;
case BINOP_AND:
return " AND ";
break;
case BINOP_OR:
return " OR ";
break;
default:
return " AND ";
break;
}
}
// TODO(mpdenton) generate better REGEXP queries in non-fts3 case. (in
// ColumnComparison as well)
// TODO(mpdenton) generate better MATCH queries in non-fts3 case.
CONV_FN(BinaryExpr, bexpr) {
std::string ret;
ret += ExprToString(bexpr.lhs());
ret += BinaryOperatorToString(bexpr.op());
#if defined(FUZZ_FTS3)
if (bexpr.op() == BINOP_MATCH && bexpr.has_fmt()) {
ret += FTS3MatchFormatToString(bexpr.fmt());
return ret;
}
#endif
ret += ExprToString(bexpr.rhs());
return ret;
}
CONV_FN(AggregateFn, af) {
std::string ret;
ret += StrToLower(AggregateFn_FnName_Name(af.fn_name()));
ret += "(";
if (af.fn_name() == AggregateFn::COUNT && af.count_star())
return ret + "*) ";
if (af.distinct())
ret += "DISTINCT ";
if (af.has_col1()) {
ret += ColumnToString(af.col1());
if (af.fn_name() == AggregateFn::GROUP_CONCAT && af.has_col2()) {
ret += ", ";
ret += ColumnToString(af.col2());
}
} else {
ret += ExprToString(af.expr1());
if (af.fn_name() == AggregateFn::GROUP_CONCAT && af.has_expr2()) {
ret += ", ";
ret += ExprToString(af.expr2());
}
}
ret += ") ";
return ret;
}
CONV_FN(ZeroArgFn, zaf) {
std::string func = ZeroArgFn_Name(zaf);
// Remove ZFN_ prefix
func.erase(0, std::string("ZFN_").length());
return StrToLower(func) + "() ";
}
CONV_FN(OneArgFn, oaf) {
std::string ret;
ret += StripTrailingUnderscores(
StrToLower(OneArgFn_OneArgFnEnum_Name(oaf.fn_enum())));
ret += "(";
ret += ExprToString(oaf.arg1());
ret += ") ";
return ret;
}
CONV_FN(TwoArgFn, taf) {
std::string ret;
ret += StrToLower(TwoArgFn_TwoArgFnEnum_Name(taf.fn_enum()));
ret += "(";
ret += ExprToString(taf.arg1());
ret += ", ";
ret += ExprToString(taf.arg2());
ret += ") ";
return ret;
}
CONV_FN(ThreeArgFn, taf) {
std::string ret;
ret += StrToLower(ThreeArgFn_ThreeArgFnEnum_Name(taf.fn_enum()));
ret += "(";
ret += ExprToString(taf.arg1());
ret += ", ";
ret += ExprToString(taf.arg2());
ret += ", ";
ret += ExprToString(taf.arg3());
ret += ") ";
return ret;
}
CONV_FN(VarNumFn, vfn) {
std::string ret;
ret += StrToLower(VarNumFn_VarNumFnEnum_Name(vfn.fn_enum()));
ret += "(";
ret += ExprToString(vfn.arg1());
ret += ", ";
ret += ExprToString(vfn.arg2());
for (int i = 0; i < vfn.other_args_size(); i++) {
ret += ", ";
ret += ExprToString(vfn.other_args(i));
}
ret += ") ";
return ret;
}
CONV_FN(CharFn, cfn) {
std::string ret("char(");
ret += std::to_string(cfn.char_());
for (int i = 0; i < cfn.extra_chars_size(); i++) {
ret += ", ";
ret += std::to_string(cfn.extra_chars(i));
}
ret += ") ";
return ret;
}
CONV_FN(SimpleFn, sfn) {
// oneof
if (sfn.has_zero_arg_fn()) {
return ZeroArgFnToString(sfn.zero_arg_fn());
} else if (sfn.has_one_arg_fn()) {
return OneArgFnToString(sfn.one_arg_fn());
} else if (sfn.has_two_arg_fn()) {
return TwoArgFnToString(sfn.two_arg_fn());
} else if (sfn.has_three_arg_fn()) {
return ThreeArgFnToString(sfn.three_arg_fn());
} else if (sfn.has_varnum_fn()) {
return VarNumFnToString(sfn.varnum_fn());
} else if (sfn.has_char_fn()) {
return CharFnToString(sfn.char_fn());
} else {
return "changes() ";
}
}
CONV_FN(PrintfFormatSpecifier, pfs) {
std::string ret("%");
for (int i = 0; i < pfs.flags_size(); i++) {
switch (pfs.flags(i)) {
case PrintfFormatSpecifier::MINUS:
ret += "-";
break;
case PrintfFormatSpecifier::PLUS:
ret += "+";
break;
case PrintfFormatSpecifier::SPACE:
ret += " ";
break;
case PrintfFormatSpecifier::ZERO:
ret += "0";
break;
case PrintfFormatSpecifier::HASH:
ret += "#";
break;
case PrintfFormatSpecifier::COMMA:
ret += ",";
break;
case PrintfFormatSpecifier::BANG:
ret += "!";
break;
}
}
if (pfs.has_width()) {
ret += std::to_string(pfs.width());
} else if (pfs.width_star()) {
ret += "*";
}
if (pfs.has_precision()) {
ret += ".";
ret += std::to_string(pfs.precision());
}
if (pfs.has_length()) {
if (pfs.length() % 3 == 1) {
ret += "l";
} else if (pfs.length() % 3 == 2) {
ret += "ll";
}
}
if (pfs.percent()) {
ret += "%";
} else {
std::string specifier = PrintfFormatSpecifier_SubType_Name(pfs.sub_type());
if (pfs.lowercase())
specifier = StrToLower(specifier);
ret += specifier;
}
return ret;
}
CONV_FN(Printf, p) {
std::string ret("printf(\'");
for (int i = 0; i < p.specifiers_size(); i++) {
ret += PrintfFormatSpecifierToString(p.specifiers(i));
if (i < p.strings_size()) {
ret += ConvertToSqlString(p.strings(i));
}
}
ret += "\'";
for (int i = 0; i < p.exprs_size(); i++) {
ret += ", ";
ret += ExprToString(p.exprs(i));
}
ret += ") ";
return ret;
}
CONV_FN(Fn, fn) {
// oneof
if (fn.has_simple_fn()) {
return SimpleFnToString(fn.simple_fn());
} else if (fn.has_fts_aux_fn()) {
#if defined(FUZZ_FTS3)
return FTS3AuxiliaryFnToString(fn.fts_aux_fn()) + " ";
#else
return "changes() ";
#endif
} else if (fn.has_dat_fn()) {
return DateAndTimeFnToString(fn.dat_fn());
} else if (fn.has_aggregate_fn()) {
return AggregateFnToString(fn.aggregate_fn());
} else if (fn.has_printf()) {
return PrintfToString(fn.printf());
} else {
return "changes() ";
}
}
CONV_FN(ParenthesesExpr, pexpr) {
std::string ret("(");
ret += ExprToString(pexpr.expr());
for (int i = 0; i < pexpr.other_exprs_size(); i++) {
ret += ", ";
ret += ExprToString(pexpr.other_exprs(i));
}
ret += ") ";
return ret;
}
CONV_FN(CastExpr, cexpr) {
std::string ret("CAST(");
ret += ExprToString(cexpr.expr());
ret += " AS ";
ret += EnumStrReplaceUnderscores(
CastTypeName_CastTypeNameEnum_Name(cexpr.type_name().type_enum()));
ret += ") ";
return ret;
}
CONV_FN(CollateExpr, cexpr) {
std::string ret;
ret += ExprToString(cexpr.expr());
ret += " COLLATE ";
ret += CollateTypeToString(cexpr.collate_type());
return ret;
}
CONV_FN(Expr1, e) {
std::string ret;
ret += ExprToString(e.expr1());
ret += " ";
if (e.not_())
ret += "NOT ";
ret += EnumStrReplaceUnderscores(Expr1_PossibleKeywords_Name(e.keyword()));
ret += " ";
ret += ExprToString(e.expr2());
ret += " "; //
if (e.has_escape_expr()) {
ret += "ESCAPE ";
ret += ExprToString(e.escape_expr());
ret += " "; //
}
return ret;
}
CONV_FN(ExprNullTests, e) {
std::string ret = ExprToString(e.expr());
ret += " ";
ret += EnumStrReplaceUnderscores(
ExprNullTests_PossibleKeywords_Name(e.keyword()));
ret += " ";
return ret;
}
CONV_FN(ExprIs, e) {
std::string ret = ExprToString(e.expr1());
ret += " IS ";
if (e.not_())
ret += "NOT ";
ret += ExprToString(e.expr2());
ret += " "; //
return ret;
}
CONV_FN(ExprBetween, e) {
std::string ret;
ret += ExprToString(e.expr1());
ret += " ";
if (e.not_())
ret += "NOT ";
ret += "BETWEEN ";
ret += ExprToString(e.expr2());
ret += " AND ";
ret += ExprToString(e.expr3());
return ret;
}
CONV_FN(ExprInParen, e) {
std::string ret("(");
// oneof
if (e.has_select()) {
ret += SelectToString(e.select());
} else if (e.has_exprs()) {
ret += ExprListToString(e.exprs());
}
ret += ") ";
return ret;
}
CONV_FN(ExprIn, e) {
std::string ret = ExprToString(e.expr());
ret += " ";
if (e.not_())
ret += "NOT ";
if (e.has_expr_in_paren()) {
ret += ExprInParenToString(e.expr_in_paren());
} else if (e.has_schema_table()) {
ret += ExprSchemaTableToString(e.schema_table());
} else if (e.has_schema_table_fn()) {
ret += ExprSchemaTableFnToString(e.schema_table_fn());
} else {
ret += "()";
}
return ret + " ";
}
CONV_FN(ExprExists, e) {
std::string ret;
if (e.not_())
ret += "NOT EXISTS ";
else if (e.exists())
ret += "EXISTS ";
ret += "(";
ret += SelectToString(e.select());
ret += ") ";
return ret;
}
// WARNING no space at end
CONV_FN(ExprWhenThen, e) {
std::string ret("WHEN ");
ret += ExprToString(e.when_expr());
ret += " THEN ";
ret += ExprToString(e.then_expr());
return ret;
}
CONV_FN(ExprCase, e) {
std::string ret("CASE ");
if (e.has_expr()) {
ret += ExprToString(e.expr());
ret += " ";
}
ret += ExprWhenThenToString(e.when_then());
ret += " ";
for (int i = 0; i < e.extra_when_thens_size(); i++) {
ret += ExprWhenThenToString(e.extra_when_thens(i));
ret += " ";
}
if (e.has_else_expr()) {
ret += "ELSE ";
ret += ExprToString(e.else_expr());
ret += " ";
}
ret += "END ";
return ret;
}
CONV_FN(ExprRaiseFn, e) {
std::string ret("RAISE(");
if (e.ignore()) {
ret += "IGNORE";
} else {
ret +=
EnumStrReplaceUnderscores(ExprRaiseFn_RaiseFnEnum_Name(e.raise_fn()));
ret += " ";
ret += ", \'";
ret += ConvertToSqlString(e.error_msg());
ret += "\'";
}
ret += ") ";
return ret;
}
CONV_FN(ComplicatedExpr, expr) {
using ExprType = ComplicatedExpr::ComplicatedExprOneofCase;
switch (expr.complicated_expr_oneof_case()) {
case ExprType::kExprStc:
return ExprSchemaTableColumnToString(expr.expr_stc());
case ExprType::kUnaryExpr:
return UnaryExprToString(expr.unary_expr());
case ExprType::kBinaryExpr:
return BinaryExprToString(expr.binary_expr());
case ExprType::kFnExpr:
return FnToString(expr.fn_expr());
case ExprType::kParExpr:
return ParenthesesExprToString(expr.par_expr());
case ExprType::kCastExpr:
return CastExprToString(expr.cast_expr());
case ExprType::kCollateExpr:
return CollateExprToString(expr.collate_expr());
case ExprType::kExpr1:
return Expr1ToString(expr.expr1());
case ExprType::kExprNullTests:
return ExprNullTestsToString(expr.expr_null_tests());
case ExprType::kExprIs:
return ExprIsToString(expr.expr_is());
case ExprType::kExprBetween:
return ExprBetweenToString(expr.expr_between());
case ExprType::kExprIn:
return ExprInToString(expr.expr_in());
case ExprType::kExprExists:
return ExprExistsToString(expr.expr_exists());
case ExprType::kExprCase:
return ExprCaseToString(expr.expr_case());
case ExprType::kExprRaise:
return ExprRaiseFnToString(expr.expr_raise());
default:
return "1";
}
}
// TODO(mpdenton) wrap in parentheses???
CONV_FN(Expr, expr) {
if (expr.has_lit_val()) {
return LiteralValueToString(expr.lit_val());
} else if (expr.has_comp_expr()) {
return ComplicatedExprToString(expr.comp_expr());
} else { // default
return "1";
}
}
// ~~~~Other~~~~
std::string ForeignKeyClauseNotMatchToString(
const ForeignKeyClauseNotMatch& nm) {
std::string ret("ON ");
ret += EnumStrReplaceUnderscores(
ForeignKeyClauseNotMatch_DeleteOrUpdate_Name(nm.del_or_update()));
ret += " ";
ret += EnumStrReplaceUnderscores(
ForeignKeyClauseNotMatch_Action_Name(nm.action()));
ret += " ";
return ret;
}
CONV_FN(ForeignKeyClauseCore, fkc) {
if (fkc.has_fkey_not_match())
return ForeignKeyClauseNotMatchToString(fkc.fkey_not_match());
return "MATCH PARTIAL"; // Sqlite does not actually parse MATCH clauses. This
// is assumed to be MATCH SIMPLE.
}
CONV_FN(DeferStrategy, ds) {
std::string ret;
if (ds.not_()) {
ret += "NOT ";
}
ret += "DEFERRABLE ";
ret +=
EnumStrReplaceUnderscores(DeferStrategy_DeferStratEnum_Name(ds.strat()));
return ret;
}
CONV_FN(ForeignKeyClause, fkey_clause) {
std::string ret("REFERENCES ");
ret += TableToString(fkey_clause.foreign_table());
if (fkey_clause.has_col_list()) {
ret += "(";
ret += ColumnListToString(fkey_clause.col_list());
ret += ")";
}
ret += " ";
for (int i = 0; i < fkey_clause.fkey_cores_size(); i++) {
ret += ForeignKeyClauseCoreToString(fkey_clause.fkey_cores(i));
ret += " ";
}
if (fkey_clause.has_defer_strat()) {
ret += DeferStrategyToString(fkey_clause.defer_strat());
ret += " ";
}
return ret;
}
CONV_FN(ConflictClause, conf) {
if (!conf.has_on_conflict())
return " ";
std::string ret("ON CONFLICT ");
ret += EnumStrReplaceUnderscores(
ConflictClause_OnConflict_Name(conf.on_conflict()));
ret += " ";
return ret;
}
CONV_FN(ColConstraintOpt1, opt1) {
std::string ret("PRIMARY KEY ");
ret += AscDescToString(opt1.asc_desc());
// space at the end already
ret += ConflictClauseToString(opt1.conflict());
if (opt1.autoincrement())
ret += "AUTOINCREMENT ";
return ret;
}
CONV_FN(ColConstraintOpt2, opt2) {
std::string ret("DEFAULT ");
if (opt2.has_expr()) {
ret += "(";
ret += ExprToString(opt2.expr());
ret += ")";
} else {
ret += LiteralValueToString(opt2.lit_val());
}
ret += " ";
return ret;
}
CONV_FN(ColumnConstraint, col_constr) {
std::string ret;
if (col_constr.has_constraint_name()) {
ret += "CONSTRAINT ";
ret += ColumnConstraintNameToString(col_constr.constraint_name());
ret += " ";
}
using ColConstrType = ColumnConstraint::ColConstraintOneofCase;
switch (col_constr.col_constraint_oneof_case()) {
case ColConstrType::kOpt1:
ret += ColConstraintOpt1ToString(col_constr.opt1());
break;
case ColConstrType::kNotNullConfClause:
ret += "NOT NULL ";
ret += ConflictClauseToString(col_constr.not_null_conf_clause());
break;
case ColConstrType::kUniqueConfClause:
ret += "UNIQUE ";
ret += ConflictClauseToString(col_constr.unique_conf_clause());
break;
case ColConstrType::kCheckExpr:
ret += "CHECK(";
ret += ExprToString(col_constr.check_expr());
ret += ") ";
break;
case ColConstrType::kOpt2:
ret += ColConstraintOpt2ToString(col_constr.opt2());
break;
case ColConstrType::kCollate:
ret += "COLLATE ";
ret += CollateTypeToString(col_constr.collate());
ret += " ";
break;
case ColConstrType::kFkeyClause:
ret += ForeignKeyClauseToString(col_constr.fkey_clause());
break;
default:
ret += ColConstraintOpt2ToString(col_constr.opt2_fallback());
}
return ret;
}
CONV_FN(TypeName, type_name) {
std::string ret;
ret += EnumStrReplaceUnderscores(
CastTypeName_CastTypeNameEnum_Name(type_name.ctn().type_enum()));
if (type_name.has_sn()) {
ret += "(";
ret += std::to_string(type_name.sn());
ret += ")";
}
ret += " ";
return ret;
}
CONV_FN(ColumnDef, col_def) {
std::string ret;
ret += ColumnToString(col_def.col());
ret += " ";
if (col_def.has_type_name()) {
ret += TypeNameToString(col_def.type_name());
ret += " ";
}
for (int i = 0; i < col_def.col_constraints_size(); i++) {
ret += ColumnConstraintToString(col_def.col_constraints(i));
ret += " ";
}
return ret;
}
CONV_FN(TableConstraintOpt1, opt1) {
std::string ret;
ret += EnumStrReplaceUnderscores(
TableConstraintOpt1_ConstraintType_Name(opt1.constraint_type()));
ret += "(";
ret += IndexedColumnListToString(opt1.indexed_col_list());
ret += ") ";
ret += ConflictClauseToString(opt1.conf_clause());
return ret;
}
CONV_FN(TableConstraintOpt2, opt2) {
std::string ret("FOREIGN KEY (");
ret += ColumnListToString(opt2.cols());
ret += ") ";
ret += ForeignKeyClauseToString(opt2.fkey_clause());
return ret;
}
CONV_FN(TableConstraint, t_constr) {
std::string ret;
if (t_constr.has_name()) {
ret += "CONSTRAINT ";
ret += TableConstraintNameToString(t_constr.name());
ret += " ";
}
if (t_constr.has_opt1()) {
ret += TableConstraintOpt1ToString(t_constr.opt1());
} else if (t_constr.has_check_expr()) {
ret += "CHECK(";
ret += ExprToString(t_constr.check_expr()); // TODO(mpdenton)
ret += ") ";
} else if (t_constr.has_opt2()) {
ret += TableConstraintOpt2ToString(t_constr.opt2());
} else {
// default to no constraint
ret += "CHECK(1)";
}
ret += " ";
return ret;
}
CONV_FN(CreateTableOpt1, opt1) {
std::string ret("(");
ret += ColumnDefToString(opt1.col_def());
for (int i = 0; i < opt1.extra_col_defs_size(); i++) {
ret += ", ";
ret += ColumnDefToString(opt1.extra_col_defs(i));
}
for (int i = 0; i < opt1.table_constraints_size(); i++) {
ret += ", ";
ret += TableConstraintToString(opt1.table_constraints(i));
}
ret += ") ";
if (opt1.without_rowid())
ret += "WITHOUT ROWID ";
return ret;
}
CONV_FN(CreateTable, create_table) {
RETURN_IF_DISABLED_QUERY(CreateTable);
#if defined(FUZZ_FTS3)
return ""; // Don't create normal tables in FTS3 fuzzing mode.
#endif
std::string ret("CREATE ");
if (create_table.has_temp_modifier()) {
ret += EnumStrReplaceUnderscores(
TempModifier_Name(create_table.temp_modifier()))
.erase(0, std::string("TM_").length());
ret += " ";
}
ret += "TABLE ";
if (create_table.if_not_exists())
ret += "IF NOT EXISTS ";
ret += ExprSchemaTableToString(create_table.schema_table());
ret += " ";
// TODO(mpdenton) need spaces at the end here???
using TableCreationType = CreateTable::CreateTableOneofCase;
switch (create_table.create_table_oneof_case()) {
case TableCreationType::kOp1:
ret += CreateTableOpt1ToString(create_table.op1());
break;
case TableCreationType::kAsSelectStmt:
ret += SelectToString(create_table.as_select_stmt());
break;
default:
ret += CreateTableOpt1ToString(create_table.op());
break;
}
return ret; // TODO(mpdenton)
}
// ~~~~For INSERT and SELECT~~~~
CONV_FN(CommonTableExpr, cte) {
std::string ret;
ret += TableToString(cte.table());
if (cte.columns_size() > 0) {
ret += "(";
ret += ColumnToString(cte.columns(0));
for (int i = 1; i < cte.columns_size(); i++) {
ret += ", ";
ret += ColumnToString(cte.columns(i));
}
ret += ")";
}
ret += " AS (";
ret += SelectToString(cte.select());
ret += ") ";
return ret;
}
CONV_FN(WithStatement, ws) {
std::string ret("WITH ");
if (ws.recursive())
ret += "RECURSIVE ";
ret += CommonTableExprToString(ws.table_expr());
ret += " ";
for (int i = 0; i < ws.extra_table_exprs_size(); i++) {
ret += CommonTableExprToString(ws.extra_table_exprs(i));
ret += " ";
}
ret += " ";
return ret;
}
// ~~~~INSERT~~~~
// WARNING no space at end
CONV_FN(ColumnComparison, cc) {
std::string ret;
ret += ExprSchemaTableColumnToString(cc.col());
ret += BinaryOperatorToString(cc.op());
#if defined(FUZZ_FTS3)
if (cc.op() == BINOP_MATCH && cc.has_fmt()) {
ret += FTS3MatchFormatToString(cc.fmt());
return ret;
}
#endif
ret += ExprToString(cc.expr());
return ret;
}
// WARNING no space at end
CONV_FN(ExprComparisonHighProbability, echp) {
if (echp.has_cc()) {
return ColumnComparisonToString(echp.cc());
} else if (echp.has_expr()) {
return ExprToString(echp.expr());
} else {
return "Col0 = 1"; // default
}
}
CONV_FN(WhereStatement, ws) {
return "WHERE " + ExprComparisonHighProbabilityToString(ws.expr()) + " ";
}
#ifndef SQLITE_OMIT_UPSERT
CONV_FN(UpsertClausePart1, uc1) {
std::string ret;
ret += "(";
ret += IndexedColumnListToString(uc1.icol_list());
ret += ") ";
if (uc1.has_where_stmt()) {
ret += WhereStatementToString(uc1.where_stmt());
ret += " ";
}
return ret;
}
#endif
// WARNING no space at end
CONV_FN(ColEqualsExpr, cee) {
std::string ret;
if (cee.has_col()) {
ret += ColumnToString(cee.col());
} else if (cee.has_col_list()) {
ret += ColumnListToString(cee.col_list());
} else {
ret += "Col0"; // default
}
ret += " = ";
ret += ExprToString(cee.expr());
return ret;
}
CONV_FN(UpsertClausePart2, uc2) {
std::string ret("SET ");
ret += ColEqualsExprToString(uc2.cee());
for (int i = 0; i < uc2.extra_cees_size(); i++) {
ret += ", ";
ret += ColEqualsExprToString(uc2.extra_cees(i));
}
if (uc2.has_where_stmt()) {
ret += " ";
ret += WhereStatementToString(uc2.where_stmt());
}
ret += " ";
return ret;
}
CONV_FN(UpsertClause, uc) {
#ifndef SQLITE_OMIT_UPSERT
std::string ret("ON CONFLICT ");
if (uc.has_uclause_p1()) {
ret += UpsertClausePart1ToString(uc.uclause_p1());
}
ret += "DO ";
if (uc.has_uclause_p2()) {
ret += "UPDATE ";
ret += UpsertClausePart2ToString(uc.uclause_p2());
} else {
ret += "NOTHING ";
}
return ret;
#else
return ""; // fine to return empty string here
#endif
}
CONV_FN(ValuesStatement, values) {
std::string ret("VALUES (");
ret += ExprListToString(values.expr_list());
ret += ")";
for (int i = 0; i < values.extra_expr_lists_size(); i++) {
ret += ", (";
ret += ExprListToString(values.extra_expr_lists(i));
ret += ")";
}
ret += " ";
return ret;
}
CONV_FN(Insert, insert) {
RETURN_IF_DISABLED_QUERY(Insert);
std::string ret;
if (insert.has_with()) {
ret += WithStatementToString(insert.with());
ret += " ";
}
ret +=
EnumStrReplaceUnderscores(Insert_InsertType_Name(insert.insert_type()));
ret += " INTO ";
ret += SchemaTableAsAliasToString(insert.staa());
if (insert.has_col_list()) {
ret += "(";
ret += ColumnListToString(insert.col_list());
ret += ") ";
}
// oneof
if (insert.has_values()) {
ret += ValuesStatementToString(insert.values());
ret += " ";
} else if (insert.has_select()) {
ret += SelectToString(insert.select());
ret += " ";
} else {
ret += "DEFAULT VALUES ";
}
if (insert.has_upsert_clause()) {
ret += UpsertClauseToString(insert.upsert_clause());
ret += " ";
}
return ret;
}
// ~~~~DELETE~~~~
CONV_FN(QualifiedTableName, qtn) {
std::string ret;
ret += SchemaTableAsAliasToString(qtn.staa());
ret += " ";
if (qtn.indexed()) {
if (qtn.not_indexed()) {
ret += "NOT INDEXED ";
} else {
ret += "INDEXED BY ";
ret += IndexToString(qtn.indexed_by());
ret += " ";
}
}
return ret;
}
CONV_FN(Delete, delete_) {
RETURN_IF_DISABLED_QUERY(Delete);
std::string ret;
if (delete_.has_with()) {
ret += WithStatementToString(delete_.with());
ret += " ";
}
ret += "DELETE FROM ";
ret += QualifiedTableNameToString(delete_.qtn());
if (delete_.has_where()) {
ret += WhereStatementToString(delete_.where());
}
ret += " ";
return ret;
}
// ~~~~UPDATE~~~~
// WARNING no space at end
CONV_FN(Update, update) {
RETURN_IF_DISABLED_QUERY(Update);
std::string ret;
if (update.has_with()) {
ret += WithStatementToString(update.with());
ret += " ";
}
ret += "UPDATE ";
if (update.has_update_type()) {
ret +=
EnumStrReplaceUnderscores(Update_UpdateType_Name(update.update_type()));
ret += " ";
}
ret += QualifiedTableNameToString(update.qtn());
ret += " ";
ret += UpsertClausePart2ToString(update.ucp2());
return ret;
}
// TODO(mpdenton) restrictions on UPDATEs in CREATE TRIGGER????
// ~~~~SELECT~~~~
CONV_FN(ExprColAlias, eca) {
std::string ret;
ret += ExprToString(eca.expr());
ret += " ";
if (eca.has_col_alias()) {
if (eca.as()) {
ret += "AS ";
}
ret += ColumnToString(eca.col_alias());
ret += " ";
}
return ret;
}
// WARNING no space at end
CONV_FN(ResultColumn, rc) {
std::string ret;
// oneof
if (rc.has_col()) {
return ColumnToString(rc.col());
} else if (rc.has_eca()) {
return ExprColAliasToString(rc.eca());
} else if (rc.has_table_star()) {
return TableToString(rc.table_star()) + ".*";
} else if (rc.has_fts3_fn()) {
#if defined(FUZZ_FTS3)
return FTS3AuxiliaryFnToString(rc.fts3_fn());
#else
return "*";
#endif
} else {
return "*";
}
}
CONV_FN(AsTableAlias, ata) {
std::string ret;
if (ata.as()) {
ret += "AS ";
}
ret += TableToString(ata.table_alias());
ret += " ";
return ret;
}
// WARNING no space at end
CONV_FN(JoinOperator, jo) {
if (jo.comma())
return ",";
std::string ret;
if (jo.natural())
ret += "NATURAL ";
if (jo.join_type() != JoinOperator::NONE) {
ret +=
EnumStrReplaceUnderscores(JoinOperator_JoinType_Name(jo.join_type()));
ret += " ";
}
ret += "JOIN ";
return ret;
}
CONV_FN(JoinConstraint, jc) {
// oneof
if (jc.has_on_expr()) {
return "ON " + ExprToString(jc.on_expr()) + " ";
} else if (jc.has_using_expr()) {
std::string ret("(");
ret += ColumnListToString(jc.using_expr().col_list());
ret += ") ";
return ret;
}
return " ";
}
CONV_FN(JoinClauseCore, jcc) {
std::string ret;
ret += JoinOperatorToString(jcc.join_op());
ret += " ";
ret += TableOrSubqueryToString(jcc.tos());
ret += " ";
ret += JoinConstraintToString(jcc.join_constraint());
ret += " ";
return ret;
}
CONV_FN(JoinClause, jc) {
std::string ret;
ret += TableOrSubqueryToString(jc.tos());
ret += " ";
for (int i = 0; i < jc.clauses_size(); i++) {
ret += JoinClauseCoreToString(jc.clauses(i));
}
ret += " ";
return ret;
}
// TODO(mpdenton) ExprIn needs it schematablefn!!!!!
CONV_FN(ExprSchemaTableFn, estf) {
std::string ret;
const TableFn& tfn = estf.table_fn();
// oneof for pragma fns
if (tfn.has_foreign_key_list()) {
ret += "pragma_foreign_key_list(\'";
ret += TableToString(tfn.foreign_key_list());
ret += "\') ";
} else if (tfn.has_index_info()) {
ret += "pragma_index_info(\'";
ret += IndexToString(tfn.index_info());
ret += "\') ";
} else if (tfn.has_index_list()) {
ret += "pragma_index_list(\'";
ret += TableToString(tfn.index_list());
ret += "\') ";
} else if (tfn.has_index_xinfo()) {
ret += "pragma_index_xinfo(\'";
ret += IndexToString(tfn.index_xinfo());
ret += "\') ";
} else if (tfn.has_integrity_check()) {
ret += "pragma_integrity_check(\'";
ret += std::to_string(tfn.integrity_check());
ret += "\') ";
} else if (tfn.has_optimize()) {
ret += "pragma_optimize(\'";
ret += std::to_string(tfn.optimize());
ret += "\') ";
} else if (tfn.has_quick_check()) {
ret += "pragma_quick_check(\'";
ret += std::to_string(tfn.quick_check());
ret += "\') ";
} else if (tfn.has_table_info()) {
ret += "pragma_table_info(\'";
ret += TableToString(tfn.table_info());
ret += "\') ";
} else if (tfn.has_table_xinfo()) {
ret += "pragma_table_xinfo(\'";
ret += TableToString(tfn.table_xinfo());
ret += "\') ";
} else {
ret += StrToLower(PragmaFnZeroArgOneResult_Name(tfn.no_arg_one_result()))
.erase(0, std::string("PFN_ZO_").length());
ret += "() ";
}
return ret;
}
CONV_FN(TableOrSubqueryOption2, toso2) {
std::string ret;
ret += ExprSchemaTableFnToString(toso2.schema_table_fn());
ret += " ";
if (toso2.has_as_table_alias()) {
ret += AsTableAliasToString(toso2.as_table_alias());
}
return ret;
}
CONV_FN(TableOrSubqueryOption3, tos3) {
std::string ret;
if (tos3.tos_list_size() > 0) {
ret += TableOrSubqueryToString(tos3.tos_list(0));
for (int i = 1; i < tos3.tos_list_size(); i++) {
ret += ", ";
ret += TableOrSubqueryToString(tos3.tos_list(i));
}
} else {
ret += JoinClauseToString(tos3.join_clause());
}
return ret;
}
CONV_FN(TableOrSubqueryOption4, tos4) {
std::string ret("(");
ret += SelectToString(tos4.select());
ret += ") ";
if (tos4.has_as_table_alias()) {
ret += AsTableAliasToString(tos4.as_table_alias());
ret += " ";
}
return ret;
}
CONV_FN(TableOrSubquery, tos) {
// oneof
if (tos.has_qtn()) {
return QualifiedTableNameToString(tos.qtn()) + " ";
} else if (tos.has_toso2()) {
return TableOrSubqueryOption2ToString(tos.toso2()) + " ";
} else if (tos.has_toso3()) {
return "(" + TableOrSubqueryOption3ToString(tos.toso3()) + ") ";
} else if (tos.has_toso4()) {
return TableOrSubqueryOption4ToString(tos.toso4()) + " ";
} else {
return ExprSchemaTableToString(tos.schema_table_expr()) + " ";
}
}
CONV_FN(FromStatement, fs) {
return "FROM " + TableOrSubqueryOption3ToString(fs.tos3());
}
CONV_FN(GroupByStatement, gbs) {
std::string ret("GROUP BY ");
ret += ExprListToString(gbs.exprs());
ret += " ";
if (gbs.has_having_expr()) {
ret += "HAVING ";
ret += ExprToString(gbs.having_expr());
ret += " ";
}
return ret;
}
CONV_FN(WindowStatement, ws) {
#if !defined(SQLITE_OMIT_WINDOWFUNC)
return "";
#else
return "";
#endif
}
CONV_FN(SelectStatementCore, ssc) {
std::string ret;
ret += EnumStrReplaceUnderscores(
SelectStatementCore_SelectOrDistinct_Name(ssc.s_or_d()));
ret += " ";
if (ssc.result_columns_size() == 0) {
ret += "* ";
} else {
ret += ResultColumnToString(ssc.result_columns(0));
for (int i = 1; i < ssc.result_columns_size(); i++) {
ret += ", ";
ret += ResultColumnToString(ssc.result_columns(i));
}
ret += " ";
}
if (ssc.has_from()) {
ret += FromStatementToString(ssc.from());
ret += " ";
}
if (ssc.has_where()) {
ret += WhereStatementToString(ssc.where());
ret += " ";
}
if (ssc.has_groupby()) {
ret += GroupByStatementToString(ssc.groupby());
ret += " ";
}
if (ssc.has_window()) {
ret += WindowStatementToString(ssc.window());
ret += " ";
}
return ret;
}
CONV_FN(SelectSubStatement, sss) {
// oneof
if (sss.has_select_core()) {
return SelectStatementCoreToString(sss.select_core());
} else if (sss.has_values()) {
return ValuesStatementToString(sss.values());
} else {
return ValuesStatementToString(sss.values_fallback());
}
}
CONV_FN(ExprOrderingTerm, eot) {
std::string ret = ExprToString(eot.expr());
ret += " ";
if (eot.has_collate_type()) {
ret += "COLLATE ";
ret += CollateTypeToString(eot.collate_type());
ret += " ";
}
ret += AscDescToString(eot.asc_desc());
return ret;
}
CONV_FN(OrderByStatement, obs) {
std::string ret("ORDER BY ");
ret += ExprOrderingTermToString(obs.ord_term());
for (int i = 0; i < obs.extra_ord_terms_size(); i++) {
ret += ", ";
ret += ExprOrderingTermToString(obs.extra_ord_terms(i));
}
ret += " ";
return ret;
}
CONV_FN(LimitStatement, ls) {
std::string ret("LIMIT ");
ret += ExprToString(ls.limit_expr());
ret += " ";
if (ls.has_second_expr()) {
if (ls.offset()) {
ret += "OFFSET ";
} else {
ret += ", ";
}
ret += ExprToString(ls.second_expr());
ret += " ";
}
return ret;
}
CONV_FN(ExtraSelectSubStatement, esss) {
std::string ret, enum1;
enum1 = CompoundOperator_Name(esss.compound_op());
// erase prefix
enum1.erase(0, std::string("CO_").length());
ret += EnumStrReplaceUnderscores(enum1);
ret += " ";
ret += SelectSubStatementToString(esss.select_substatement());
return ret;
}
CONV_FN(Select, select) {
RETURN_IF_DISABLED_QUERY(Select);
std::string ret;
if (select.has_with()) {
ret += WithStatementToString(select.with());
ret += " ";
}
ret += SelectStatementCoreToString(select.select_core());
for (int i = 0; i < select.extra_select_substatements_size(); i++) {
ret +=
ExtraSelectSubStatementToString(select.extra_select_substatements(i));
ret += " ";
}
if (select.has_orderby()) {
ret += OrderByStatementToString(select.orderby());
ret += " ";
}
if (select.has_limit()) {
ret += LimitStatementToString(select.limit());
}
return ret;
}
// ~~~~FTS3~~~~
// CORPUS currently relying on normal SELECTs to generate good compound
// queries for FTS, like AND, OR, and NOT. Generate a corpus entry with a lot of
// creative FTS queries.
CONV_FN(FTS3Table, ft) {
// std::string ret("FTS3Table");
std::string ret(
"Table"); // for now, use the same naming scheme as normal tables.
ret += std::to_string(ft.table() % kMaxFTS3TableNumber);
return ret;
}
CONV_FN(FTS3MatchToken, fmt) {
std::string ret;
if (fmt.has_col()) {
ret += ColumnToString(fmt.col());
ret += ":";
}
if (fmt.negate()) {
ret += "-";
}
if (fmt.token().length() == 0)
ret += "a";
else
ret += ConvertToSqlString(
fmt.token()); // TODO(mpdenton) good enough? Need something better????
if (fmt.prefix())
ret += "*";
return ret;
}
CONV_FN(FTS3PhraseQuery, fpq) {
std::string ret("\"");
ret += FTS3MatchTokenToString(fpq.mt());
for (int i = 0; i < fpq.extra_mts_size(); i++) {
ret += " ";
ret += FTS3MatchTokenToString(fpq.extra_mts(i));
}
ret += "\"";
return ret;
}
CONV_FN(FTS3MatchFormatCore, fmfc) {
// oneof
if (fmfc.has_pq()) {
return FTS3PhraseQueryToString(fmfc.pq());
} else if (fmfc.has_nq()) {
return FTS3NearQueryToString(fmfc.nq());
} else {
return FTS3MatchTokenToString(fmfc.mt_fallback());
}
}
CONV_FN(FTS3NearQuery, fnq) {
std::string ret = FTS3MatchFormatCoreToString(fnq.format_core1());
ret += " NEAR";
if (fnq.has_num_tokens_near()) {
ret += "/";
ret += std::to_string(fnq.num_tokens_near());
}
ret += " ";
ret += FTS3MatchFormatCoreToString(fnq.format_core2());
return ret;
}
CONV_FN(FTS3CompoundAndCore, fcac) {
std::string ret(" ");
ret += FTS3CompoundAndCore_CompoundOp_Name(fcac.op());
ret += " ";
ret += FTS3MatchFormatCoreToString(fcac.core());
return ret;
}
CONV_FN(FTS3MatchCompoundFormat, fmcf) {
std::string ret = FTS3MatchFormatCoreToString(fmcf.core());
for (int i = 0; i < fmcf.compound_and_cores_size(); i++) {
ret += FTS3CompoundAndCoreToString(fmcf.compound_and_cores(i));
}
return ret;
}
CONV_FN(FTS3MatchFormat, fmf) {
std::string ret("\'");
if (fmf.ft_size() > 0) {
ret += FTS3MatchCompoundFormatToString(fmf.ft(0));
}
for (int i = 1; i < fmf.ft_size(); i++) {
ret += " ";
ret += FTS3MatchCompoundFormatToString(fmf.ft(i));
}
ret += "\'";
return ret;
}
CONV_FN(FTS3SpecialCommand, fsc) {
RETURN_IF_DISABLED_QUERY(FTS3SpecialCommand);
std::string ret("INSERT INTO ");
ret += FTS3TableToString(fsc.table());
ret += "(";
ret += FTS3TableToString(fsc.table());
ret += ") VALUES(\'";
switch (fsc.command()) {
case FTS3SpecialCommand::OPTIMIZE:
ret += "optimize";
break;
case FTS3SpecialCommand::REBUILD:
ret += "rebuild";
break;
case FTS3SpecialCommand::INTEGRITY_CHECK:
ret += "integrity-check";
break;
case FTS3SpecialCommand::MERGE:
ret += "merge=";
ret += std::to_string(fsc.val1());
ret += ",";
ret += std::to_string(fsc.val2());
break;
case FTS3SpecialCommand::AUTOMERGE:
ret += "automerge=";
ret += std::to_string(fsc.val1() % 16);
break;
}
ret += "\')";
return ret;
}
// WARNING no space at end
CONV_FN(FTS3SelectMatch, fsm) {
RETURN_IF_DISABLED_QUERY(FTS3SelectMatch);
std::string ret("SELECT * FROM ");
ret += FTS3TableToString(fsm.table());
ret += " WHERE ";
ret += ColumnToString(fsm.col());
ret += " MATCH ";
ret += FTS3MatchFormatToString(fsm.match_pattern());
return ret;
}
CONV_FN(FTS3SpecificQuery, fsq) {
RETURN_IF_DISABLED_QUERY(FTS3SpecificQuery);
#if defined(FUZZ_FTS3)
// oneof
if (fsq.has_command()) {
return FTS3SpecialCommandToString(fsq.command());
} else if (fsq.has_select()) {
return FTS3SelectMatchToString(fsq.select());
} else {
return "";
}
#else
return "";
#endif
}
CONV_FN(ICULocale, il) {
std::string ret;
std::string lc = IsoLangCode_Name(il.iso_lang_code());
lc.erase(0, std::string("ISO_LANG_CODE_").length());
ret += lc;
ret += "_";
// extract country code from integer
ret += (char)((il.country_code() & 0xFF) % 26) + 'A';
ret += (char)(((il.country_code() & 0xFF00) >> 8) % 26) + 'A';
return ret;
}
CONV_FN(CreateFTS3Table, cft) {
RETURN_IF_DISABLED_QUERY(CreateFTS3Table);
std::string ret("CREATE VIRTUAL TABLE ");
if (cft.if_not_exists())
ret += "IF NOT EXISTS ";
if (cft.has_schema()) {
ret += SchemaToString(cft.schema());
ret += ".";
}
ret += FTS3TableToString(cft.table());
ret += " USING fts3(";
// TODO(mpdenton) not using schema here, should I???
if (cft.has_col_list()) {
ret += ColumnListToString(cft.col_list());
if (cft.has_tokenizer_type())
ret += ", ";
}
if (cft.has_tokenizer_type()) {
ret += "tokenize=";
std::string tt = TokenizerType_Name(cft.tokenizer_type());
tt.erase(0, std::string("TT_").length());
tt = StrToLower(tt);
#if defined(SQLITE_DISABLE_FTS3_UNICODE)
if (tt == "unicode61")
tt = "porter";
#endif
ret += tt;
// now generate locales for ICU
if (cft.tokenizer_type() == TokenizerType::TT_ICU) {
ret += " ";
ret += ICULocaleToString(cft.locale());
} else if (cft.tokenizer_type() == TokenizerType::TT_UNICODE61) {
// Chrome does not actually enable this option. FIXME in the future.
}
}
ret += ")";
return ret;
}
// WARNING no space at end
CONV_FN(FTS3OffsetsFn, fof) {
return "offsets(" + FTS3TableToString(fof.table()) + ")";
}
// WARNING no space at end
CONV_FN(FTS3SnippetsFn, fsf) {
std::string ret("snippets(");
ret += FTS3TableToString(fsf.table());
// Now (possibly) emit the five optional arguments.
int num_args = (int)fsf.num_optional_args();
if (num_args >= 1) {
ret += ", \'";
ret += ConvertToSqlString(fsf.start_match());
ret += "\'";
}
if (num_args >= 2) {
ret += ", \'";
ret += ConvertToSqlString(fsf.end_match());
ret += "\'";
}
if (num_args >= 3) {
ret += ", \'";
ret += ConvertToSqlString(fsf.ellipses());
ret += "\'";
}
if (num_args >= 4) {
ret += ", ";
if (fsf.has_col_number()) {
ret += std::to_string(fsf.col_number() % kMaxColumnNumber);
} else {
ret += "-1";
}
}
if (num_args >= 5) {
ret += ", ";
ret +=
std::to_string((fsf.num_tokens() % 129) - 64); // clamp into [-64, 64]
}
ret += ")";
return ret;
}
// WARNING no space at end
CONV_FN(FTS3MatchInfoFn, fmi) {
constexpr static char matchinfo_chars[] = {
'p', 'c', 's', 'x', 'y', 'b',
// 'n', 'a', 'l', // These characters only available for FTS4.
};
std::string ret("matchinfo(");
ret += FTS3TableToString(fmi.table());
if (fmi.chars_size() > 0) {
ret += ", \'";
for (int i = 0; i < fmi.chars_size(); i++) {
ret += matchinfo_chars[fmi.chars(i) % sizeof(matchinfo_chars)];
}
ret += "\'";
}
ret += ")";
return ret;
}
// WARNING no space at end
CONV_FN(FTS3AuxiliaryFn, faf) {
if (faf.has_snippets()) {
return FTS3SnippetsFnToString(faf.snippets());
} else if (faf.has_matchinfo()) {
return FTS3MatchInfoFnToString(faf.matchinfo());
} else {
return FTS3OffsetsFnToString(faf.offsets_fallback());
}
}
CONV_FN(FTS3HiddenTable, fht) {
std::string tab = FTS3HiddenTable_HiddenTableVal_Name(fht.htv());
tab = StrToLower(tab);
return FTS3TableToString(fht.table()) + "_" + tab;
}
CONV_FN(FTS3HiddenTableColumn, fhtc) {
std::string tab = FTS3HiddenTableColumn_Name(fhtc);
tab = tab.erase(0, std::string("FTS3_HT_").length());
tab = StrToLower(tab);
return tab;
}
CONV_FN(FTS3HiddenTableInsert, fi) {
RETURN_IF_DISABLED_QUERY(FTS3HiddenTableInsert);
std::string ret("INSERT INTO ");
ret += FTS3HiddenTableToString(fi.fht());
if (fi.col_vals_size() == 0) {
ret += " DEFAULT VALUES";
return ret;
}
ret += "(";
ret += FTS3HiddenTableColumnToString(fi.col_vals(0).col());
for (int i = 1; i < fi.col_vals_size(); i++) {
ret += ", ";
ret += FTS3HiddenTableColumnToString(fi.col_vals(i).col());
}
ret += ") VALUES(";
ret += ExprToString(fi.col_vals(0).expr());
for (int i = 0; i < fi.col_vals_size(); i++) {
ret += ", ";
ret += ExprToString(fi.col_vals(i).expr());
}
ret += ")";
return ret;
}
CONV_FN(FTS3HiddenTableUpdate, fu) {
RETURN_IF_DISABLED_QUERY(FTS3HiddenTableUpdate);
std::string ret("UPDATE ");
ret += FTS3HiddenTableToString(fu.fht());
ret += " ";
if (fu.col_vals_size() == 0) {
ret += "start_block = 0";
return ret;
}
ret += "SET ";
ret += FTS3HiddenTableColumnToString(fu.col_vals(0).col());
ret += " = ";
ret += ExprToString(fu.col_vals(0).expr());
for (int i = 1; i < fu.col_vals_size(); i++) {
ret += ", ";
ret += FTS3HiddenTableColumnToString(fu.col_vals(i).col());
ret += " = ";
ret += ExprToString(fu.col_vals(i).expr());
}
if (fu.has_col_where()) {
ret += " WHERE ";
ret += FTS3HiddenTableColumnToString(fu.col_where());
ret += BinaryOperatorToString(fu.bin_op());
ret += ExprToString(fu.comp_expr());
}
return ret;
}
CONV_FN(FTS3HiddenTableDelete, fd) {
RETURN_IF_DISABLED_QUERY(FTS3HiddenTableDelete);
std::string ret("DELETE FROM ");
ret += FTS3HiddenTableToString(fd.fht());
if (fd.has_col_where()) {
ret += " WHERE ";
ret += FTS3HiddenTableColumnToString(fd.col_where());
ret += BinaryOperatorToString(fd.bin_op());
ret += ExprToString(fd.comp_expr());
}
return ret;
}
// ~~~~TRANSACTIONS/SAVEPOINTS
CONV_FN(BeginTransaction, bt) {
RETURN_IF_DISABLED_QUERY(BeginTransaction);
std::string ret("BEGIN ");
if (bt.has_type()) {
ret += BeginTransaction_TransactionType_Name(bt.type());
ret += " ";
}
ret += "TRANSACTION";
return ret;
}
CONV_FN(CommitTransaction, ct) {
RETURN_IF_DISABLED_QUERY(CommitTransaction);
return EnumStrReplaceUnderscores(
CommitTransaction_CommitText_Name(ct.text()));
}
CONV_FN(RollbackStatement, rt) {
RETURN_IF_DISABLED_QUERY(RollbackStatement);
#if !defined(FUZZ_OMIT_SAVEPOINT)
if (rt.has_save_point()) {
return "ROLLBACK TO SAVEPOINT " + SavePointToString(rt.save_point());
}
#endif
return "ROLLBACK TRANSACTION";
}
#if !defined(FUZZ_OMIT_SAVEPOINT)
CONV_FN(CreateSavePoint, csp) {
RETURN_IF_DISABLED_QUERY(CreateSavePoint);
return "SAVEPOINT " + SavePointToString(csp.save_point());
}
CONV_FN(ReleaseSavePoint, rsp) {
RETURN_IF_DISABLED_QUERY(ReleaseSavePoint);
return "RELEASE SAVEPOINT " + SavePointToString(rsp.save_point());
}
#endif
CONV_FN(Analyze, a) {
RETURN_IF_DISABLED_QUERY(Analyze);
std::string ret("ANALYZE");
if (a.has_schema_name()) {
ret += " ";
ret += SchemaToString(a.schema_name());
if (a.has_table_name()) {
ret += ".";
ret += TableToString(a.table_name());
} else if (a.has_index_name()) {
ret += ".";
ret += IndexToString(a.index_name());
}
} else if (a.has_table_name()) {
ret += " ";
ret += TableToString(a.table_name());
} else if (a.has_index_name()) {
ret += " ";
ret += IndexToString(a.index_name());
}
return ret;
}
// ~~~~VACUUM~~~~
CONV_FN(Vacuum, v) {
RETURN_IF_DISABLED_QUERY(Vacuum);
std::string ret("VACUUM");
if (v.has_schema()) {
ret += " ";
ret += SchemaToString(v.schema());
}
return ret;
}
// ~~~~PRAGMA~~~~
CONV_FN(Pragma, p) {
RETURN_IF_DISABLED_QUERY(Pragma);
#if defined(FUZZ_OMIT_PRAGMA)
return "";
#else
constexpr static const char* locking_modes[] = {"NORMAL", "EXCLUSIVE"};
constexpr static const char* journal_modes[] = {
"DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"};
Table table;
std::string ret("PRAGMA ");
if (p.has_schema()) {
ret += SchemaToString(p.schema());
ret += ".";
}
ret += StripTrailingUnderscores(
StrToLower(Pragma_PragmaCommand_Name(p.command())));
switch (p.command()) {
case Pragma::AUTO_VACUUM:
ret += " = ";
ret += std::to_string((uint32_t)p.arg1() % 3);
break;
case Pragma::WRITEABLE_SCHEMA:
ret += " = ";
ret += std::to_string((uint32_t)p.arg1() % 2);
break;
case Pragma::LOCKING_MODE:
ret += " = ";
ret += locking_modes[(uint32_t)p.arg1() % 2];
break;
case Pragma::TEMP_STORE:
ret += " = ";
ret += std::to_string((uint32_t)p.arg1() % 3);
break;
case Pragma::PAGE_SIZE_:
ret += " = ";
ret += std::to_string(p.arg1());
break;
case Pragma::TABLE_INFO:
ret += "(\'";
table.set_table((uint32_t)p.arg1());
ret += TableToString(table);
ret += "\')";
break;
case Pragma::JOURNAL_MODE:
ret += " = ";
ret += journal_modes[(uint32_t)p.arg1() % 6];
break;
case Pragma::MMAP_SIZE:
ret += " = ";
ret += std::to_string(p.arg1());
break;
}
return ret;
#endif
}
// ~~~~CREATE INDEX~~~~
CONV_FN(CreateIndex, ci) {
RETURN_IF_DISABLED_QUERY(CreateIndex);
std::string ret("CREATE ");
if (ci.unique())
ret += "UNIQUE ";
ret += "INDEX ";
if (ci.if_not_exists())
ret += "IF NOT EXISTS ";
if (ci.has_schema()) {
ret += SchemaToString(ci.schema());
ret += ".";
}
ret += IndexToString(ci.index());
ret += " ON ";
ret += TableToString(ci.table());
ret += "(";
ret += IndexedColumnListToString(ci.icol_list());
ret += ")";
if (ci.has_where()) {
ret += " ";
ret += WhereStatementToString(ci.where());
}
return ret;
}
// ~~~~CREATE VIEW~~~~
CONV_FN(CreateView, cv) {
RETURN_IF_DISABLED_QUERY(CreateView);
std::string ret("CREATE ");
if (cv.has_temp_modifier()) {
ret += EnumStrReplaceUnderscores(TempModifier_Name(cv.temp_modifier()))
.erase(0, std::string("TM_").length());
ret += " ";
}
ret += "VIEW ";
if (cv.if_not_exists())
ret += "IF NOT EXISTS ";
if (cv.has_schema()) {
ret += SchemaToString(cv.schema());
ret += ".";
}
ret += ViewToString(cv.view());
ret += " ";
if (cv.has_col_list()) {
ret += "(";
ret += ColumnListToString(cv.col_list());
ret += ") ";
}
ret += SelectToString(cv.select());
return ret;
}
// ~~~~CREATE TRIGGER~~~~
CONV_FN(TypicalQuery, tq) {
// oneof
if (tq.has_update())
return UpdateToString(tq.update());
else if (tq.has_insert())
return InsertToString(tq.insert());
else if (tq.has_select())
return SelectToString(tq.select());
else
return DeleteToString(tq.delete_fallback());
}
// WARNING no space at end
CONV_FN(CreateTrigger, ct) {
RETURN_IF_DISABLED_QUERY(CreateTrigger);
std::string ret("CREATE ");
if (ct.has_temp_modifier()) {
ret += EnumStrReplaceUnderscores(TempModifier_Name(ct.temp_modifier()))
.erase(0, std::string("TM_").length());
ret += " ";
}
ret += "TRIGGER ";
if (ct.if_not_exists())
ret += "IF NOT EXISTS ";
if (ct.has_schema()) {
ret += SchemaToString(ct.schema());
ret += " ";
}
ret += TriggerToString(ct.trigger());
ret += " ";
if (ct.has_trigger_type()) {
ret += EnumStrReplaceUnderscores(
CreateTrigger_TriggerType_Name(ct.trigger_type()));
ret += " ";
}
ret += CreateTrigger_TriggerInstr_Name(ct.trigger_instr());
ret += " ";
if (ct.trigger_instr() == CreateTrigger::UPDATE) {
ret += "OF ";
ret += ColumnListToString(ct.col_list());
ret += " ";
}
ret += "ON ";
ret += TableToString(ct.table());
ret += " ";
if (ct.for_each_row())
ret += "FOR EACH ROW ";
if (ct.has_when()) {
ret += "WHEN ";
ret += ExprComparisonHighProbabilityToString(ct.when());
ret += " ";
}
ret += "BEGIN ";
ret += TypicalQueryToString(ct.tq());
ret += "; ";
for (int i = 0; i < ct.extra_tqs_size(); i++) {
ret += TypicalQueryToString(ct.extra_tqs(i));
ret += "; ";
}
ret += "END";
return ret;
}
// ~~~~REINDEX~~~~
CONV_FN(ReIndex, ri) {
RETURN_IF_DISABLED_QUERY(ReIndex);
// Chrome doesn't use REINDEX
#if !defined(SQLITE_OMIT_REINDEX)
if (ri.empty())
return "REINDEX";
std::string ret("REINDEX ");
if (ri.has_collate_type()) {
ret += CollateTypeToString(ri.collate_type());
return ret;
}
if (ri.has_schema()) {
ret += SchemaToString(ri.schema());
ret += ".";
}
if (ri.has_table())
ret += TableToString(ri.table());
else
ret += IndexToString(ri.index());
return ret;
#else
return "";
#endif
}
CONV_FN(Drop, d) {
RETURN_IF_DISABLED_QUERY(Drop);
std::string ret("DROP ");
std::string if_exists("");
std::string schema("");
if (d.if_exists())
if_exists = "IF EXISTS ";
if (d.has_schema()) {
schema = SchemaToString(d.schema());
schema += " ";
}
// oneof
if (d.has_index()) {
ret += "INDEX ";
ret += if_exists;
ret += schema;
ret += IndexToString(d.index());
} else if (d.has_table()) {
ret += "TABLE ";
ret += if_exists;
ret += schema;
ret += TableToString(d.table());
} else if (d.has_trigger()) {
ret += "TRIGGER ";
ret += if_exists;
ret += schema;
ret += TriggerToString(d.trigger());
} else {
ret += "VIEW ";
ret += if_exists;
ret += schema;
ret += ViewToString(d.view_fallback());
}
return ret;
}
// ~~~~ALTER TABLE~~~~
CONV_FN(AlterTable, at) {
RETURN_IF_DISABLED_QUERY(AlterTable);
std::string ret("ALTER TABLE ");
ret += ExprSchemaTableToString(at.schema_table());
ret += " ";
if (at.has_col()) {
ret += "RENAME ";
if (at.column())
ret += "COLUMN ";
ret += ColumnToString(at.col());
ret += " TO ";
ret += ColumnToString(at.col_to());
} else if (at.has_col_def()) {
ret += "ADD ";
if (at.column())
ret += "COLUMN ";
ret += ColumnDefToString(at.col_def());
} else {
ret += "RENAME TO ";
ret += TableToString(at.table_fallback());
}
return ret;
}
// ~~~~ATTACH DATABASE~~~~
CONV_FN(AttachDatabase, ad) {
RETURN_IF_DISABLED_QUERY(AttachDatabase);
std::string ret("ATTACH DATABASE \'");
if (ad.in_memory()) {
if (ad.file_uri()) {
ret += "file:";
std::string add;
if (ad.has_db_name()) {
ret += SchemaToString(ad.db_name());
ret += "?mode=memory";
add = "&";
} else {
ret += ":memory:";
add = "?";
}
if (ad.shared_cache()) {
ret += add;
ret += "cache=shared";
}
}
}
ret += "\' AS ";
ret += SchemaToString(ad.schema());
return ret;
}
// ~~~~DETACH DATABASE~~~~
CONV_FN(DetachDatabase, dd) {
RETURN_IF_DISABLED_QUERY(DetachDatabase);
std::string ret("DETACH DATABASE ");
ret += SchemaToString(dd.schema());
return ret;
}
// ~~~~Time and date fns~~~~
CONV_FN(HoursStuff, hs) {
std::string ret;
if (hs.has_hh()) {
ret += std::to_string(hs.hh() % 100);
if (hs.has_mm()) {
ret += ":";
ret += std::to_string(hs.mm() % 100);
if (hs.has_ss()) {
ret += ":";
ret += std::to_string(hs.ss() % 100);
if (hs.has_sss()) {
ret += ".";
ret += std::to_string(hs.sss() % 1000);
}
}
}
}
return ret;
}
CONV_FN(TimeString, ts) {
std::string ret;
if (ts.has_yyyy()) {
// FIXME in the future add zeroes for integers < 1000.
ret += std::to_string(ts.yyyy() % 10000);
ret += "-";
ret += std::to_string(ts.mm() % 100);
ret += "-";
ret += std::to_string(ts.dd() % 100);
if (ts.extra_t())
ret += "T";
if (ts.has_hs())
ret += HoursStuffToString(ts.hs());
} else if (ts.has_hs()) {
ret += HoursStuffToString(ts.hs());
} else if (ts.has_dddddddddd()) {
ret += std::to_string(ts.dddddddddd() % 10000000000);
} else if (ts.now()) {
ret += "now";
} else {
ret += ConvertToSqlString(ts.random_bytes());
}
if (ts.has_tz_plus()) {
if (ts.z()) {
ret += "Z";
} else {
if (ts.plus())
ret += "+";
else
ret += "-";
ret += std::to_string(ts.tz_hh() % 100);
ret += std::to_string(ts.tz_mm() % 100);
}
}
return ret;
}
CONV_FN(TimeModifier, tm) {
std::string ret;
if (tm.has_nm()) {
ret += std::to_string(tm.num());
ret += " ";
if (tm.has_dot_num()) {
ret += ".";
ret += std::to_string(tm.dot_num());
}
ret += StrToLower(TimeModifier_NumberedModifiers_Name(tm.nm()));
} else {
ret += StrToLower(
EnumStrReplaceUnderscores(TimeModifier_OtherModifiers_Name(tm.om())));
}
if (tm.om() == TimeModifier::WEEKDAY) {
ret += " ";
ret += std::to_string(tm.num());
}
return ret;
}
CONV_FN(SimpleDateAndTimeFn, sfn) {
std::string ret;
ret += StrToLower(SimpleDateAndTimeFn_FnName_Name(sfn.fn_name()));
ret += "(\'";
ret += TimeStringToString(sfn.time_string());
ret += "\'";
for (int i = 0; i < sfn.modifiers_size(); i++) {
ret += ", \'";
ret += TimeModifierToString(sfn.modifiers(i));
ret += "\'";
}
ret += ") ";
return ret;
}
CONV_FN(StrftimeFormat, sf) {
std::string ret;
if (sf.has_subs()) {
std::string subs = StrftimeFormat_Substitution_Name(sf.subs());
if