Имя: Пароль:
1C
1C 7.7
v7: ODBCRecordSet - экранирование строковых параметров в запросе
0 Chai Nic
 
08.11.23
15:57
Есть sql-версия 1с 7.7, sql2000.

Выполняю прямой запрос, туда через УстановитьТекстовыйПараметр() передаю строковую переменную, которая в запросе подставляется в условие. И возникла проблема. Если в передаваемой строке есть символ апострофа, то sql выдает ошибку "Unclosed quotation mark before the character string".

Так как можно экранировать текстовый параметр, чтобы он всегда корректно обработался, как часть строки, независимо от всего мусора, что в нём лежит? Пишут, что апострофы надо удваивать перед передачей. Но только ли они могут вызывать проблему, может ещё какие символы нужно как-то экранировать или дублировать?

Кто в курсе?
1 vladmenleo
 
08.11.23
16:26
Вот список
https://learn.microsoft.com/ru-ru/sql/t-sql/functions/string-escape-transact-sql?view=sql-server-ver16
ну и апостров само собой - это экранирование строковых переменных типа Select * from dt where dt.name like '%вася%'
2 Chai Nic
 
08.11.23
16:52
(1) Применимо к: SQL Server 2016 (13.x) и более поздних версий
А у меня sql2000.
3 vladmenleo
 
09.11.23
04:25
(2) Я не имел ввиду использование функции, а таблицу с перечислением экранируемых символов
4 Chai Nic
 
09.11.23
06:58
(3) Там об экранировании в json, а мне надо что относится к экранированию строковых констант в sql-запросах.
5 ADirks
 
09.11.23
07:15
(0) одиночный апостроф экранируется им же, т.е. в текст запроса вместо "Д'Артаньян" надо передавать "Д''Артаньян"
6 Chai Nic
 
09.11.23
07:35
(5) Так вопрос о том, нет ли ещё каких запрещенных в sql-запросах символов, подлежащих экранированию..
7 vladmenleo
 
09.11.23
07:53
(6) Как вариант отсекай при формировании параметра все символы с кодом меньше 32 (пробел) ну и двойной апостроф. Вроде ни на что не должно ругаться
8 Chai Nic
 
09.11.23
08:46
(7) "Как вариант отсекай при формировании параметра"
А если они значащие и мне по ним как раз и надо накладывать условие?
9 АгентБезопасной Нацио
 
09.11.23
09:11
(8) ну они по-идее не текстовые...
10 АгентБезопасной Нацио
 
09.11.23
09:13
(8) до 20h идут спецсимволы всякие - перевод строки, возврат каретки, протяжка страницы, гудок и т.п.
11 Chai Nic
 
09.11.23
09:16
(10) А как тогда в запросе прописать "нетекстовые" символы в условии, если сравнивается строка типа varchar, скажем? Нет ли какого-то универсального метода, скажем, передачей кодов символов?
12 Chai Nic
 
09.11.23
09:17
(10) Они же могут храниться в char и varchar.. какая разница, текстовые они или нет. Кроме того, в некоторых кодировках они вполне себе текстовые, там может быть псевдографика и всякие значки.
13 АгентБезопасной Нацио
 
09.11.23
09:50
(12) Ну, серверу есть разница:
"Символьные типы данных имеют фиксированный (char) или переменный (varchar) размер. Начиная с SQL Server 2019 (15.x) при использовании параметров сортировки с поддержкой UTF-8 эти типы данных хранят весь диапазон символьных данных Юникод и используют кодировку UTF-8. Если указаны параметры сортировки без поддержки UTF-8, эти типы данных хранят только подмножество символьных данных, поддерживаемых соответствующей кодовой страницей указанных параметров сортировки."©
Как вариант, попробуй собирать строку поиска используя https://learn.microsoft.com/ru-ru/sql/t-sql/functions/char-transact-sql?view=sql-server-ver16 (для 2000 эта функция тоже работает)
Ну или попробуй вместо УстановитьТекстовыйПараметр использовать Подготовить+ПостроитьПараметры+УстПараметр+Выполнить. Но, имхо, тоже будут проблемы, ибо сам знаешь, во что преобразуются параметризованные запросы...
14 trad
 
09.11.23
09:56
Про апостроф, поддержу коллег, достаточно только его экранировать в передаваемом текстовом параметре или при написании литерала в сам тексте

Есть еще похожая тема - это указание шаблона для оператора like, когда в шаблоне нужно указать символы %_^[] не как символ шаблона, а как часть строки
Делается это так:
descr like '%~%%' escape '~'
15 Chai Nic
 
09.11.23
10:27
(13) Да, через char(кодсимвола_1)+..char(кодсимвола_n) получилось передать апостроф и теоретически всё что угодно. Но при этом невозможно использовать УстановитьТекстовыйПараметр(), потому что он сам кавычит апострофами то, что передается. То есть, запрос надо формировать программно через соединение строк в 1с, а не средствами текстовых параметров ODBCRecordSet. Не совсем это удобно, наверное, это можно даже посчитать недоработкой в 1с++. Они могли бы в УстановитьТекстовыйПараметр добавить например опцию, чтобы параметр заменялся на выражение из char().
16 АгентБезопасной Нацио
 
09.11.23
10:55
(15) попробуй в строке заменять спецсимволы на "'+char(39)+'"
Т.е "Д'Артаньян" должно выглядеть в текстовом параметре как
"Д'+char(39)+'Артаньян"
17 Chai Nic
 
09.11.23
11:00
(16) Я сделал инструмент для подобного, в глобальнике. Может кому пригодится.

Функция ТекстовыйПараметрВыражение(знач Параметр) Экспорт
    Параметр=Строка(Параметр);
    
    Стр="";
    ДлинаПараметра=СтрДлина(Параметр);
    Если ДлинаПараметра>0 Тогда
        Для Сч=1 по ДлинаПараметра Цикл
            Символ=Сред(Параметр,Сч,1);
            Стр=Стр+"CHAR("+КодСимв(Символ)+")+";
        КонецЦикла;
        Стр=Лев(Стр,СтрДлина(Стр)-1); // уберем лишний +
    КонецЕсли;
    Возврат Стр;
КонецФункции

Процедура УстановитьТекстовыйПараметрВыражение(ТекстЗапроса,ИмяПараметра,Параметр) Экспорт
    ТекстЗапроса=СтрЗаменить(ТекстЗапроса,":"+ИмяПараметра,ТекстовыйПараметрВыражение(Параметр));
КонецПроцедуры
18 АгентБезопасной Нацио
 
09.11.23
13:48
(17) для условий заменяй только в том случае, если символ - апостроф или непечатный.  
Для условия "подобно" - там сложнее, см (14)
19 Chai Nic
 
09.11.23
13:52
(18) Да ладно, и так работает. Можно всё заменять, механически. Речь как раз о поиске в справочнике по LIKE. И даже спецсимвол % нормально воспринимается как часть выражения через CHAR.
20 АгентБезопасной Нацио
 
09.11.23
13:57
(19) "Делай хорошо. Плохо - само получится"©
21 Chai Nic
 
09.11.23
13:58
(20) Вопрос критериев. Чем короче код, тем лучше. Производительность тут не важна. Всё равно затраты времени на скан таблиц на много порядков выше. Зато коротко и ясно, без кучи лишних условий.
22 АгентБезопасной Нацио
 
09.11.23
13:59
(21) ...пока не придется запрос в профайлере посмотреть, например...
23 Chai Nic
 
09.11.23
14:07
(22) Ну так то да.. Поправлю.
24 Chai Nic
 
09.11.23
15:36
Вот новая версия кода для вставки параметров в виде выражения с учетом справедливой критики)

В табло выдает
ТекстовыйПараметрВыражение("Д'Артаньян") = 'Д'+CHAR(39)+'Артаньян'

-------------

Функция ЗапрещенныйВПараметрахСимвол(Символ)
    Возврат ?((Символ="'") или (КодСимв(Символ)<32),1,0);
КонецФункции

Функция ТекстовыйПараметрВыражение(знач Параметр) Экспорт
    Параметр=Строка(Параметр);
    Стр="";

    ДлинаПараметра=СтрДлина(Параметр);
    Если ДлинаПараметра>0 Тогда
        ПредСимвол="";        
        Для Сч=1 по ДлинаПараметра Цикл
            Символ=Сред(Параметр,Сч,1);
            Если ЗапрещенныйВПараметрахСимвол(Символ)=1 Тогда
                Если ПредСимвол="" Тогда // начало строки
                    Стр="CHAR("+КодСимв(Символ)+")";
                ИначеЕсли ЗапрещенныйВПараметрахСимвол(ПредСимвол)=1 Тогда
                    Стр=Стр+"+CHAR("+КодСимв(Символ)+")"  
                иначе // предыдущий был обычный символ
                    Стр=Стр+"'+CHAR("+КодСимв(Символ)+")";
                КонецЕсли;
            Иначе // обычный символ
                Если ПредСимвол="" Тогда
                    Стр="'"+Символ; // начало строки
                ИначеЕсли ЗапрещенныйВПараметрахСимвол(ПредСимвол)=1 Тогда   // перед этим был запрещенный
                    Стр=Стр+"+'"+Символ;
                Иначе // предыдущий был обычный символ
                    Стр=Стр+Символ;
                КонецЕсли;
            КонецЕсли;
            ПредСимвол=Символ;
        КонецЦикла;
        Если ЗапрещенныйВПараметрахСимвол(Символ)=0 Тогда
            Стр=Стр+"'";
        КонецЕсли;
    КонецЕсли;
    Возврат Стр;
КонецФункции

Процедура УстановитьТекстовыйПараметрВыражение(ТекстЗапроса,ИмяПараметра,Параметр) Экспорт
    ТекстЗапроса=СтрЗаменить(ТекстЗапроса,":"+ИмяПараметра,ТекстовыйПараметрВыражение(Параметр));
КонецПроцедуры
25 Sserj
 
09.11.23
17:48
(24) Как же все это прекрасно, пока база живет в своем уютном закутке, где не нужно думать о всяких sql-инъекциях.
Все эти замены мелкая припарка, нет лучше метода чем подготовленный запрос с подстановкой параметров.
26 АгентБезопасной Нацио
 
09.11.23
18:02
(25) а в параметрах - сработает? (Я предлагал в (13), но сам раньше не натыкался на и на таком случае не пробовал А сейчас мне просто попробовать не на чем)
27 Chai Nic
 
09.11.23
18:09
(25) Я думал про подготовленные запросы, просто я их не умею готовить) Научусь - перепишу.
Есть два вида языков, одни постоянно ругают, а вторыми никто не пользуется.