Any better way to write the (fn).() thing?

It is kind of alien to write like this: |> (fn x -> x == “SELECT” end).()
Any better way to write or idioms?

  def is_sql_select?(nil) do
    false
  end

  def is_sql_select?(sql) when not is_nil(sql) do
    sql
    |> String.trim_leading()
    |> String.slice(0, 6)
    |> String.upcase()
    |> (fn x -> x == "SELECT" end).()
  end
1 Like

Not pretty but you can use the & operator

"SELECT" |> (&(&1 == "SELECT")).()

I think the better way to do it would be to extract that to a named function, maybe use "SELECT" |> String.equivalent?("SELECT") but that does a bit more than you need.

2 Likes

This kind of still similar, right now people need to digest &.

String.equivalent? will do normalize that bring in performance issue, so just not accept it at the first place.

or this way will introduce another temp var, but simpler:

sql2 = sql |> .....
sql2 == "SELECT"
1 Like

Yes, there is a better way: put it in its own helper function with its own name.
It was a conscious choice to make this syntax obtuse, to guide people towards having more (and smaller) helper functions.

4 Likes

It’s not necessary to always pipe everything so I would do:

  def is_sql_select?(nil) do
    false
  end

  def is_sql_select?(sql) when not is_nil(sql) do
    sql_statement =
      sql
      |> String.trim_leading()
      |> String.slice(0, 6)
      |> String.upcase()
    
    sql_statement == "SELECT"
  end
14 Likes

Anyway from maintenance point of view, I will select @axelson 's reply as solution.
At the same time I also get other people’s help to make this more better:

  def sql_select?(nil) do
    false
  end

  def sql_select?(sql) when not is_nil(sql) do
    sql_statement =
      sql
      |> String.trim_leading()
      |> String.slice(0, 6)
      |> String.upcase()
    
    sql_statement == "SELECT"
  end

@ityonemo explains here:

https://hexdocs.pm/elixir/Kernel.html#defguard/1

Note the convention in Elixir is to name functions/macros allowed in guards with the  `is_`  prefix, such as
[ `is_list/1`  ](https://hexdocs.pm/elixir/Kernel.html#is_list/1). If, however, the function/macro returns a
boolean and is not allowed in guards, it should have no prefix and end with a question mark, such as
[ `Keyword.keyword?/1`  ](https://hexdocs.pm/elixir/Keyword.html#keyword?/1).

Of course it’s your code and you can do what you want with your function names =D, it’s just a convention that makes reading code easier!

3 Likes

In this particular case you can do

"SELECT"
|> Kernel.==("SELECT")

Though in the general case, extracting into a helper function is usually the way to go.

9 Likes

For this particular function, you could go even further and use more of the functions that the String module already provides for you:

def sql_select?(string) when is_binary(string) do
  sql
  |> String.trim_leading()
  |> String.upcase()
  |> String.starts_with?("SELECT")
end
8 Likes

This solution looks cool.

Language feature should support people to express the way they want. If we want work around, that also works, but not ideal. Thanks for your solution, it works.

You could use the ~> syntax, available in Elixir2_000 :stuck_out_tongue: (Match on the end of a pipe (Matchpipe?))

  def is_sql_select?(sql) when not is_nil(sql) do
    sql
    |> String.trim_leading()
    |> String.slice(0, 6)
    |> String.upcase()
    |> x ~> x == "SELECT"
  end

Keep in mind that pretty much all the proposed solutions here will fail upon meeting a UNION of selects in parentheses.

Just to say that detecting sql syntax is a bit more complex than stripping leading spaces and uppercasing. Not on topic of the actual question, just thought it relevant to mention.

1 Like

Not quite get you, even use Union, Select still the first keyword in SQL.

First keyword, yes. But your not matching first keyword, you’re matching against the first six characters, uppercases.

Even if you matched against first keyword, it would get it wrong if it came up against a query that starts with – select, followed by a newline and then some other keyword.

I only want to diff the Command side and Query side, are you saying in Select can have partial insert/update/create/delete ? like this:

Select * from account a, where a.is_success = (insert into user .....) 

I’m saying your query can start with an SQL comment, followed by the select keyword, and the line after that contains an insert statement

Really? Do you have a source for that?

That will not be a problem, already considered by trim first, so “- SELECT” != “SELECT”.

Trim removes whitespace - so all you’re doing is checking whether the first characters in your query (that are not whitespace) is SELECT. In the case of

-- SELECT id, name
SELECT *
FROM users

You’d have a false negative.

There are other SQL select queries that do not start with SELECT. One of them is using WITH syntax:

WITH cte AS (SELECT...)
SELECT ... FROM cte ...
1 Like