regex — катастрофический откат назад при использовании регулярного выражения PCRE для проверки SQL в переполнении стека

Я пытаюсь проверить некоторые SQL в общем виде:

UPDATE `mytable` SET `keyname` = 'keyvalue',
`a` = 'somestring',
`b` = 123,
`c` = NULL
WHERE `keyname` = 'keyvalue'

Есть больше полей, чем это. Значения будут строками, целыми числами или NULL.

Мое оригинальное регулярное выражение это:

(?ix)

^

\s*
UPDATE \s+ `mytable` \s+
SET \s+ `keyname` \s = \s 'keyvalue'
(, \s+
`[A-Z_]+`  (?# field name)
\s+ = \s+  (?# equals value)
(
-?[0-9]+         (?# an integer, possibly negative)
|
'(\\.|''|[^'])*' (?# a string in single quotes)
|
NULL             (?# NULL)
)
)+    (?# one or more such assignments)

\s+ WHERE \s+ `keyname` \s+ = \s+ 'keyvalue'

$

Это работает до определенного момента. В соответствии с https://regex101.com/ это соответствует 180 шагам.

К сожалению, реальный SQL длиннее, например:

UPDATE `mytable`
SET `keyname` = 'keyvalue',
`Markup` =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.
''Quisque vel mattis odio, quis iaculis sem.''
Nulla facilisi.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia Curae; Fusce ut dui venenatis, maximus lorem eget, ornare ex.
Aenean tempus pulvinar est, id fringilla enim sagittis id. Mauris finibus
cursus commodo.\r\n\r\n
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia Curae; Fusce ut dui venenatis, maximus lorem eget, ornare ex.
Aenean tempus pulvinar est, id fringilla enim sagittis id. Mauris finibus
cursus commodo.\r\n\r\n\r\n
\'Aenean in augue a est vulputate accumsan.\'
Phasellus nulla diam, laoreet a elit non, mattis finibus magna. Phasellus
faucibus iaculis mi sed pulvinar.\r\n
Aliquam non nisl ultricies, aliquam augue vitae, efficitur sapien.
Etiam viverra, magna a laoreet sollicitudin, ipsum erat tincidunt sem, nec
faucibus enim tortor eget massa.
Nunc nisi orci, lacinia vitae dictum et, vestibulum sed metus. ',
`From_Date` = NULL,
`To_Date` = NULL,
`Foo` = '',
`Box_Colour` = NULL,
`Modification_Date` = '2016-09-08 12:30:47',
`Modified_User` = 1,
`Modified_IP` = '192.168.1.1'
WHERE `keyname` = 'keyvalue'

Это теперь занимает 4301 шаг. Фактически, если вы увеличите Lorem Ipsum, мы достигнем более 20000 шагов.

Также, если мы введем ошибку (чтобы она не соответствовала), например, изменим:

`Foo` = '',

в

`Foo` = ''

Теперь он падает с Катастрофический откат.

Я могу избавиться от Катастрофического возврата (в некоторой степени), сделав внутреннюю группу (пары ключ / значение) атомарной группой. То есть изменить:

SET \s+ `keyname` \s = \s 'keyvalue'
(, \s+

в

SET \s+ `keyname` \s = \s 'keyvalue'
(?>, \s+

Более 20000 шагов на реальных данных вызывают сбой сценария PHP при запуске на моем целевом веб-сервере. Мне нужно свести шаги к более реалистичному значению. Я не могу понять, почему так много возвратов, когда кажется, что регулярное выражение достаточно явное. Похоже, что манипулирование собственническими квантификаторами или атомарными группами либо ничего не делает, либо приводит к тому, что SQL «pass» или «fail» не совпадает правильно.

0

Решение

редактировать: Для строкового подвыражения:
Используя версию развернутого цикла @NikiC,
замена \s с \s* где необходимо,
и добавление дополнительной атомной группы,
это сводится к разумному количеству шагов.

https://regex101.com/r/yV5xI7/3

(редактировать: Вы также можете попробовать эту версию без развернутого цикла
'(?>[^'\\]+|\\.|'')*' такая же разница. https://regex101.com/r/yV5xI7/5 )

 (?si)

^

\s*
UPDATE \s+ `mytable` \s+
SET \s+ `keyname` \s* = \s* 'keyvalue'

(?# one or more such key = value  )
(?>
\s* , \s*

(?# field name )
` [A-Z_]+ `

(?# equals )
\s* = \s*

(?# value )
(?>
(?# an integer, possibly negative )
-? [0-9]+
|
(?# or, a string )
'
[^'\\]*
(?:
(?: \\ . | '' )
[^'\\]*
)*
'

# '
# (?: [^'\\] | '' | \\ . )*
# '

|
(?# or, literal NULL )
NULL
)
)+

\s+ WHERE \s+ `keyname` \s* = \s* 'keyvalue'

$

Вы можете попытаться обернуть квантифицированное ядро ​​в атомную группу.
Кроме того, вам, вероятно, понадобится модификатор dot-all для любой ссылки на точку .,

И вы должны исключить побег, если вы соответствуете в одном из
чередования в вашем строковом подвыражении.
Известно, что двигатели выбирают путь, который соответствует.
В этом случае, [^'] также соответствует побег, и это выглядит как
побег не должен существовать сам по себе.
И, на самом деле, может быть действительная последовательность escape + перевод строки, следовательно, (?s),
То есть использование (?: \\ . | '' | [^'\\] )*,

Собираем все вместе, вот один для ideone

/
(?si)
^ \s* UPDATE \s+ `mytable` \s+ SET \s+ `keyname` \s = \s 'keyvalue'
(?>
, \s+ ` [A-Z_]+ `
(?# field name )
\s+ = \s+
(?# equals value )
(?:
-? [0-9]+
(?# an integer, possibly negative )
|  '
(?: \\ . | '' | [^'\\] )*
'
(?# a string in single quotes )
|  NULL
(?# NULL )
)
)+
(?# one or more such assignments )
\s+ WHERE \s+ `keyname` \s+ = \s+ 'keyvalue' $
/x

И один для php

'/
(?si)
^ \s* UPDATE \s+ `mytable` \s+ SET \s+ `keyname` \s = \s \'keyvalue\'
(?>
, \s+ ` [A-Z_]+ `
(?# field name )
\s+ = \s+
(?# equals value )
(?:
-? [0-9]+
(?# an integer, possibly negative )
|  \'
(?: \\\ . | \'\' | [^\'\\\] )*
\'
(?# a string in single quotes )
|  NULL
(?# NULL )
)
)+
(?# one or more such assignments )
\s+ WHERE \s+ `keyname` \s+ = \s+ \'keyvalue\' $
/x'
1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector