7.2 Formatting SQL
| (require toolbox/db/sql) | package: toolbox-db-lib |
If v is a string, it is used directly.
If v is a symbol, it is formatted as a SQL identifier using sql:id.
If v is an exact integer, it is converted using number->string.
If v is any other rational number, it is converted to a flonum using real->double-flonum, then converted to a string using number->string.
If v is sql-null, it is converted to the string "NULL".
> (~sql "SELECT " 'id " FROM " 'comment " WHERE " 'rating " > " 0.75) "SELECT \"id\" FROM \"comment\" WHERE \"rating\" > 0.75"
The ~sql function is especially useful when used @ syntax via the at-exp language.
Example:
@~sql{SELECT name FROM user WHERE id IN @sql:tuple*[user-ids]}
procedure
name : (or/c symbol? string?) keywords : (or/c (listof string?) #f) = (current-sql-keywords)
If keywords is not #f, quoting is suppressed for any valid SQL identifiers not in the supplied keywords list (compared using string-ci=?). If using SQLite, current-sql-keywords can be set to (sqlite3-keywords) to safely disable quoting of non-keyword identifiers:
> (current-sql-keywords (sqlite3-keywords)) > (sql:id "hello") "hello"
> (sql:id "select") "\"select\""
Changed in version 1.1 of package toolbox-db-lib: Added the #:keywords argument.
parameter
(current-sql-keywords) → (or/c (listof string?) #f)
(current-sql-keywords keywords) → void? keywords : (or/c (listof string?) #f)
= #f
If using SQLite, this parameter can safely be set to (sqlite3-keywords).
Added in version 1.1 of package toolbox-db-lib.
procedure
(sql:string name) → string?
name : (or/c symbol? string?)
> (sql:string "hello") "'hello'"
> (sql:string "it's") "'it''s'"
> (sql:seq 1 2 3) "1,2,3"
> (sql:seq* 1 2 '(3 4)) "1,2,3,4"
> (sql:tuple 1 2 3) "(1,2,3)"
procedure
(sql:tuple* v ... vs) → string?
v : pre-sql? vs : (listof pre-sql?)
> (sql:tuple* 1 2 '(3 4)) "(1,2,3,4)"
In a sense, query:bag is the inverse of query-list. However, because the query contains no ORDER BY clause, the order of the resulting rows cannot be guaranteed. If the order of vs is important, query:indexed-list should be used instead.
procedure
(query:indexed-list vs) → string?
vs : (listof pre-sql?)
> (query:indexed-list '(1 2 3)) "VALUES (0,1),(1,2),(2,3)"
> (query:indexed-list '()) "SELECT NULL,NULL WHERE 0"
> (query-list (~sql "WITH nums(i,n) AS (" (query:indexed-list (range 10)) ")\n" "SELECT n*n FROM nums ORDER BY i")) '(0 1 4 9 16 25 36 49 64 81)
procedure
(query:rows rows [#:columns num-columns]) → string?
rows : (listof (vectorof pre-sql?)) num-columns : (or/c exact-nonnegative-integer? #f) = #f
If num-columns is not #f, it supplies the number of columns the query should return. Otherwise, the number of columns is inferred from the length of the elements of rows. If num-columns is #f and no rows are provided, an exn:fail:contract exception is raised.
> (query:rows '(#(1 2) #(3 4) #(5 6))) "VALUES (1,2),(3,4),(5,6)"
> (query:rows '() #:columns 2) "SELECT NULL,NULL WHERE 0"
> (query:rows '()) query:rows: cannot infer number of columns;
no rows given and #:columns not specified
Pre-SQL values can be converted to SQL strings using ~sql.