Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

condition for quoting SQL NULL #251

Open
pedro-vicente opened this issue Jul 24, 2022 · 2 comments
Open

condition for quoting SQL NULL #251

pedro-vicente opened this issue Jul 24, 2022 · 2 comments

Comments

@pedro-vicente
Copy link

pedro-vicente commented Jul 24, 2022

In SQL I can INSERT a row in a database with a VARCHAR SQL type column named 'col1' with

std::string my_template = "INSERT INTO [my_table] (col1) VALUES ('my_value');"

To note that the SQL VARCHAR type requires to single quote

'my_value'

To insert a SQL NULL value, the single quotes are not specified

"INSERT INTO [my_table] (col1) VALUES (NULL);"

I have a template where the value is single quoted, and it allows to insert values like 'my_value'

std::string insert_template =
"INSERT INTO [my_table] (col1) VALUES ( '{{my_value}}' )";
nlohmann::json js;
to_json(js, my structure to convert to JSON);
std::string sql = inja::render(insert_template, js);

calling the template with a

std::string my_null = "NULL";

is incorrect because the string is single quoted resulting in a SQL insertion
of 'NULL' as a 4 character string and not an SQL NULL value

question

how can I make a condition to detect if the argument
'{{my_value}}' )

should be single quoted or not in the case the value is "NULL" ?

defining the template as (not single quoted)

std::string insert_template =
"INSERT INTO [my_table] (col1) VALUES ( {{my_value}} )";

would work for a NULL value but not a string

so, I would want something like (in pseudo INJA syntax)

std::string insert_template =
"INSERT INTO [my_table] (col1) VALUES ( 
{% if my_value == NULL %}
{{my_value}} 
{% else %}
'{{my_value}}'
{% endif %}
 )";

Is this possible to achieve somehow?

@pedro-vicente
Copy link
Author

this construct in the template chooses the quoted version or not depending on the value being "NULL"

nlohmann::json json4;
  json4["name"] = "NULL";
  std::string template5 = "INSERT INTO [my_table] (col1) VALUES ( \
  {%if name == \"NULL\"%}\
  {{name}}\
  {%else%}\
  '{{name}}'\
  {%endif%}\
  )";

  std::string rendered5 = inja::render(template5, json4);
  std::cout << rendered5 << std::endl;

rendered string is

INSERT INTO [my_table] (col1) VALUES (     NULL    )

@kubo
Copy link
Contributor

kubo commented Aug 3, 2022

I would add the following function sql_quote to escape single quotation marks in string values. Otherwise it causes SQL injection.

As for SQL NULL:
json string "NULL" -> 'NULL'
json null -> null

  inja::Environment env;
  env.add_callback("sql_quote", 1, [](inja::Arguments& args) {
    using json = nlohmann::json;
    auto arg = args.at(0);
    switch (arg->type()) {
    case json::value_t::null:
      return json("null");
    case json::value_t::boolean:
    case json::value_t::number_integer:
    case json::value_t::number_unsigned:
    case json::value_t::number_float:
      break;
    case json::value_t::string: {
      const auto& s = arg->get_ref<const json::string_t&>();
      // uncomment the next line if string "NULL" must be SQL null.
      // if (s == "NULL") return json("null");
      json::string_t quoted;
      quoted.push_back('\'');
      for (char c : s) {
        if (c == '\'') {
          // escape a single quotation mark
          quoted.push_back('\'');
        }
        quoted.push_back(c);
      }
      quoted.push_back('\'');
      return json(quoted);
    }
    case json::value_t::object:
    case json::value_t::array:
    case json::value_t::binary:
    case json::value_t::discarded:
      throw std::runtime_error("cannot convert " + std::string(arg->type_name()) + " to sql literal");
    }
    return *arg;
  });

  json j;
  j["name"] = nullptr;
  std::string tmpl = "INSERT INTO [my_table] (col1) VALUES ({{ sql_quote(name) }})";
  auto rendered = env.render(tmpl, j);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants