racket/collects/db/scribblings/util.scrbl
Ryan Culpepper 9d34f0f147 db: added support for postgresql 9.2 types (json, ranges)
Other major changes:
 - pg code now uses only binary format
 - pg timestamptz now always UTC (tz = 0), added doc section
 - added contracts to most pg "can't-convert" errors
2012-09-17 12:29:17 -04:00

267 lines
8.3 KiB
Racket

#lang scribble/doc
@(require scribble/manual
scribble/eval
scribble/struct
racket/sandbox
racket/runtime-path
"config.rkt"
(for-label db db/util/datetime db/util/geometry db/util/postgresql db/util/testing))
@(define-runtime-path log-file "log-for-util.rktd")
@(define the-eval (make-pg-eval log-file #t))
@title[#:tag "util"]{Utilities}
The bindings described in this section are provided by the specific
modules below, not by @racketmodname[db] or @racketmodname[db/base].
@;{========================================}
@section[#:tag "datetime-util"]{Datetime Type Utilities}
@defmodule[db/util/datetime]
@deftogether[[
@defproc[(sql-datetime->srfi-date [t (or/c sql-date? sql-time? sql-timestamp?)])
srfi:date?]
@defproc[(srfi-date->sql-date [d srfi:date?])
sql-date?]
@defproc[(srfi-date->sql-time [d srfi:date?])
sql-time?]
@defproc[(srfi-date->sql-time-tz [d srfi:date?])
sql-time?]
@defproc[(srfi-date->sql-timestamp [d srfi:date?])
sql-timestamp?]
@defproc[(srfi-date->sql-timestamp-tz [d srfi:date?])
sql-timestamp?]]]{
Converts between this library's date and time values and SRFI 19's
date values (see @racketmodname[srfi/19]). SRFI dates store more
information than SQL dates and times, so converting a SQL time to a
SRFI date, for example, puts zeroes in the year, month, and day
fields.
@examples[#:eval the-eval
(sql-datetime->srfi-date
(query-value pgc "select time '7:30'"))
(sql-datetime->srfi-date
(query-value pgc "select date '25-dec-1980'"))
(sql-datetime->srfi-date
(query-value pgc "select timestamp 'epoch'"))
]
}
@defproc[(sql-day-time-interval->seconds [interval sql-day-time-interval?])
rational?]{
Returns the length of @racket[interval] in seconds.
}
@;{========================================}
@section[#:tag "geometry"]{Geometric Types}
@defmodule[db/util/geometry]
The following structures and functions deal with geometric values
based on the OpenGIS (ISO 19125) model.
@section-index{PostGIS}
Note: Geometric columns defined using the PostGIS extension to
PostgreSQL are not directly supported. Instead, data should be
exchanged in the Well-Known Binary format; conversion of the following
structures to and from WKB format is supported by the
@racket[wkb->geometry] and @racket[geometry->wkb] functions.
@defstruct*[point
([x real?] [y real?])]{
Represents an OpenGIS @tt{Point}.
}
@defstruct*[line-string
([points (listof point?)])]{
Represents an OpenGIS @tt{LineString}.
}
@defstruct*[polygon
([exterior linear-ring?]
[interior (listof linear-ring?)])]{
Represents an OpenGIS @tt{Polygon}.
}
@defstruct*[multi-point ([elements (listof point?)])]{
Represents an OpenGIS @tt{MultiPoint}, a collection of points.
}
@defstruct*[multi-line-string ([elements (listof line-string?)])]{
Represents an OpenGIS @tt{MultiLineString}, a collection of line-strings.
}
@defstruct*[multi-polygon ([elements (listof polygon?)])]{
Represents an OpenGIS @tt{MultiPolygon}, a collection of polygons.
}
@defstruct*[geometry-collection ([elements (listof geometry2d?)])]{
Represents an OpenGIS @tt{GeometryCollection}, a collection of
arbitrary geometric values.
}
@defproc[(geometry2d? [x any/c]) boolean?]{
Returns @racket[#t] if @racket[x] is a @racket[point],
@racket[line-string], @racket[polygon], @racket[multi-point],
@racket[multi-line-string], @racket[multi-polygon], or
@racket[geometry-collection]; @racket[#f] othewise.
}
@defproc[(line? [x any/c]) boolean?]{
Returns @racket[#t] if @racket[x] is a @racket[line-string]
consisting of exactly two points (cf OpenGIS @tt{Line}); @racket[#f]
otherwise.
}
@defproc[(linear-ring? [x any/c]) boolean?]{
Returns @racket[#t] if @racket[x] is a @racket[line-string] whose
first and last points are equal (cf OpenGIS @tt{LinearRing});
@racket[#f] otherwise.
}
@defproc[(geometry->wkb [g geometry2d?]
[#:big-endian? big-endian? (system-big-endian?)])
bytes?]{
Returns the Well-Known Binary (WKB) encoding of the geometric value
@racket[g]. The @racket[big-endian?] argument determines the byte
order used (the WKB format includes byte-order markers, so a robust
client should accept either encoding).
}
@defproc[(wkb->geometry [b bytes?])
geometry2d?]{
Decodes the Well-Known Binary (WKB) representation of a geometric
value.
}
@;{========================================}
@section[#:tag "postgresql-ext"]{PostgreSQL-specific Types}
@defmodule[db/util/postgresql]
@defstruct*[pg-array
([dimensions exact-nonnegative-integer?]
[dimension-lengths (listof exact-positive-integer?)]
[dimension-lower-bounds (listof exact-integer?)]
[contents vector?])]{
Represents a PostrgreSQL array. The @racket[dimension-lengths] and
@racket[dimension-lower-bounds] fields are both lists of
@racket[dimensions] elements. By default, PostgreSQL array indexes
start with 1 (not 0), so @racket[dimension-lower-bounds] is typically
a list of @racket[1]s.
}
@defproc[(pg-array-ref [arr pg-array?] [index exact-integer?] ...+)
any/c]{
Returns the element of @racket[arr] at the given position. There must
be as many @racket[index] arguments as the dimension of
@racket[arr]. Recall that PostgreSQL array indexes usually start with
1, not 0.
}
@defproc[(pg-array->list [arr pg-array?])
list?]{
Returns a list of @racket[arr]'s contents. The dimension of
@racket[arr] must be 1; otherwise an error is raised.
}
@defproc[(list->pg-array [lst list?])
pg-array?]{
Returns a @racket[pg-array] of dimension 1 with the contents of
@racket[lst].
}
@defstruct*[pg-empty-range ()]{
Represents an empty range.
}
@defstruct*[pg-range
([lb _range-type]
[includes-lb? boolean?]
[ub _range-type]
[includes-ub? boolean?])]{
Represents a range of values from @racket[lb] (lower bound) to
@racket[ub] (upper bound). The @racket[includes-lb?] and
@racket[includes-ub?] fields indicate whether each end of the range is
open or closed.
The @racket[lb] and @racket[ub] fields must have the same type; the
permissible types are exact integers, real numbers, and
@racket[sql-timestamp]s. Either or both bounds may also be
@racket[#f], which indicates the range is unbounded on that end.
}
@defproc[(pg-range-or-empty? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a @racket[pg-range] or
@racket[pg-empty-range] instance; otherwise, returns @racket[#f].
}
@deftogether[[
@defstruct*[pg-box
([ne point?] [sw point?])]
@defstruct*[pg-path
([closed? boolean?] [points (listof point?)])]
@defstruct*[pg-circle
([center point?] [radius real?])]
]]{
These structures represent certain of PostgreSQL's built-in geometric
types that have no appropriate analogue in the OpenGIS model:
@tt{box}, @tt{path}, and @tt{circle}. The @tt{point}, @tt{lseg}, and
@tt{polygon} PostgreSQL built-in types are represented using
@racket[point], @racket[line-string] (@racket[line?]), and
@racket[polygon] structures.
Note: PostgreSQL's built-in geometric types are distinct from those
provided by the PostGIS extension library (see @secref["geometry"]).
}
@;{========================================}
@section[#:tag "util-testing"]{Testing Database Programs}
@defmodule[db/util/testing]
This module provides utilities for testing programs that use database
connections.
@defproc[(high-latency-connection [connection connection?]
[latency (>=/c 0)]
[#:sleep-atomic? sleep-atomic? any/c #f])
connection?]{
Returns a proxy connection for @racket[connection] that introduces
@racket[latency] additional seconds of latency before operations that
require communicating with the database back end---@racket[prepare],
@racket[query], @racket[start-transaction], etc.
Use this function in performance testing to roughly simulate
environments with high-latency communication with a database back
end.
If @racket[sleep-atomic?] is true, then the proxy enters atomic mode
before sleeping, to better simulate the effect of a long-running FFI
call (see @secref["ffi-concurrency"]). Even so, it may not accurately
simulate an ODBC connection that internally uses cursors to fetch data
on demand, as each fetch would introduce additional latency.
}
@(close-eval the-eval)