jade-sql 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/building.md +12 -16
- data/lib/jade-sql/bin/generate_schema.rb +1 -1
- data/lib/jade-sql/runtime.rb +8 -8
- data/lib/jade-sql/version.rb +1 -1
- metadata +3 -4
- data/lib/jade-sql/sql/decimal.jd +0 -193
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 203373e2a0789908e800cda5e81acee5883edb436205ec016f281549451218cd
|
|
4
|
+
data.tar.gz: 3338e7d2e8132fdf4ed4de0e8b53658d8a2cce64ee8f13394cc7dca53413906c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f5e68d85a8d74169946d1368aa91ef46fe99c05a6b3250a5f11b44cc0ee13f75c66b3e1a3bbe59febb9c63ebee3f32ea419396444dffb699fa239a211f943821
|
|
7
|
+
data.tar.gz: 4ad41bf4b0b780a78b599bba48bd90f7b14a35dfd2564a38b8153bbeec8dd8a7abd77a08d6c43db9888df49096429702aa31d2a6ceebd606446023d729988dd7
|
data/docs/building.md
CHANGED
|
@@ -28,22 +28,18 @@ bundle exec rake jade:schema \
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Type map: `bigint`/`integer`/`smallint` → `Int`, `numeric`/`decimal` →
|
|
31
|
-
`Decimal` (
|
|
32
|
-
`varchar`/`text`/`char` → `String`, `boolean` → `Bool`,
|
|
33
|
-
`Decode.Value`, `date` → `Calendar.Date`, `timestamp` →
|
|
34
|
-
`uuid` → `Uuid` (from `Sql.Uuid`). Unknown types fail
|
|
35
|
-
table+column name.
|
|
36
|
-
|
|
37
|
-
`numeric`/`decimal` map to `
|
|
38
|
-
(`
|
|
39
|
-
floating-point columns (`double precision`/`real`) map to `Float`.
|
|
40
|
-
isn't mapped yet, though jade's `Bytes` is the natural target.
|
|
41
|
-
|
|
42
|
-
`
|
|
43
|
-
Mirroring `BigDecimal`, `/` can't represent a repeating quotient, so it
|
|
44
|
-
rounds half-up to a default scale (use `div(a, b, scale)` for explicit
|
|
45
|
-
control, `round(d, scale)` to round); division by zero raises. `to_i`
|
|
46
|
-
truncates toward zero and `to_float` converts (lossily, on purpose).
|
|
31
|
+
`Decimal` (jade's stdlib exact decimal), `double precision`/`real` →
|
|
32
|
+
`Float`, `varchar`/`text`/`char` → `String`, `boolean` → `Bool`,
|
|
33
|
+
`jsonb`/`json` → `Decode.Value`, `date` → `Calendar.Date`, `timestamp` →
|
|
34
|
+
`Clock.Instant`, `uuid` → `Uuid` (from `Sql.Uuid`). Unknown types fail
|
|
35
|
+
loudly with the table+column name.
|
|
36
|
+
|
|
37
|
+
`numeric`/`decimal` map to the stdlib `Decimal` — an exact base-10 value
|
|
38
|
+
(`coefficient * 10^exponent`), never `Float`, so no precision is lost.
|
|
39
|
+
Genuine floating-point columns (`double precision`/`real`) map to `Float`.
|
|
40
|
+
`bytea` isn't mapped yet, though jade's `Bytes` is the natural target. See
|
|
41
|
+
jade-lang's `Decimal` for the full API (`of`/`scaled`/`parse`, arithmetic,
|
|
42
|
+
`round`, `to_i`/`to_float`).
|
|
47
43
|
|
|
48
44
|
For each table, the generator emits:
|
|
49
45
|
|
|
@@ -53,7 +53,7 @@ module JadeSql
|
|
|
53
53
|
"Clock.Instant" => "import Clock",
|
|
54
54
|
"Decode.Value" => "import Decode",
|
|
55
55
|
"Uuid" => "import Sql.Uuid exposing(Uuid)",
|
|
56
|
-
"Decimal" => "import
|
|
56
|
+
"Decimal" => "import Decimal exposing(Decimal)",
|
|
57
57
|
}.freeze
|
|
58
58
|
|
|
59
59
|
# Column names that collide with Jade keywords get a trailing underscore
|
data/lib/jade-sql/runtime.rb
CHANGED
|
@@ -82,8 +82,8 @@ module JadeSql
|
|
|
82
82
|
# other List(a) column.
|
|
83
83
|
#
|
|
84
84
|
# numeric/decimal columns come back as ::BigDecimal; the schema generator
|
|
85
|
-
# maps them to
|
|
86
|
-
# "<
|
|
85
|
+
# maps them to jade's stdlib Decimal, whose decoder reads the exact
|
|
86
|
+
# "<coefficient>e<exponent>" wire form. Float would lose precision, so don't.
|
|
87
87
|
def self.coerce_row(row)
|
|
88
88
|
row.transform_values { |v| coerce_value(v) }
|
|
89
89
|
end
|
|
@@ -99,19 +99,19 @@ module JadeSql
|
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
# ::BigDecimal -> "<
|
|
102
|
+
# ::BigDecimal -> "<coefficient>e<exponent>" with value = coeff * 10^exp,
|
|
103
103
|
# exactly (BigDecimal#split gives sign, significant digits, and a base-10
|
|
104
|
-
# exponent). Matches the wire form
|
|
104
|
+
# exponent). Matches the wire form jade's stdlib Decimal decoder parses.
|
|
105
105
|
#
|
|
106
|
-
# 'NaN'/'Infinity'::numeric are legal Postgres values that
|
|
107
|
-
#
|
|
106
|
+
# 'NaN'/'Infinity'::numeric are legal Postgres values that Decimal can't
|
|
107
|
+
# represent; fail loudly rather than silently decode them as 0.
|
|
108
108
|
def self.decimal_wire(v)
|
|
109
109
|
raise ArgumentError, "non-finite numeric: #{v}" unless v.finite?
|
|
110
110
|
|
|
111
111
|
sign, digits, _base, exp = v.split
|
|
112
|
-
|
|
112
|
+
coefficient = sign * digits.to_i
|
|
113
113
|
exponent = exp - digits.length
|
|
114
|
-
"#{
|
|
114
|
+
"#{coefficient}e#{exponent}"
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
# PG arrays render as `{}`, `{a,b,c}`, `{"a,b","c"}`, with NULL as
|
data/lib/jade-sql/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jade-sql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- agustin
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.2.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.2.0
|
|
26
26
|
description: Query and mutation builders, schema generation from db/structure.sql,
|
|
27
27
|
and an ActiveRecord-backed runtime for the Jade language. Renders typed queries
|
|
28
28
|
to (String, List(Value)) and decodes rows into Jade structs.
|
|
@@ -42,7 +42,6 @@ files:
|
|
|
42
42
|
- lib/jade-sql/bin/generate_schema.rb
|
|
43
43
|
- lib/jade-sql/runtime.rb
|
|
44
44
|
- lib/jade-sql/sql.jd
|
|
45
|
-
- lib/jade-sql/sql/decimal.jd
|
|
46
45
|
- lib/jade-sql/sql/loader.jd
|
|
47
46
|
- lib/jade-sql/sql/mutation.jd
|
|
48
47
|
- lib/jade-sql/sql/query.jd
|
data/lib/jade-sql/sql/decimal.jd
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
module Sql.Decimal exposing (
|
|
2
|
-
Decimal,
|
|
3
|
-
decimal,
|
|
4
|
-
div,
|
|
5
|
-
exponent,
|
|
6
|
-
mantissa,
|
|
7
|
-
parse,
|
|
8
|
-
round,
|
|
9
|
-
to_float,
|
|
10
|
-
to_i,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
import Decode exposing (Decodable, Decoder)
|
|
14
|
-
import Encode exposing (Encodable)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# An exact base-10 decimal: value = mantissa * 10 ^ exponent. Lossless for
|
|
18
|
-
# Postgres numeric/decimal — no Float rounding. The wire form is
|
|
19
|
-
# "<mantissa>e<exponent>" (e.g. "175e-3"), which Postgres casts to numeric
|
|
20
|
-
# exactly and which decodes back without parsing a decimal point.
|
|
21
|
-
type Decimal = Decimal(Int, Int)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def decimal(m: Int, e: Int) -> Decimal
|
|
25
|
-
Decimal(m, e)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def mantissa(d: Decimal) -> Int
|
|
30
|
-
Decimal(m, _) = d
|
|
31
|
-
|
|
32
|
-
m
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def exponent(d: Decimal) -> Int
|
|
37
|
-
Decimal(_, e) = d
|
|
38
|
-
|
|
39
|
-
e
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def pow10(n: Int) -> Int
|
|
44
|
-
n <= 0 ? 1 : 10 * pow10(n - 1)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def abs(x: Int) -> Int
|
|
49
|
-
x < 0 ? 0 - x : x
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# Integer division, rounding ties half away from zero — i.e. Java's HALF_UP
|
|
54
|
-
# and Ruby BigDecimal's default mode (2.5 -> 3, -2.5 -> -3). Rounds the
|
|
55
|
-
# magnitude, then re-applies the sign. A zero `den` raises through Int `/`.
|
|
56
|
-
def round_div(num: Int, den: Int) -> Int
|
|
57
|
-
negative = not ((num < 0) == (den < 0))
|
|
58
|
-
n = abs(num)
|
|
59
|
-
d = abs(den)
|
|
60
|
-
q = n / d
|
|
61
|
-
r = n - q * d
|
|
62
|
-
rounded = (2 * r) >= d ? q + 1 : q
|
|
63
|
-
|
|
64
|
-
negative ? 0 - rounded : rounded
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def trunc_div(num: Int, den: Int) -> Int
|
|
69
|
-
q = abs(num) / abs(den)
|
|
70
|
-
|
|
71
|
-
num < 0 ? 0 - q : q
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# `+` `-` `*` are exact. `+`/`-` line the operands up on the smaller exponent
|
|
76
|
-
# first; `*` adds exponents.
|
|
77
|
-
def add(a: Decimal, b: Decimal) -> Decimal
|
|
78
|
-
Decimal(ma, ea) = a
|
|
79
|
-
Decimal(mb, eb) = b
|
|
80
|
-
e = ea < eb ? ea : eb
|
|
81
|
-
|
|
82
|
-
Decimal((ma * pow10(ea - e)) + (mb * pow10(eb - e)), e)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def sub(a: Decimal, b: Decimal) -> Decimal
|
|
87
|
-
Decimal(ma, ea) = a
|
|
88
|
-
Decimal(mb, eb) = b
|
|
89
|
-
e = ea < eb ? ea : eb
|
|
90
|
-
|
|
91
|
-
Decimal((ma * pow10(ea - e)) - (mb * pow10(eb - e)), e)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def mul(a: Decimal, b: Decimal) -> Decimal
|
|
96
|
-
Decimal(ma, ea) = a
|
|
97
|
-
Decimal(mb, eb) = b
|
|
98
|
-
|
|
99
|
-
Decimal(ma * mb, ea + eb)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# a / b rounded half-up to `scale` decimal places — like Java's
|
|
104
|
-
# BigDecimal.divide(divisor, scale, HALF_UP). Division by zero raises.
|
|
105
|
-
def div(a: Decimal, b: Decimal, scale: Int) -> Decimal
|
|
106
|
-
Decimal(ma, ea) = a
|
|
107
|
-
Decimal(mb, eb) = b
|
|
108
|
-
k = (ea - eb) + scale
|
|
109
|
-
num = k >= 0 ? ma * pow10(k) : ma
|
|
110
|
-
den = k >= 0 ? mb : mb * pow10(0 - k)
|
|
111
|
-
|
|
112
|
-
Decimal(round_div(num, den), 0 - scale)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# The Numeric `/`: like Ruby's BigDecimal `/`, it can't be exact for a
|
|
117
|
-
# repeating quotient (1/3), so it rounds half-up to a generous default scale.
|
|
118
|
-
def divide(a: Decimal, b: Decimal) -> Decimal
|
|
119
|
-
div(a, b, 32)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
implements Numeric(Decimal) with
|
|
124
|
-
(+): add,
|
|
125
|
-
(-): sub,
|
|
126
|
-
(*): mul,
|
|
127
|
-
(/): divide
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# Round to `scale` decimal places, half-up. Fewer places than the value
|
|
132
|
-
# already has are left untouched.
|
|
133
|
-
def round(d: Decimal, scale: Int) -> Decimal
|
|
134
|
-
Decimal(m, e) = d
|
|
135
|
-
shift = e + scale
|
|
136
|
-
|
|
137
|
-
shift >= 0 ? d : Decimal(round_div(m, pow10(0 - shift)), 0 - scale)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# Truncates toward zero (drops the fractional part).
|
|
142
|
-
def to_i(d: Decimal) -> Int
|
|
143
|
-
Decimal(m, e) = d
|
|
144
|
-
|
|
145
|
-
e >= 0 ? m * pow10(e) : trunc_div(m, pow10(0 - e))
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def to_float(d: Decimal) -> Float
|
|
150
|
-
Decimal(m, e) = d
|
|
151
|
-
|
|
152
|
-
e >= 0
|
|
153
|
-
? Basics.to_float(m * pow10(e))
|
|
154
|
-
: Basics.to_float(m) / Basics.to_float(pow10(0 - e))
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def to_wire(d: Decimal) -> String
|
|
159
|
-
Decimal(m, e) = d
|
|
160
|
-
|
|
161
|
-
String.from_int(m) ++ "e" ++ String.from_int(e)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def parse(s: String) -> Maybe(Decimal)
|
|
166
|
-
case String.split(s, "e")
|
|
167
|
-
in [m, e]
|
|
168
|
-
String.to_int(m)
|
|
169
|
-
|> Maybe.and_then((mi) -> {
|
|
170
|
-
String.to_int(e) |> Maybe.map((ei) -> { Decimal(mi, ei) })
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
else Nothing
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def decimal_decoder(s: String) -> Decoder(Decimal)
|
|
179
|
-
case parse(s)
|
|
180
|
-
in Just(d) then Decode.succeed(d)
|
|
181
|
-
in Nothing then Decode.fail("invalid decimal: " ++ s)
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
implements Decodable(Decimal) with
|
|
187
|
-
decoder: -> { Decode.string |> Decode.and_then(decimal_decoder) }
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
implements Encodable(Decimal) with
|
|
192
|
-
encoder: (d) -> { Encode.string(to_wire(d)) }
|
|
193
|
-
end
|