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

cast-scalarclj

(cast-scalar v
             type-str
             {:keys [explicit? parse-timestamp resolve-regclass
                     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. :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`.
  :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

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

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