Создание параметризованного запроса MySQL из нескольких объединенных строк

Я собираюсь создать несколько функций, которые помогут мне в создании множества MySqlCommand. Но я ошибаюсь (информация представлена ​​ниже).

Фон

Я достиг этого, определив константы в классе, например:

Public Const Create_Table = "CREATE TABLE IF NOT EXISTS `@arg0` (@arg1)"

Такие строки составляют основу моего параметризованного запроса. Теперь эти аргументы не являются одиночными значениями, они на самом деле являются конкатенированными строками. Так что я знаю, что приближаюсь к опасным водам, так что терпите меня здесь.

Я использую следующую функцию для объединения аргументов:

Public Shared Function ListToQuery(values As List(Of String),
                                   Optional ByVal separator As String = ", ") As String
    Dim queryBuilder As New StringBuilder

    For Each value As String In values
        queryBuilder.Append((value) & separator)
    Next
    Dim query As String = queryBuilder.ToString
    Return query.Remove(query.Length - 2)
End Function

Это работает как обычно; когда я называю это списком:

Dim myValues As New List(Of String)
myValues.AddRange({"Int Primary", "Text Name"})
MsgBox(ListToQuery(myValues))

Он возвращает "Int Primary, Text Name" - совершенно нормально. Однако здесь все начинает идти не так.

Моя следующая часть — создать MySqlCommand на основе нескольких аргументов, массива String. Это достигается вызовом следующей функции:

Public Shared Function BuildCommand(args() As String, Command As String, connection As MySqlConnection) As MySqlCommand

    Dim cmd As New MySqlCommand(Command, connection)
    For i As Integer = 0 To args.Length - 1
        'cmd.CommandText = cmd.CommandText.Replace("@arg" & i, args(i))
        cmd.Parameters.AddWithValue("@arg" & i, MySqlHelper.EscapeString(args(i)))
    Next
    Return cmd

End Function

Проблема

Я вызываю эту функцию следующим образом:

Dim myCommand as MySqlCommand = BuildCommand({"MyTableName", ListToQuery(myValues)}, Create_Table, myConnection)
Dim dr = myCommand.ExecuteReader()

Что, кажется, происходит, так это то, что возникает ошибка, и я обнаружил что-то, что, кажется, происходит в параметризованном запросе:

У вас есть ошибка в синтаксисе SQL; проверьте руководство, соответствующее вашей версии сервера MariaDB, на предмет правильного синтаксиса для использования рядом с «Int Primary, Text Name») в строке 1.

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

Изменить: результирующий запрос должен был выглядеть так:

СОЗДАЙТЕ ТАБЛИЦУ, ЕСЛИ НЕ СУЩЕСТВУЕТ `MyTableName` (Int Primary, Text Name)

Вопрос

У меня есть парочка, на самом деле. Во-первых, я хотел бы спросить, является ли это безопасным способом (т.е. даже с объединением моих аргументов и использованием параметров в запросе) для запуска команды, а во-вторых, как я могу избежать этой ошибки?

Я не совсем уверен, смогу ли я проанализировать каждый аргумент по отдельности, не сделав запрос «небезопасным» или не изменив общий оператор MySQL.

Я обсуждал это с другим пользователем, и ему удалось найти этот вопрос: параметризованный динамический запрос sql. Это задает аналогичный вопрос. Однако это не та же проблема, что и у меня.

Заранее спасибо.


person Ally    schedule 06.09.2017    source источник
comment
Во-первых: Связывать картинки - не лучшая идея... Во-вторых: Пожалуйста, не используйте .AddWithValue. Третье: Можете ли вы привести пример, какой запрос должен быть сгенерирован, потому что я не очень понял, что вы хотите сделать.   -  person muffi    schedule 06.09.2017
comment
@muffi, что бы вы посоветовали мне использовать в качестве альтернативы .AddWithValue() - и почему бы мне не использовать это? Кроме того, сгенерированный запрос в этом примере должен был быть CREATE TABLE IF NOT EXISTS ``MyTableName`` (Int Primary, Text Name) (не обращайте внимания на двойное `- форматирование синтаксиса в комментариях раздражает)   -  person Ally    schedule 06.09.2017
comment
.AddWithValue не очень хорошая идея, потому что в некоторых ситуациях тип базы данных неверен. Другое дело: я не знаю, как сделать то, что вы хотите, только обходной путь. Увидите это через несколько минут в ответе.   -  person muffi    schedule 06.09.2017
comment
Я не думаю, что вы можете использовать параметры для этого. Это было бы похоже на попытку выполнить INSERT INTO @tableNameAsVariable.   -  person the_lotus    schedule 06.09.2017
comment
@the_lotus, что ты имеешь в виду...? Я уверен, что ты сможешь :/   -  person Ally    schedule 06.09.2017


Ответы (2)


Этот код отличается от того, который вы мне показывали раньше, но теперь, когда я вижу, какие изменения вы внесли и в каком контексте вы это используете, все стало намного проще.

При добавлении параметров SQL вам не нужно экранировать их значение, так как это делается автоматически. Вероятно, поэтому вы испытываете его дважды.

Изменять:

cmd.Parameters.AddWithValue("@arg" & i, MySqlHelper.EscapeString(args(i)))

to:

cmd.Parameters.AddWithValue("@arg" & i, args(i))

...и это должно работать (и быть безопасным)!

person Visual Vincent    schedule 06.09.2017
comment
К сожалению, я изначально пробовал это, он все еще инкапсулировал параметр в '', как ни странно. - person Ally; 07.09.2017
comment
@АлексМ. : параметр должен быть заключен в один набор из ', иначе значение нельзя будет использовать должным образом (например, оно не может содержать пробелы). Выполняя обычную небезопасную конкатенацию, вы делаете то же самое: "'" & TextBox1.Text & "'". - person Visual Vincent; 07.09.2017
comment
@АлексМ. : Что, если вы попробуете вместо этого: cmd.Parameters.Add("@arg" & i, SqlDbType.VarChar).Value = args(i)? - person Visual Vincent; 07.09.2017
comment
[ERROR] You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''ID Int, Blah1 BigInt, PRIMARY KEY (ID)')' at line 1 - та же проблема. - person Ally; 07.09.2017
comment
Хм, возможно, вы не можете использовать строки с пробелами? Что произойдет, если вы попробуете что-то простое, например: args(0) = "Hello"? Хотя кажется, проблема в том, что по какой-то причине он добавляет дополнительную скобку в конце: ')'. - person Visual Vincent; 07.09.2017
comment
Нет, это не дополнительный кронштейн. Исключение показывает только ту часть запроса, которая вызывает проблему. Это означает, что ' вызывает проблему. Я думаю, что понял это; но это не то, что я могу изменить. - person Ally; 08.09.2017
comment
@АлексМ. : Давно не виделись :). Это было довольно поздно принять, я думал, что это не работает для вас... Что изменилось? - person Visual Vincent; 23.02.2018
comment
Я использовал его неправильно ... он предназначен не для имен, а только для значений, что сделало бы этот ответ действительным и более простым. Спасибо. - person Ally; 23.02.2018

Эта функция генерирует команду CREATE TABLE. Несколько раз сталкивался с одной и той же проблемой, но до сих пор не нашел подходящего решения. Вместо этого я создаю свой собственный запрос:

    Public Shared Function BuildCommand(args() As String, connection As MySqlConnection) As MySqlCommand
    Dim sb As New StringBuilder
    Dim cmd As New MySqlCommand
    cmd.Connection = connection
    sb.Append("CREATE TABLE IF NOT EXISTS`")
    sb.Append(args(0))
    sb.Append("`(")
    For i As Integer = 1 To args.Length - 1
        sb.Append("`")
        sb.Append(args(i))
        sb.Append("`,")
    Next
    sb.Remove(sb.Length - 1, 1)
    sb.Append(")")
    cmd.CommandText = sb.ToString
    Return cmd
End Function
person muffi    schedule 06.09.2017
comment
Это полностью разрушило цель моей функции. Это должно было обеспечить универсальную базовую функцию, которую можно было бы использовать командами с различным синтаксисом. Кроме того, могу я спросить, почему вы звоните sb.Append так много раз? - person Ally; 06.09.2017
comment
Алекс, связь в аргументах функции (ты так же закодировал ;-) ). Поскольку я отключил параметр Command, мне нужно было указать cmd, какое соединение он должен использовать. Я не создаю новое соединение! sb — это StringBuilder. Это намного быстрее, чем конкатенация, например str = str & "newstring". - person muffi; 06.09.2017
comment
«Почему вы звоните sb.Append так много раз?» Я имею в виду, есть ли лучший способ сделать это @muffi? Кроме того, я понял, что мои глаза сыграли злую шутку (связь). - person Ally; 06.09.2017
comment
Вы знаете класс StringBuilder? Название говорит само за себя :-) Методом .Append я добавляю строку в StringBuilder... ;-) Что тут сказать, не более ;-) - person muffi; 06.09.2017
comment
Я знаю, что он делает. В любом случае этот метод не будет работать с другими запросами. По этой причине я не принимаю этот ответ, извините. - person Ally; 06.09.2017