On this page:
4.1 SQL Type Conversions
4.1.1 Postgre  SQL Types
4.1.2 My  SQL Types
4.1.3 Cassandra Types
4.1.4 SQLite Types
4.1.5 ODBC Types
4.2 SQL Data
4.2.1 SQL NULL
sql-null
sql-null?
sql-null->false
false->sql-null
4.2.2 Dates and Times
sql-date
sql-time
sql-timestamp
sql-interval
sql-year-month-interval?
sql-day-time-interval?
sql-interval->sql-time
sql-time->sql-interval
4.2.3 Bits
make-sql-bits
sql-bits?
sql-bits-length
sql-bits-ref
sql-bits-set!
sql-bits->list
sql-bits->string
list->sql-bits
string->sql-bits

4 SQL Types and Conversions🔗ℹ

Connections automatically convert query results to appropriate Racket types. Likewise, query parameters are accepted as Racket values and converted to the appropriate SQL type.

Examples:
> (query-value pgc "select count(*) from the_numbers")

4

> (query-value pgc "select false")

#f

> (query-value pgc "select 1 + $1" 2)

3

If a query result contains a column with a SQL type not supported by this library, an exception is raised. As a workaround, cast the column to a supported type:

Examples:
> (query-value pgc "select inet '127.0.0.1'")

query-value: unsupported type

  type: inet

  typeid: 869

> (query-value pgc "select cast(inet '127.0.0.1' as varchar)")

"127.0.0.1/32"

The exception for unsupported types in result columns is raised when the query is executed, not when it is prepared; for parameters it is raised when the parameter values are supplied. Thus even unexecutable prepared statements can be inspected using prepared-statement-parameter-types and prepared-statement-result-types.

4.1 SQL Type Conversions🔗ℹ

This section describes the correspondences between SQL types and Racket types for the supported database systems.

4.1.1 PostgreSQL Types🔗ℹ

This section applies to connections created with postgresql-connect.

The following table lists the PostgreSQL types known to this library, using the names listed in pg_type.typname, along with their corresponding Racket representations.

PostgreSQL

type

Racket

bool

'boolean

boolean?

char

'char1

char?

int2

'smallint

exact-integer?

int4

'integer

exact-integer?

int8

'bigint

exact-integer?

float4

'real

real?

float8

'double

real?

numeric

'decimal

real?

bpchar

'character

string?

varchar

'varchar

string?

uuid

'uuid

uuid?

text

'text

string?

bytea

'bytea

bytes?

date

'date

sql-date?

time

'time

sql-time?

timetz

'timetz

sql-time?

timestamp

'timestamp

sql-timestamp? or -inf.0 or +inf.0

timestamptz

'timestamptz

sql-timestamp? or -inf.0 or +inf.0

interval

'interval

sql-interval?

bit

'bit

bit-vector?

varbit

'varbit

bit-vector?

json

'json

jsexpr?

jsonb

'jsonb

jsexpr?

int4range

'int4range

pg-range-or-empty?

int8range

'int8range

pg-range-or-empty?

numrange

'numrange

pg-range-or-empty?

tsrange

'tsrange

pg-range-or-empty?

tstzrange

'tstzrange

pg-range-or-empty?

daterange

'daterange

pg-range-or-empty?

point

'point

point?

lseg

'lseg

line?

path

'path

pg-path?

box

'box

pg-box?

polygon

'polygon

polygon?

circle

'circle

pg-circle?

The 'char1 type, written "char" in PostgreSQL’s SQL syntax (the quotation marks are significant), is one byte, essentially a tiny integer written as a character.

A SQL value of type decimal is converted to either an exact rational or +nan.0. When converting Racket values to SQL decimal, exact rational values representable by finite decimal strings are converted without loss of precision. (Precision may be lost, of course, if the value is then stored in a database field of lower precision.) Other real values are converted to decimals with a loss of precision. In PostgreSQL, numeric and decimal refer to the same type.

Examples:
> (query-value pgc "select real '+Infinity'")

+inf.0

> (query-value pgc "select numeric '12345678901234567890'")

12345678901234567890

A SQL timestamp with time zone is converted to a Racket sql-timestamp in UTC—that is, with a tz field of 0. If a Racket sql-timestamp without a time zone (tz is #f) is given for a parameter of type timestamp with time zone, it is treated as a timestamp in UTC. See also PostgreSQL Timestamps and Time Zones.

The geometric types such as 'point are represented by structures defined in the db/util/geometry and db/util/postgresql modules.

PostgreSQL user-defined domains are supported in query results if the underlying type is supported. Recordset headers and prepared-statement-result-types report them in terms of the underlying type. Parameters with user-defined domain types are not currently supported. As a workaround, cast the parameter to the underlying type. For example, if the type of $1 is a domain whose underlying type is integer, then replace $1 with ($1::integer).

For each type in the table above, the corresponding array type is also supported, using the pg-array structure. Use the = ANY syntax with an array parameter instead of dynamically constructing a SQL IN expression:

Examples:
> (query-value pgc "select 1 in (1, 2, 3)")

#t

> (query-value pgc "select 1 = any ($1::integer[])"
               (list->pg-array (list 1 2 3)))

#t

A list may be provided for an array parameter, in which case it is automatically converted using list->pg-array. The type annotation can be dropped when the array type can be inferred from the left-hand side.

Examples:
> (query-value pgc "select 1 = any ($1)" (list 1 2 3))

#t

> (query-value pgc "select $1::integer = any ($2)"
               1 (list 1 2 3))

#t

> (query-value pgc "select $1 = any ($2)" ; what type are we using?
               1 (list 1 2 3))

query-value: cannot convert given value to SQL type

  given: 1

  type: string

  expected: string?

  dialect: PostgreSQL

PostgreSQL defines many other types, such as network addresses and row types. These are currently not supported, but support may be added in future versions of this library.

Changed in version 1.1 of package db-lib: Added support for the 'uuid type.

Changed in version 1.14 of package db-lib: The numeric type now supports infinities.

4.1.2 MySQL Types🔗ℹ

This section applies to connections created with mysql-connect.

The types that MySQL reports types for query results do not correspond exactly to the declared types in SQL. The following table lists the MySQL result types known to this library, along with their corresponding Racket representations:

MySQL

result type

Racket

INTEGER

'integer

exact-integer?

TINYINT

'tinyint

exact-integer?

SMALLINT

'smallint

exact-integer?

MEDIUMINT

'mediumint

exact-integer?

BIGINT

'bigint

exact-integer?

FLOAT

'real

real?

DOUBLE

'double

real?

DECIMAL

'decimal

exact?

NUMERIC

'decimal

exact?

CHAR

'string [1]

string?

VARCHAR

'var-string [1]

string?

TEXT

'blob [1]

string?

BINARY

'string [1]

bytes?

VARBINARY

'var-string [1]

bytes?

BLOB

'blob [1]

bytes?

BIT

'bit

bit-vector?

DATE

'date

sql-date?

DATETIME

'datetime

sql-timestamp?

TIMESTAMP

'datetime

sql-timestamp?

TIME

'time [2]

sql-time? or sql-day-time-interval?

JSON

'json [3]

jsexpr?

VECTOR

'vector [4]

flvector?

GEOMETRY

'geometry [5]

geometry2d?

Historically, MySQL did not provide types for query parameters; rather, the client was expected to tell the server the type of each parameter it sent. (Since MySQL v8.0, the server sends generally accurate type information for parameters, but this library currently discards that information.) Consequently, prepared-statement-parameter-types reports the pseudo-type 'any for every parameter of a MySQL prepared statement, and the parameter type sent to the server is inferred from the value as follows:

Racket

parameter type

string?

'var-string

exact-integer?

'integer or 'bigint or 'decimal

rational?

'double

bytes?

'blob

sql-date?

'date

sql-time?

'time

sql-day-time-interval?

'time

sql-timestamp?

'timestamp

bit-vector?

'bit

geometry2d?

'blob or 'geometry [5]

mysql-json?

'json

[1] Fields of type CHAR and BINARY are both reported as 'string; they are distinguished by the absence or presence of 'binary in the type’s flags, respectively. Likewise, VARCHAR and VARBINARY are reported as 'var-string; and TEXT and BLOB are reported as 'blob; and they are distinguished by the absence or presence of a 'binary flag. Both TEXT and BLOB types have the 'blob flag set.

[2] MySQL’s TIME type represents time intervals, which may not correspond to times of day (for example, the interval may be negative or larger than 24 hours). In conversion from MySQL results to Racket values, those TIME values that represent times of day are converted to sql-time values; the rest are represented by sql-interval values. (See also sql-time->sql-interval and sql-interval->sql-time.)

[3] The JSON type (introduced in MySQL 5.7) is represented by jsexpr? values, but that representation overlaps with the representation of MySQL character types (eg, VARCHAR), and the values are interpreted in incompatible ways. Query parameters are only sent as JSON if they are wrapped with mysql-json; JSON result values are returned as unwrapped jsexpr? values.

[4] The VECTOR type (introduced in MySQL 9.0) is represented by non-empty flvectors (flvector?). Beware that flvectors store double-precision floating-point numbers, but MySQL VECTOR fields are single-precision.

[5] Geometry values (geometry2d?) are sent using their “well-known binary” encoding. For servers version 8.0 and later, the values are sent as 'blob; for servers version 5.7.x and earlier, values are sent as 'geometry.

The MySQL enum and set types are not supported. As a workaround, cast them to or from either integers or strings.

Changed in version 1.11 of package db-lib: Added support for JSON, VECTOR. Changed format of GEOMETRY query parameters for servers running MySQL 8.0 and later.

Changed in version 1.14 of package db-lib: Send exact integers as DECIMAL instead of FLOAT.

4.1.3 Cassandra Types🔗ℹ

This section applies to connections created with cassandra-connect.

The following table lists the Cassandra types known to this library, along with their corresponding Racket representations.

Cassandra

Racket

'ascii

string?

'bigint

exact-integer?

'int

exact-integer?

'blob

bytes?

'boolean

boolean?

'decimal

exact-integer?

'double

real?

'float

real?

'text

string?

'varchar

string?

'timestamp

sql-timestamp?

'uuid

uuid?

'timeuuid

uuid?

'varint

exact-integer?

`(list ,t)

(listof t)

`(set ,t)

(set/c t)

`(map ,k ,v)

(alistof k v)

`(tuple ,t ...)

(vector/c t ...)

4.1.4 SQLite Types🔗ℹ

This section applies to connections created with sqlite3-connect.

The following table lists the SQLite types known to this library, along with their corresponding Racket representations.

Unlike PostgreSQL and MySQL, SQLite does not enforce declared type constraints (with the exception of integer primary key) on columns. Rather, every SQLite value has an associated “storage class”.

SQLite storage class

Racket

integer

exact-integer?

real

real?

text

string?

blob

bytes?

SQLite does not report specific parameter and result types for prepared queries. Instead, they are assigned the pseudo-type 'any. Conversion of Racket values to parameters accepts strings, bytes, and real numbers.

An exact integer that cannot be represented as a 64-bit signed integer is converted as real, not integer.

Examples:
> (expt 2 80)

1208925819614629174706176

> (query-value slc "select ?" (expt 2 80))

1.2089258196146292e+24

4.1.5 ODBC Types🔗ℹ

This section applies to connections created with odbc-connect or odbc-driver-connect.

The following table lists the ODBC types known to this library, along with their corresponding Racket representations.

ODBC

type

Racket

SQL_CHAR

'character

string?

SQL_VARCHAR

'varchar

string?

SQL_LONGVARCHAR

'longvarchar

string?

SQL_WCHAR

'wchar

string?

SQL_WVARCHAR

'wvarchar

string?

SQL_WLONGVARCHAR

'wlongvarchar

string?

SQL_BINARY

'binary

bytes?

SQL_VARBINARY

'varbinary

bytes?

SQL_LONGVARBINARY

'longvarbinary

bytes?

SQL_INTEGER

'integer

exact-integer?

SQL_TINYINT

'tinyint

exact-integer?

SQL_SMALLINT

'smallint

exact-integer?

SQL_BIGINT

'bigint

exact-integer?

SQL_FLOAT

'float

real?

SQL_REAL

'real

real?

SQL_DOUBLE

'double

real?

SQL_NUMERIC

'numeric

rational?

SQL_DECIMAL

'decimal

rational?

SQL_BIT

'bit1

boolean?

SQL_DATE

'date

sql-date?

SQL_TIME

'time

sql-time?

SQL_DATETIME

'datetime

sql-timestamp?

SQL_TIMESTAMP

'timestamp

sql-timestamp?

SQL_UNKNOWN

'unknown

string?

SQL Server extensions:

SQL_SS_TIME2 (-154)

'time2

sql-time?

DB2 extensions:

SQL_GRAPHIC (-95)

'graphic

string?

SQL_VARGRAPHIC (-96)

'vargraphic

string?

SQL_LONGVARGRAPHIC (-97)

'longvargraphic

string?

SQL_BLOB (-98)

'blob

bytes?

SQL_CLOB (-99)

'clob

string?

SQL_DBCLOB (-350)

'dbclob

string?

If the connection was made with #:strict-parameter-types? #t, then the parameter type set by the ODBC driver is usually respected. Unfortunately, few ODBC drivers provide useful parameter types for prepared queries. Some assign parameters the 'unknown type; worse, some assign all parameters a single type such as 'varchar. To avoid enforcing irrelevant type constraints in the latter case, connect with #:strict-parameter-types? #f, and the connection will assign all parameters the type 'unknown. When a parameter type is 'unknown, this library sends the ODBC driver a parameter type based on the parameter value as follows:

Racket

parameter type

exact-integer?

SQL_INTEGER or SQL_BIGINT or SQL_NUMERIC

(and/c real? exact?)

SQL_NUMERIC or SQL_DOUBLE

(and/c real? inexact?)

SQL_DOUBLE

boolean?

SQL_BIT

string?

SQL_VARCHAR or SQL_WVARCHAR [1]

bytes?

SQL_VARBINARY [2]

sql-date?

SQL_DATE

sql-time?

SQL_TIME

sql-day-time-interval?

SQL_TIME

sql-timestamp?

SQL_TIMESTAMP

[1] The type of a string parameter is determined by the connection’s character mode; see odbc-connect’s #:character-mode argument. Depending on connection quirks, the type may also be set as SQL_LONGVARCHAR or SQL_WLONGVARCHAR.

[2] Depending on connection quirks, the type may also be set as SQL_LONGVARBINARY.

Interval types are not currently supported for ODBC connections.

Changed in version 1.14 of package db-lib: Added support for the DB2 types. Exact numbers are sent as SQL_NUMERIC if possible.

4.2 SQL Data🔗ℹ

This section describes data types for representing various SQL types that have no existing appropriate counterpart in Racket.

4.2.1 SQL NULL🔗ℹ

SQL NULL is translated into the unique sql-null value.

A special value used to represent NULL values in query results. The sql-null value may be recognized using eq?.

Example:
> (query-value pgc "select NULL")

#<sql-null>

procedure

(sql-null? x) → boolean?

  x : any/c
Returns #t if x is sql-null; #f otherwise.

procedure

(sql-null->false x) → any/c

  x : any/c
Returns #f if x is sql-null; otherwise returns x.

Examples:
> (sql-null->false "apple")

"apple"

> (sql-null->false sql-null)

#f

> (sql-null->false #f)

#f

procedure

(false->sql-null x) → any/c

  x : any/c
Returns sql-null if x is #f; otherwise returns x.

Examples:
> (false->sql-null "apple")

"apple"

> (false->sql-null #f)

#<sql-null>

4.2.2 Dates and Times🔗ℹ

The DATE, TIME (WITH TIME ZONE and without), TIMESTAMP (WITH TIME ZONE and without), and INTERVAL SQL types are represented by the following structures.

See also Datetime Type Utilities for more functions on datetime values.

struct

(struct sql-date (year month day))

  year : exact-integer?
  month : (integer-in 0 12)
  day : (integer-in 0 31)
Represents a SQL date.

Dates with zero-valued month or day components are a MySQL extension.

struct

(struct sql-time (hour minute second nanosecond tz))

  hour : exact-nonnegative-integer?
  minute : exact-nonnegative-integer?
  second : exact-nonnegative-integer?
  nanosecond : exact-nonnegative-integer?
  tz : (or/c exact-integer? #f)

struct

(struct sql-timestamp (year
    month
    day
    hour
    minute
    second
    nanosecond
    tz))
  year : exact-nonnegative-integer?
  month : exact-nonnegative-integer?
  day : exact-nonnegative-integer?
  hour : exact-nonnegative-integer?
  minute : exact-nonnegative-integer?
  second : exact-nonnegative-integer?
  nanosecond : exact-nonnegative-integer?
  tz : (or/c exact-integer? #f)
Represents SQL times and timestamps.

The tz field indicates the time zone offset as the number of seconds east of GMT (as in srfi/19). If tz is #f, the time or timestamp does not carry time zone information.

The sql-time and sql-timestamp structures store fractional seconds to nanosecond precision for compatibility with srfi/19. Note, however, that database systems generally do not support nanosecond precision; PostgreSQL, for example, only supports microsecond precision.

Examples:
> (query-value pgc "select date '25-dec-1980'")

(sql-date 1980 12 25)

> (query-value pgc "select time '7:30'")

(sql-time 7 30 0 0 #f)

> (query-value pgc "select timestamp 'epoch'")

(sql-timestamp 1970 1 1 0 0 0 0 #f)

> (query-value pgc "select timestamp with time zone 'epoch'")

(sql-timestamp 1970 1 1 0 0 0 0 0)

struct

(struct sql-interval (years
    months
    days
    hours
    minutes
    seconds
    nanoseconds))
  years : exact-integer?
  months : exact-integer?
  days : exact-integer?
  hours : exact-integer?
  minutes : exact-integer?
  seconds : exact-integer?
  nanoseconds : exact-integer?
Represents lengths of time. Intervals are normalized to satisfy the following constraints:
  • years and months have the same sign

  • months ranges from -11 to 11

  • days, hours, minutes, seconds, and nanoseconds all have the same sign

  • hours ranges from -23 to 23

  • minutes and seconds range from -59 to 59

  • nanoseconds ranges from (- (sub1 #e1e9)) to (sub1 #e1e9)

That is, an interval consists of two groups of components: year-month and day-time, and normalization is done only within groups. In fact, the SQL standard recognizes those two types of intervals separately (see sql-year-month-interval? and sql-day-time-interval?, below), and does not permit combining them. Intervals such as 1 month 3 days are a PostgreSQL extension.

procedure

(sql-year-month-interval? x) → boolean?

  x : any/c
Returns #t if x is a sql-interval value where the days, hours, minutes, seconds, and nanoseconds fields are zero.

procedure

(sql-day-time-interval? x) → boolean?

  x : any/c
Returns #t if x is a sql-interval value where the years and months fields are zero.

procedure

(sql-interval->sql-time interval [failure]) → any

  interval : sql-interval?
  failure : any/c = (lambda () (error ....))
If interval is a day-time interval that represents a time of day, returns the corresponding sql-time value. In particular, the following must be true:
  • hours, minutes, seconds, and nanoseconds must all be non-negative

  • hours must be between 0 and 23

The corresponding constraints on minutes, etc are enforced by the constructor.

If interval is out of range, the failure value is called, if it is a procedure, or returned, otherwise.

procedure

(sql-time->sql-interval time) → sql-day-time-interval?

  time : sql-time?
Converts time to an interval. If time has time-zone information, it is ignored.

4.2.3 Bits🔗ℹ

The BIT and BIT VARYING (VARBIT) SQL types are represented by bit-vectors (data/bit-vector).

The following functions are provided for backwards compatibility. They are deprecated and will be removed in a future release of Racket.

procedure

(make-sql-bits len) → sql-bits?

  len : exact-nonnegative-integer?

procedure

(sql-bits? v) → boolean?

  v : any/c

procedure

(sql-bits-length b) → exact-nonnegative-integer?

  b : sql-bits?

procedure

(sql-bits-ref b i) → boolean?

  b : sql-bits?
  i : exact-nonnegative-integer?

procedure

(sql-bits-set! b i v) → void?

  b : sql-bits?
  i : exact-nonnegative-integer?
  v : boolean?

procedure

(sql-bits->list b) → (listof boolean?)

  b : sql-bits?

procedure

(sql-bits->string b) → string?

  b : sql-bits?

procedure

(list->sql-bits lst) → sql-bits?

  lst : (listof boolean?)

procedure

(string->sql-bits s) → sql-bits?

  s : string?
Deprecated; use data/bit-vector instead.