Lisp cl-dbi - как встраивать управляющие символы в запросы sqlite - PullRequest
2 голосов
/ 18 июня 2019

Как вы встраиваете управляющие символы (например, "или" или :) в запросы cl-dbi для sqlite3?

в настоящее время используете make-string-output / get-output-stream-string для созданияпеременная, которая содержит данные в кавычках (json). Теперь я хочу иметь возможность хранить данные в sqlite3 db, но я явно строю строку неправильно, потому что я получаю ошибку

DBОшибка: нераспознанный токен: ":" (Код: ОШИБКА)

как мне экранировать символы в cl-dbi, чтобы передать их в sqlite3?

РЕДАКТИРОВАТЬ - вот краткий отрывокданных JSON, которые я пытаюсь сохранить (в виде текста) в базе данных sqlite3:

{
   "type": "artificial",

Обновление: GAH! У меня ушел день, чтобы найти заблудшую: вподготовленная строка запроса.: /

1 Ответ

3 голосов
/ 19 июня 2019

Насколько я могу судить, вы пытаетесь сгенерировать в Лиспе некоторую строку, содержащую действительный SQL, которая сама содержит строку литерала SQL.

Прежде всего, это экземпляр антипаттерна, который я называю «язык в строке». Я продолжаю думать, что у меня есть большая диатриба об этом, чтобы указывать людям, но, похоже, у меня нет. Достаточно сказать, что это своего рода антитеза тому, что люди Lisp пытались достичь в течение более 60 лет, и именно поэтому у нас есть атаки с использованием SQL-инъекций и много других дерьмов, которые поражают нас. Но эта битва давно проиграна, и все, что мы можем сделать, - это попытаться не утонуть в море грязи и гниющих кусочков людей, которые теперь засоряют поле битвы.

Итак, для этого нужно уметь делать две вещи.

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

Второе из них тривиально: это то, что делает директива format ~A. Или, если вы хотите получить фантазию, вы можете использовать cl-interpol .

Во-первых, я не знаю синтаксиса строковых литералов SQL, но приведу пример, который предполагает следующие простые правила:

  • буквенные строки разделяются " символами;
  • символ \ экранирует следующий символ, чтобы удалить его из какого-либо специального значения;
  • разрешены все остальные символы (это почти наверняка неверно).

Ну, есть много способов сделать это, каждый из которых включает в себя прогулку по последовательности символов в поисках тех, кого нужно убежать. Вот что-то довольно ужасное и быстрое, что я написал. Ему нужен макрос с именем nlet, который является конструкцией схемы с именем let, и он предполагает использование TRO в реализации (если ваша реализация этого не делает, получите тот, который делает).

(defmacro nlet (name bindings &body forms)
  "named let"
  (multiple-value-bind (vars vals) (values (mapcar (lambda (b)
                                                     (etypecase b
                                                       (symbol b)
                                                       (cons (first b))))
                                                   bindings)
                                           (mapcar (lambda (b)
                                                     (etypecase b
                                                       (symbol 'nil)
                                                       (cons
                                                        (unless (null (cddr b))
                                                          (error "bad binding ~A" b))
                                                        (second b))))
                                                   bindings))
    `(labels ((,name ,vars ,@forms))
       (,name ,@vals))))

(defun ->sql-string (seq)
  ;; turn SEQ (a sequence of characters) into a string representing an
  ;; SQL literal string (perhaps)
  (nlet loupe ((tail (coerce seq 'list))
               (accum '()))
    (if (null tail)
        (coerce (cons #\" (nreverse (cons #\" accum))) 'string)
      (destructuring-bind (first . rest) tail
        (loupe rest
               (case first
                 ((#\\ #\")
                  (append (list first #\\) accum))
                 (otherwise
                  (cons first accum))))))))

Так что теперь:

> (->sql-string "foo")
"\"foo\""
> (->sql-string '(#\f #\\ #\o #\" #\o))
"\"f\\\\o\\\"o\""

Это делает уродливым принтер Lisp, но (см. Выше) мы можем видеть, какие строки на самом деле:

> (format t "~&select x from y where x.y = ~A;~%"
          (->sql-string '(#\f #\\ #\o #\" #\o)))
select x from y where x.y = "f\\o\"o";
nil

И вы можете видеть, что литеральная строка SQL подчиняется правилам, которые я изложил выше.

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

...