// CAST (spec M4d2).
///|
/// The width of an integer target: TINYINT, SMALLINT, INT, BIGINT.
pub(all) enum IntWidth {
Bits8
Bits16
Bits32
Bits64
} derive(Eq, Debug)
///|
/// REAL (binary32) or DOUBLE PRECISION (binary64).
pub(all) enum FloatWidth {
Real
Double
} derive(Eq, Debug)
///|
/// The scale of a DECIMAL target: a bare DECIMAL keeps the scale of the
/// value (rounded to 38 digits); DECIMAL(p, s) has scale s.
pub(all) enum Scale {
Unconstrained
Fixed(precision~ : Int32, scale~ : Int32)
} derive(Eq, Debug)
///|
/// A character string target. Lengths are in code points.
pub(all) enum TextKind {
Char(Int32)
Varchar(Int32)
String
} derive(Eq, Debug)
///|
/// The target of a CAST, resolved by lowering (spec M4d2 §3.1). `Map` keeps
/// the targets of its keys and values and the declared map types, because a
/// target cannot tell NUMERIC from DECIMAL and a `MapType` can.
pub(all) enum CastTarget {
Any
Bool
Integer(IntWidth)
Decimal(Scale)
Float(FloatWidth)
Text(TextKind)
Clob
Blob
Date
Time(precision~ : Int32?, zone~ : Bool)
Timestamp(precision~ : Int32?, zone~ : Bool)
Interval(@ast.IntervalQualifier)
List
Bag
Tuple
Map(
key~ : CastTarget,
key_type~ : @value.MapType,
value~ : CastTarget,
value_type~ : @value.MapType
)
} derive(Eq, Debug)
///|
/// The session time zone displacement in minutes east of UTC, −18:00 to
/// +18:00 (spec M4d2 §4.4).
pub struct ZoneOffset {
minutes : Int32
} derive(Eq, Debug)
///|
/// The zero-minute offset, UTC.
pub fn ZoneOffset::utc() -> ZoneOffset {
{ minutes: 0, }
}
///|
/// The displacement of `minutes` east of UTC, or `None` outside −18:00 to
/// +18:00.
#alias(new, deprecated="Use ZoneOffset::from_minutes instead")
pub fn ZoneOffset::from_minutes(minutes : Int32) -> ZoneOffset? {
if minutes >= -1080 && minutes <= 1080 {
Some({ minutes, })
} else {
None
}
}
///|
/// The SQL name of a value's kind, for messages.
fn kind_name(x : @value.Value) -> String {
match x {
Null => "NULL"
Missing => "MISSING"
Bool(_) => "BOOL"
Int(_) | BigInt(_) => "INT"
Float(_) => "FLOAT"
Decimal(_) => "DECIMAL"
String(_) => "STRING"
Clob(_) => "CLOB"
Blob(_) => "BLOB"
Date(_) => "DATE"
Time(_) => "TIME"
Timestamp(_) => "TIMESTAMP"
IntervalYM(_) | IntervalDT(_) => "INTERVAL"
List(_) => "LIST"
Bag(_) => "BAG"
Tuple(_) => "TUPLE"
Map(_) => "MAP"
Graph(_) => "GRAPH"
Ion(_) => "ION"
}
}
///|
/// The SQL name of a target, for messages.
fn target_name(t : CastTarget) -> String {
match t {
Any => "ANY"
Bool => "BOOL"
Integer(Bits8) => "TINYINT"
Integer(Bits16) => "SMALLINT"
Integer(Bits32) => "INT"
Integer(Bits64) => "BIGINT"
Decimal(Unconstrained) => "DECIMAL"
Decimal(Fixed(precision~, scale~)) => "DECIMAL(\{precision},\{scale})"
Float(Real) => "REAL"
Float(Double) => "DOUBLE PRECISION"
Text(Char(n)) => "CHAR(\{n})"
Text(Varchar(n)) => "VARCHAR(\{n})"
Text(String) => "STRING"
Clob => "CLOB"
Blob => "BLOB"
Date => "DATE"
Time(zone~, ..) => if zone { "TIME WITH TIME ZONE" } else { "TIME" }
Timestamp(zone~, ..) =>
if zone {
"TIMESTAMP WITH TIME ZONE"
} else {
"TIMESTAMP"
}
Interval(_) => "INTERVAL"
List => "LIST"
Bag => "BAG"
Tuple => "TUPLE"
Map(..) => "MAP"
}
}
///|
/// `CAST(v AS target)` (spec M4d2 §4.2): an Ion value is unboxed; NULL and
/// MISSING stay as they are; ANY gives the operand; then the target group
/// decides by the kind of the value. `zone` is the session time zone.
pub fn cast(
v : @value.Value,
target : CastTarget,
zone~ : ZoneOffset = ZoneOffset::utc(),
today~ : () -> @value.Date raise OpFailure = no_clock_today,
) -> @value.Value raise OpFailure {
let x = boxed(v)
if x is (Null | Missing) {
return x
}
match target {
Any => v
Bool => to_bool(x)
Integer(width) => to_integer(x, width)
Decimal(scale) => to_decimal_target(x, scale)
Float(width) => to_float_target(x, width)
Text(kind) => to_text(x, kind)
Clob | Blob => to_lob(x, target)
Date => to_date(x, zone)
Time(precision~, zone=with_zone) => to_time(x, precision, with_zone, zone)
Timestamp(precision~, zone=with_zone) =>
to_timestamp(x, precision, with_zone, zone, today)
Interval(q) => to_interval(x, q)
List | Bag | Tuple => to_collection(x, target)
Map(key~, key_type~, value~, value_type~) =>
to_map(x, key, key_type, value, value_type, zone, today)
}
}
///|
/// The date CAST of TIME to TIMESTAMP takes when the evaluator gives no
/// clock (spec session-clock §5.6).
fn no_clock_today() -> @value.Date raise OpFailure {
raise DataFailure(
"CAST of TIME to TIMESTAMP needs a clock: pass clock~ to evaluate",
)
}