Блестящий текст Ввод в жало используется в SQL - PullRequest
0 голосов
/ 06 февраля 2020

У меня есть textInput в приложении Shiny, чтобы пользователь мог написать трехсимвольные коды продуктов, разделенные запятой. Например: F03, F04, F05.

Вывод textInput используется в функции, вызывающей скрипт sql. Он будет использоваться в качестве фильтра в операторе sql, например

sqlfunction <- function(text){

sqlQuery(conn, stri_paste("select .... where product_code in (", text, ");"))

}

Чтобы преобразовать textInput в строку, которую я могу использовать в операторе sql, я использовал

toString(sprintf("'%s'", unlist(strsplit(input$text_input, ","))))

Это работает и преобразует textInput в 'F03', 'F04'. 'F05', однако, при использовании в sql только первый код, 'F03', используется в поиске, несмотря на использование product_code in () . Возвращаются только данные с кодом продукта F03.

Как получить все три кода, если не больше, записанные в textInput в строку для использования в предложении sql?

Андрей

Ответы [ 3 ]

1 голос
/ 06 февраля 2020

, если вы вводите

F03, F04, F05

с пробелами между запятыми и следующим значением, оператор дает:

выберите .... где код продукта в ('F03', 'F04', 'F05');

Обратите внимание на пробелы. Тогда значения 'F04' и 'F05' не найдены.

Что делать, если введено значение 'F03,F04,F05'? (без пробелов)

0 голосов
/ 06 февраля 2020

Я объединю важные компоненты других ответов и обсуждения в один предлагаемый ответ. Не стесняйтесь принять одну из них, сначала у них были идеи.

Как сказал Jrm_FRL, вполне возможно, что в вашем сценарии сохраняется пробел вокруг запятых, который не должен совпадать в SQL.

toString(sQuote(unlist(strsplit("hello, world, again", ","))))
# [1] "'hello', ' world', ' again'"
###              ^--       ^--  leading spaces on the strings

Некоторые параметры:

  1. Если вы считаете, что для пользователя важно иметь возможность преднамеренно вводить пробелы вокруг запятых (то есть в начале или в конце строки / шаблона), тогда ваша единственная надежда состоит в том, чтобы поручить пользователю использовать только пробелы, когда это необходимо.

  2. В противном случае вы можете использовать trimws:

    toString(sQuote(trimws(unlist(strsplit("hello, world, again", ",")))))
    # [1] "'hello', 'world', 'again'"
    
  3. strsplit(..., ",") может быть неверным, если у пользователя есть кавычки, чтобы держать вещи вместе. Вы можете рассмотреть возможность использования read.csv:

    trimws(unlist(read.csv(text="hello, \"world, too\", again", header = FALSE, stringsAsFactors = FALSE)))
    #           V1           V2           V3 
    #      "hello" "world, too"      "again" 
    

    Это не совсем совместимо с вариантом 1, приведенным выше.

Во-вторых, как согласились Тим Бигелайзен и Jrm_FRL, вы особенно склонны к инъекции SQL здесь. Искаженная (случайно или намеренно) поисковая строка пользователя может в лучшем случае испортить результаты этого запроса , в худшем случае (в зависимости от разрешений на подключение) повредить или удалить данные в базе данных. (Я настоятельно рекомендую вам прочитать https://db.rstudio.com/best-practices/run-queries-safely/.)

Способы защиты:

  1. Не добавляйте одиночные кавычки вручную вокруг вашего данные: если строка содержит одинарную кавычку, она будет не экранирована и в лучшем случае приведет к ошибке SQL.

    toString(sQuote(trimws(unlist(strsplit("hello'; drop table students; --", ",")))))
    # [1] "'hello'; drop table students; --'"
    ### this query may delete the 'students' table
    ### notice that `sQuote` is not enough here, it is not escaping this correctly
    

    Вместо этого используйте DBI::dbQuoteString. Хотя я полагаю, что большинство СУБД используют одно и то же соглашение, заключенное в одинарные кавычки (и ваш вопрос говорит о том, что и ваши тоже), было бы неплохо позволить драйверу базы данных определять, как обращаться с литеральными строками и со встроенными кавычками. *

    toString(DBI::dbQuoteString(con, trimws(unlist(strsplit("hello'; drop table students; --", ",")))))
    # [1] "'hello''; drop table students; --'"
    ###          ^^ this is SQL's way of escaping an embedded single-quote
    ### this is now a single string, allegedly SQL-safe
    
  2. Вместо включения строк в запрос, используйте DBI::dbBind, хотя по общему признанию вам нужно будет включить несколько обязательных закладок (часто, но не всегда ?) на основе длина вектора ваших значений.

    val_str <- "hello, world, again"
    val_vec <- trimws(unlist(strsplit("hello, world, again", ",")))
    qmarks <- paste(rep("?", length(val_vec)), collapse = ",")
    qmarks
    # [1] "?,?,?"
    qry <- paste("select ... where product_code in (", qmarks, ")")
    out <- tryCatch({
      res <- NULL
      res <- DBI::dbSendStatement(con, qry)
      DBI::dbBind(res, val_vec)
      DBI::dbFetch(res)
    }, finally = { if (!is.null(res)) suppressWarnings(DBI::dbClearResult(res)) })
    

    Использование ? варьируется в зависимости от DMBS, поэтому вам может потребоваться провести некоторые исследования для вашей конкретной c ситуации.

    (Хотя я здесь используется tryCatch, чтобы «гарантировать», что res будет очищено при выходе, этот шаблон немного более надежен, чем без него. Если часть запроса или привязки завершится неудачно без части finally=, то он может выйти соединение в несовершенном состоянии.)

0 голосов
/ 06 февраля 2020

Вот то, что я подозреваю, то, что вы хотите:

text_input = "A,B,C"
in_clause <- paste0("'", unlist(strsplit(text_input, ",")), "'", collapse=",")
sql <- paste0("WHERE product_code IN (", in_clause, ")")
sql

[1] "WHERE product_code IN ('A','B','C')"

Здесь я все еще использую вашу комбинацию unlist и strsplit для генерации строкового вектора терминов для предложения IN , Но затем я использую paste с collapse, чтобы получить желаемый результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...