Liking cljdoc? Tell your friends :D

datahike.pg.sql.cast

One implementation of CAST(<value> AS <type>).

The same cast semantics used to be written out four times — in sql.clj's table-free literal fast path, in expr.clj's translate-cast, in stmt.clj's apply-sql-cast, and in coerce.clj's INSERT path — each a case/cond over types/cast-category that had drifted from the others. Which copy ran depended on the SHAPE of the expression, not on its meaning, so the same cast could behave three ways:

29::bit(4) → literal fast path → correct (-44)::bit(12) → translate-cast → passed through as -44 '101'::bit(3)::int → nested, another path → read the digits as decimal 101, not 5

Issue #12 hit exactly this for '1'::boolean, and issue #19 hit it again for bit. Adding a branch to one copy fixes one shape.

This namespace holds the value-level semantics — (value, type) → value. Callers keep their own surrounding logic (when to fold at translate time, how to bind a runtime var, how to read a JSqlParser AST node); they just stop reimplementing what a cast MEANS.

parse-timestamp is injected rather than required because the parser lives in expr.clj, which requires this namespace's dependencies — taking it as an argument keeps this namespace a leaf and avoids a load cycle.

One implementation of `CAST(<value> AS <type>)`.

The same cast semantics used to be written out four times — in
`sql.clj`'s table-free literal fast path, in `expr.clj`'s
`translate-cast`, in `stmt.clj`'s `apply-sql-cast`, and in
`coerce.clj`'s INSERT path — each a `case`/`cond` over
`types/cast-category` that had drifted from the others. Which copy ran
depended on the SHAPE of the expression, not on its meaning, so the
same cast could behave three ways:

  29::bit(4)          → literal fast path      → correct
  (-44)::bit(12)      → translate-cast         → passed through as -44
  '101'::bit(3)::int  → nested, another path   → read the digits as
                                                 decimal 101, not 5

Issue #12 hit exactly this for `'1'::boolean`, and issue #19 hit it
again for bit. Adding a branch to one copy fixes one shape.

This namespace holds the value-level semantics — `(value, type) →
value`. Callers keep their own surrounding logic (when to fold at
translate time, how to bind a runtime var, how to read a JSqlParser
AST node); they just stop reimplementing what a cast MEANS.

`parse-timestamp` is injected rather than required because the parser
lives in `expr.clj`, which requires this namespace's dependencies —
taking it as an argument keeps this namespace a leaf and avoids a
load cycle.
raw docstring

apply-numeric-typmodclj

(apply-numeric-typmod v p s)

PostgreSQL's apply_typmod (numeric.c): round to the declared scale, then reject anything whose integer part no longer fits the declared precision.

Both halves were missing. The scale is why 123.456::numeric(10,1) answered 123.456 instead of 123.5, and the precision is why 123456::numeric(5,2) was accepted at all -- 22003 numeric field overflow was never raised on any path.

PostgreSQL's apply_typmod (numeric.c): round to the declared scale,
 then reject anything whose integer part no longer fits the declared
 precision.

 Both halves were missing. The scale is why `123.456::numeric(10,1)`
 answered 123.456 instead of 123.5, and the precision is why
 `123456::numeric(5,2)` was accepted at all -- 22003 numeric field
overflow was never raised on any path.
sourceraw docstring

bad-timestamp!clj

(bad-timestamp! s tz?)

The timestamp counterpart of bad-date!: PostgreSQL tells a value whose date FIELDS are impossible (22008) from text that is not a timestamp at all (22007), and says so with the target's own name.

Public because the INSERT coercion needs the same verdict -- writing a timestamp has to refuse exactly what casting one refuses, and by the same rule.

The timestamp counterpart of `bad-date!`: PostgreSQL tells a value
whose date FIELDS are impossible (22008) from text that is not a
timestamp at all (22007), and says so with the target's own name.

Public because the INSERT coercion needs the same verdict -- writing
a timestamp has to refuse exactly what casting one refuses, and by
the same rule.
sourceraw docstring

cast-scalarclj

(cast-scalar v
             type-str
             {:keys [explicit? parse-timestamp resolve-regclass resolve-regtype
                     prefer-local-datetime? src-oid]
              :or {explicit? true}})

Apply a SQL cast of v to the target named by type-str.

Options: :explicit? — an explicit CAST reshapes silently; an assignment raises instead (matters for bit width coercion). :parse-timestamp — fn String → java.util.Date, from expr.clj. :resolve-regclass— fn String → oid, for ::regclass. :resolve-regtype — fn String → oid, for ::regtype. :prefer-local-datetime? — return a LocalDateTime (microsecond precision) rather than a Date for a timestamp cast. See the :timestamp branch. :src-oid — the OID of the value being cast, when the caller knows it. Only ::text uses it, to tell a date from a timestamp: both are java.util.Date here.

Returns v unchanged for a target this doesn't classify, which is what every call site did before and keeps unknown types passing through rather than erroring.

Apply a SQL cast of `v` to the target named by `type-str`.

Options:
  :explicit?       — an explicit CAST reshapes silently; an assignment
                     raises instead (matters for bit width coercion).
  :parse-timestamp — fn String → java.util.Date, from expr.clj.
  :resolve-regclass— fn String → oid, for `::regclass`.
  :resolve-regtype — fn String → oid, for `::regtype`.
  :prefer-local-datetime? — return a LocalDateTime (microsecond
                     precision) rather than a Date for a timestamp
                     cast. See the :timestamp branch.
  :src-oid         — the OID of the value being cast, when the caller
                     knows it. Only `::text` uses it, to tell a date
                     from a timestamp: both are java.util.Date here.

Returns `v` unchanged for a target this doesn't classify, which is
what every call site did before and keeps unknown types passing
through rather than erroring.
sourceraw docstring

cast-to-bitclj

(cast-to-bit v type-str explicit?)

int / text / bit → bit(n) or bit varying(n).

An integer source keeps the RIGHTMOST n bits and sign-extends on the left (varbit.c:1550), which is why (-44)::bit(12) is 111111010100 and not the digits of -44.

int / text / bit → bit(n) or bit varying(n).

An integer source keeps the RIGHTMOST n bits and sign-extends on the
left (varbit.c:1550), which is why `(-44)::bit(12)` is
`111111010100` and not the digits of -44.
sourceraw docstring

cast-to-floatclj

(cast-to-float v type-str)

float4 and float8. real is a DISTINCT type, not a spelling of double precision: 1.1::real is 1.100000023841858, and a value that does not fit is an error rather than an Infinity.

float4 and float8. `real` is a DISTINCT type, not a spelling of
double precision: `1.1::real` is 1.100000023841858, and a value that
does not fit is an error rather than an Infinity.
sourceraw docstring

cast-to-integerclj

(cast-to-integer v type-str)

Cast to one of PostgreSQL's three integer widths.

Two things this has to do that a plain coerce-numeric … :long does not. It ROUNDS rather than truncates -- and the two source families round DIFFERENTLY, which is not a detail we get to smooth over:

float -> int rint, half to EVEN (float.c dtoi4) numeric-> int half AWAY FROM ZERO (numeric.c round_var)

so 2.5::float8::int is 2 while 2.5::numeric::int is 3. And it RANGE-CHECKS against the target width: every integer target used to collapse to Java long, so 100000::int2 and 99999999999::int4 passed through unchanged where PostgreSQL raises 22003.

Cast to one of PostgreSQL's three integer widths.

Two things this has to do that a plain `coerce-numeric … :long` does
not. It ROUNDS rather than truncates -- and the two source families
round DIFFERENTLY, which is not a detail we get to smooth over:

  float  -> int   rint, half to EVEN          (float.c dtoi4)
  numeric-> int   half AWAY FROM ZERO         (numeric.c round_var)

so `2.5::float8::int` is 2 while `2.5::numeric::int` is 3. And it
RANGE-CHECKS against the target width: every integer target used to
collapse to Java long, so `100000::int2` and `99999999999::int4`
passed through unchanged where PostgreSQL raises 22003.
sourceraw docstring

decode-numeric-dateclj

(decode-numeric-date f1 f2 f3 order)

Three numeric date fields to a LocalDate, per DateStyle's field ORDER -- the half of DateStyle that says whether 8/10/2017 is August 10th or the 8th of October.

PostgreSQL's rules (DecodeDateTime):

  • a leading field of FOUR or more digits is the year, whatever the order, so 2017-08-10 reads the same under MDY, DMY and YMD;
  • otherwise the order assigns the fields, and a two-digit year is mapped to a century;
  • the fields are then CHECKED, not rolled: 13/10/2017 is a month 13 under MDY and an error, while under DMY it is the 13th of October.

Returns nil when the fields do not make a date; the caller decides whether that is 22008 or 22007. Separators do not matter here -- 8/10/2017 and 10-08-2017 differ only in punctuation, and PostgreSQL reads both by order.

Three numeric date fields to a LocalDate, per `DateStyle`'s field
ORDER -- the half of DateStyle that says whether `8/10/2017` is
August 10th or the 8th of October.

PostgreSQL's rules (DecodeDateTime):

  - a leading field of FOUR or more digits is the year, whatever the
    order, so `2017-08-10` reads the same under MDY, DMY and YMD;
  - otherwise the order assigns the fields, and a two-digit year is
    mapped to a century;
  - the fields are then CHECKED, not rolled: `13/10/2017` is a month
    13 under MDY and an error, while under DMY it is the 13th of
    October.

Returns nil when the fields do not make a date; the caller decides
whether that is 22008 or 22007. Separators do not matter here --
`8/10/2017` and `10-08-2017` differ only in punctuation, and
PostgreSQL reads both by order.
sourceraw docstring

month-name->numberclj

(month-name->number t)
source

month-namesclj

source

numeric-typmodclj

(numeric-typmod type-str)

[precision scale] from a numeric(p[,s]) target, or nil for bare numeric. A modifier with no scale means scale 0 -- numeric(10) truncates to an integer, which is easy to miss.

`[precision scale]` from a `numeric(p[,s])` target, or nil for bare
`numeric`. A modifier with no scale means scale 0 -- `numeric(10)`
truncates to an integer, which is easy to miss.
sourceraw docstring

parse-moneyclj

(parse-money v)

Parse PostgreSQL's money input in the C locale.

The upstream regression suite fixes lc_monetary to C. Its input accepts a dollar sign, comma separators, and either a minus sign or parentheses for a negative amount. PostgreSQL stores an int64 count of cents; we retain the equivalent two-scale BigDecimal carrier.

Parse PostgreSQL's `money` input in the C locale.

The upstream regression suite fixes `lc_monetary` to C. Its input
accepts a dollar sign, comma separators, and either a minus sign or
parentheses for a negative amount. PostgreSQL stores an int64 count
of cents; we retain the equivalent two-scale BigDecimal carrier.
sourceraw docstring

special-datetimeclj

(special-datetime s kind)

PostgreSQL's reserved datetime inputs (datetime.c's datetktbl), for the ones whose value does not depend on when the statement runs.

now, today, tomorrow and yesterday are deliberately absent: their value is the statement's, and a cast is constant-folded into a cached plan, so folding one would freeze it -- the same trap a volatile DEFAULT has. They need the deferred treatment now() gets. infinity and -infinity are absent because they need a value to BE, which java.util.Date has no room for without a sentinel convention across storage, text, binary and comparison.

Returns ::none rather than nil so a caller can tell a value that is not special from a special value that is legitimately nil.

PostgreSQL's reserved datetime inputs (datetime.c's `datetktbl`),
for the ones whose value does not depend on when the statement runs.

`now`, `today`, `tomorrow` and `yesterday` are deliberately absent:
their value is the statement's, and a cast is constant-folded into a
cached plan, so folding one would freeze it -- the same trap a
volatile DEFAULT has. They need the deferred treatment `now()` gets.
`infinity` and `-infinity` are absent because they need a value to
BE, which java.util.Date has no room for without a sentinel
convention across storage, text, binary and comparison.

Returns `::none` rather than nil so a caller can tell a value that is
not special from a special value that is legitimately nil.
sourceraw docstring

split-trailing-zoneclj

(split-trailing-zone s)

[head zone-offset] for a datetime literal that carries a numeric zone, else [s nil].

Splitting it out is the whole point: timestamp without time zone keeps the fields and DROPS the zone (datetime.c decodes tzp and then ignores it), while timestamptz converts by it. Reading the literal once and letting the target decide is what keeps the two answers from being parsed by different code.

`[head zone-offset]` for a datetime literal that carries a numeric
zone, else `[s nil]`.

Splitting it out is the whole point: `timestamp without time zone`
keeps the fields and DROPS the zone (datetime.c decodes tzp and then
ignores it), while `timestamptz` converts by it. Reading the literal
once and letting the target decide is what keeps the two answers from
being parsed by different code.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close