query-execのドキュメントの2番目の例は、パラメーターを受け取るデータベースクエリを実行する方法を示しています。
たとえば、と列を持つfood
テーブルがあると仮定すると、そのテーブルに挿入できる関数を記述できます。name
calories
;; insert-food!: database-connection string number -> void
;; Inserts an element into the food table.
(define (insert-food! conn name cals)
(query-exec conn
"insert into food (name, calories) values ($1, $2)"
name
cals))
データベースがPostgreSQLまたはプレースホルダーなどの使用をサポートするデータベースであり、それらのプレースホルダーの値を追加の引数としてに渡すことを前提として、パラメーター化されたクエリを使用しています。$1
$2
query-exec
外部から入力を受け取り、それを使用してSQLクエリを作成する場合は、パラメータ化されたクエリの使用方法を知っている必要があります。そうしないと、データベースの安全性が危険にさらされます。変数の値をすべて1つのクエリ文字列に補間する単一の文字列をフォーマットしようとしないでください。非常に間違ってプログラムをSQLインジェクションのエクスプロイトにさらす可能性があります。代わりに、これらの値を個別の引数としてに渡しますquery-exec
。
また、上記の関数はWebサーバーのコンテキストで使用する必要はないことに注意してください。たとえば、単体テストのコンテキストで使用できます。この関数をWebサーブレットで使用せずにテストすることをお勧めします。このようなもの:
#lang racket
(require db)
;; We want to export the following functions to outside clients.
(provide [struct-out foo]
insert-food!
get-foods!)
;; A food is a:
(struct food (name ;; string
cals) ;; number
#:transparent)
;; insert-food!: database-connection food -> void
;; Inserts an element into the food table.
(define (insert-food! conn a-food)
(query-exec conn
"insert into food (name, calories) values ($1, $2)"
(food-name a-food)
(food-cals a-food)))
;; get-foods!: database-connection -> (listof food)
;; Get a list of the foods in the database.
(define (get-foods! conn)
(for/list ([(name cal)
(in-query conn "select name, calories from food")])
(food name cal)))
(module+ test
(require rackunit)
;; Internal test. We'll use an in-memory SQLite database.
(define conn (sqlite3-connect #:database 'memory))
(query-exec conn "create table food (name string, calories double)")
;; Initially, it should be empty.
(check-equal? (get-foods! conn) '())
;; Now let's add a food:
;; http://www.eiyoukeisan.com/JapaneseFoodCalorie/zryouri/misoramen.html
(insert-food! conn (food "miso ramen" 56000.0))
;; Can we get it back?
(check-equal? (get-foods! conn) (list (food "miso ramen" 56000.0))))
これで、DrRacketでこのファイルを実行することにより、これらのデータベース機能の機能をテストできます。それらが確実に機能することがわかったら、Webサーブレットなどのさまざまなコンテキストでそれらを使用できます。
HTTPリクエストハンドラと同じ場所でこれをすべて実行しようとすると、間違ったアプローチになる可能性があります。データモデル、データベース操作をHTTPリクエストハンドラーとは別のモジュールに配置する必要があります。そうしないと、データモデルのテストが非常に困難になるリスクがあります。これが、 ContinueWebサーバーチュートリアルが「モデル」を個別のモジュールとして抽出することについて説明するのに邪魔にならない主な理由です。