-2

I'm trying to create a Scala code that is similar to python where I read a text file such as:

test.sql

 select * from {name}

and insert name variable from the main program itself.

I'm new to Scala but I was able to read the file as such:

val filename = "test.sql"
val src_file = scala.io.Source.fromFile(filename)
val sql_str = try src_file.getLines mkString "\n" finally src_file.close()

Now I'm trying to do something like I would do in Python but in Scala:

sql_str.format(name = "table1")

Is there an equivalent of this in Scala.

codeBarer
  • 2,238
  • 7
  • 44
  • 75
  • I don't know of any `format` similar to Python, but can you do something like: `val sql_str = (mytable: String) => s"select * from ${mytable}" ; println(sql_str("MySqlTable")) // select * from MySqlTable` – Alin Gabriel Arhip Jul 16 '22 at 02:07
  • 1
    I'm not aware of (far from meaning it doesn't exist) something that would do something similar to python string interpolation (in your example). The main thing is that python `.format` doesn't depend on order (can be any). Otherwise you _could_ substitute "by order" using a C-style format string: https://stackoverflow.com/questions/6431933/how-to-format-strings-in-java – Jared Jul 16 '22 at 02:46

1 Answers1

3

I strongly advise against using a string interpolation / replacement to build SQL queries, as it's an easy way to leave your program vulnerable to SQL Injection (regardless of what programming language you're using). If interacting with SQL is your goal, I'd recommend looking into a database helper library like Doobie or Slick.

That disclaimer out of the way, there are a few approaches to string interpolation in Scala.

Normally, string interpolation is done with a string literal in your code, with $... or ${...} used to interpolate expressions into your string (the {} are needed if your expression is more than just a name reference). For example

val name: String = /* ... */
val message = s"Hello, $name"

// bad idea if `table` might come from user input
def makeQuery(table: String) = s"select * from $table"

But this doesn't work for string templates that you load from a file; it only works for string literals that are defined in your code. If you can change things up so that your templates are defined in your code instead of a file, that'll be the easiest way.

If that doesn't work, you could resort to Java's String.format method, in which the template String uses %s as a placeholder for an expression (see the docs for full info on the syntax for that). This related question has an example for using that. This is probably closest to what you actually asked for.

You could also do something custom with string replacement, e.g.

val template: String = /* load from file */
template.replace("{name}", "my_table")

// or something more general-purpose
def customInterpolate(template: String, vars: Map[String, String]): String = {
  vars.foldLeft(template) { case (s, (k, v)) =>
    s.replace(s"{$k}", v)
  }
}
val exampleTmp = s"update {name} set message = {message}"
customInterpolate(exampleTmp, Map(
  "name" -> "my_table",
  "message" -> "hello",
))
Dylan
  • 13,645
  • 3
  • 40
  • 67
  • I liked your answer. I also have 2 questions for you: 1. What do you think about `String.formatted` ( in Jdk 15) ? and 2. You talked about SQL Injection - Do you happen to recommend any particular read on how sqls injections are exploited from within code? Thank you! – Alin Gabriel Arhip Jul 16 '22 at 07:39
  • 2
    [The docs](https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/String.html?is-external=true#formatted(java.lang.Object...)) say `String.formatted` is identical to `String.format(this, args)` so there's no reason to choose one over the other. As for SQL injection, [relevant xkcd](https://xkcd.com/327/) and [the OWASP page](https://owasp.org/www-community/attacks/SQL_Injection) – Dylan Jul 16 '22 at 12:56
  • 2
    Example would be if a user input the value `"my_table; drop all tables"` as the `table` argument to your `makeQuery`. – Dylan Jul 16 '22 at 12:57