jade-sql 0.6.0 → 0.8.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/README.md +16 -10
- data/docs/building.md +398 -96
- data/docs/running.md +37 -17
- data/exe/jade-sql +37 -9
- data/lib/jade-sql/bin/generate_schema.rb +655 -44
- data/lib/jade-sql/compiler/assignable.rb +146 -0
- data/lib/jade-sql/compiler/columns.rb +220 -0
- data/lib/jade-sql/compiler/errors.rb +82 -0
- data/lib/jade-sql/compiler/selectable.rb +76 -0
- data/lib/jade-sql/compiler.rb +34 -0
- data/lib/jade-sql/runtime.rb +26 -12
- data/lib/jade-sql/schema_drift.rb +115 -0
- data/lib/jade-sql/sql/expr.jd +65 -0
- data/lib/jade-sql/sql/json.jd +154 -0
- data/lib/jade-sql/sql/query.jd +284 -92
- data/lib/jade-sql/sql/write.jd +625 -0
- data/lib/jade-sql/sql.jd +339 -89
- data/lib/jade-sql/tasks.rake +41 -8
- data/lib/jade-sql/version.rb +1 -1
- data/lib/jade-sql.rb +8 -7
- metadata +16 -8
- data/lib/jade-sql/sql/mutation.jd +0 -329
data/lib/jade-sql/tasks.rake
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'jade-sql/bin/generate_schema'
|
|
2
|
+
require 'jade-sql/schema_drift'
|
|
2
3
|
|
|
3
4
|
namespace :jade do
|
|
4
5
|
desc "Generate schema.jd from db/structure.sql (INPUT, OUTPUT, TABLES, COLUMNS, MODULE)"
|
|
@@ -9,15 +10,47 @@ namespace :jade do
|
|
|
9
10
|
columns = parse_columns_env(ENV['COLUMNS'])
|
|
10
11
|
module_name = ENV['MODULE'] || 'Schema'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
output,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
JadeSql::SchemaGenerator
|
|
14
|
+
.generate(File.read(input), tables:, columns:, module_name:)
|
|
15
|
+
.map { |name, source| write_module(output, module_name, name, source) }
|
|
16
|
+
.then { puts "wrote #{it.join(', ')}" }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Every module the generator would write, read back if it is there. A file
|
|
20
|
+
# left behind for a module no longer generated is not found this way, which
|
|
21
|
+
# is the lesser problem: a missing one breaks the build, a stale one is dead
|
|
22
|
+
# code the compiler still reads.
|
|
23
|
+
def on_disk_modules(generated, root_module, output)
|
|
24
|
+
generated.keys.to_h do |name|
|
|
25
|
+
JadeSql::SchemaGenerator
|
|
26
|
+
.module_path(root_module, name, output)
|
|
27
|
+
.then { [name, File.exist?(it) ? File.read(it) : nil] }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def write_module(output, root_module, name, source)
|
|
32
|
+
JadeSql::SchemaGenerator
|
|
33
|
+
.module_path(root_module, name, output)
|
|
34
|
+
.tap { FileUtils.mkdir_p(File.dirname(it)) }
|
|
35
|
+
.tap { File.write(it, source) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
namespace :schema do
|
|
39
|
+
desc "Fail if schema.jd no longer matches db/structure.sql (INPUT, OUTPUT, TABLES, COLUMNS, MODULE)"
|
|
40
|
+
task :check do
|
|
41
|
+
input = ENV['INPUT'] || 'db/structure.sql'
|
|
42
|
+
output = ENV['OUTPUT'] || 'app/jade/schema.jd'
|
|
43
|
+
tables = ENV['TABLES']&.split(',')&.map(&:strip)&.reject(&:empty?)
|
|
44
|
+
columns = parse_columns_env(ENV['COLUMNS'])
|
|
45
|
+
module_name = ENV['MODULE'] || 'Schema'
|
|
46
|
+
|
|
47
|
+
abort "#{output} does not exist. Generate it with `rake jade:schema`." unless File.exist?(output)
|
|
19
48
|
|
|
20
|
-
|
|
49
|
+
JadeSql::SchemaGenerator
|
|
50
|
+
.generate(File.read(input), tables:, columns:, module_name:)
|
|
51
|
+
.then { JadeSql::SchemaDrift.between(it, on_disk_modules(it, module_name, output)) }
|
|
52
|
+
.then { it.any? ? abort(it.to_s) : puts("#{output} matches #{input}.") }
|
|
53
|
+
end
|
|
21
54
|
end
|
|
22
55
|
|
|
23
56
|
# COLUMNS="patients:id,age;visits:id,seen_on" — tables separated by `;`,
|
data/lib/jade-sql/version.rb
CHANGED
data/lib/jade-sql.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative 'jade-sql/version'
|
|
|
4
4
|
|
|
5
5
|
Jade.extension(__FILE__)
|
|
6
6
|
|
|
7
|
+
require_relative 'jade-sql/compiler'
|
|
7
8
|
require_relative 'jade-sql/uuid_runtime'
|
|
8
9
|
|
|
9
10
|
# Encoded `Sql.SqlError` values (the `[tag, ...args]` shape produced
|
|
@@ -12,12 +13,12 @@ require_relative 'jade-sql/uuid_runtime'
|
|
|
12
13
|
module JadeSql
|
|
13
14
|
module SqlErrors
|
|
14
15
|
NOT_FOUND = ["NotFound"].freeze
|
|
15
|
-
|
|
16
|
+
TOO_MANY_ROWS = ["TooManyRows"].freeze
|
|
16
17
|
|
|
17
18
|
def self.db_error(msg) = ["DbError", msg]
|
|
18
19
|
def self.not_found = NOT_FOUND
|
|
19
|
-
def self.
|
|
20
|
-
def self.
|
|
20
|
+
def self.too_many_rows = TOO_MANY_ROWS
|
|
21
|
+
def self.unique_violation(name) = ["UniqueViolation", name]
|
|
21
22
|
end
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -26,14 +27,14 @@ module Sql
|
|
|
26
27
|
class Error < StandardError; end
|
|
27
28
|
class DbError < Error; end
|
|
28
29
|
class NotFound < Error; end
|
|
29
|
-
class
|
|
30
|
-
class
|
|
30
|
+
class TooManyRows < Error; end
|
|
31
|
+
class UniqueViolation < Error; end
|
|
31
32
|
|
|
32
33
|
BY_TAG = {
|
|
33
34
|
"DbError" => DbError,
|
|
34
35
|
"NotFound" => NotFound,
|
|
35
|
-
"
|
|
36
|
-
"
|
|
36
|
+
"TooManyRows" => TooManyRows,
|
|
37
|
+
"UniqueViolation" => UniqueViolation,
|
|
37
38
|
}.freeze
|
|
38
39
|
end
|
|
39
40
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jade-sql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- agustin
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-09-10 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: jade-lang
|
|
@@ -15,17 +15,17 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.10.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.
|
|
26
|
-
description: Query and
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
version: 0.10.0
|
|
26
|
+
description: Query and write builders, schema generation from db/structure.sql, and
|
|
27
|
+
an ActiveRecord-backed runtime for the Jade language. Renders typed queries to (String,
|
|
28
|
+
List(Value)) and decodes rows into Jade structs.
|
|
29
29
|
email:
|
|
30
30
|
- agustincornu@fastmail.com
|
|
31
31
|
executables:
|
|
@@ -40,12 +40,20 @@ files:
|
|
|
40
40
|
- exe/jade-sql
|
|
41
41
|
- lib/jade-sql.rb
|
|
42
42
|
- lib/jade-sql/bin/generate_schema.rb
|
|
43
|
+
- lib/jade-sql/compiler.rb
|
|
44
|
+
- lib/jade-sql/compiler/assignable.rb
|
|
45
|
+
- lib/jade-sql/compiler/columns.rb
|
|
46
|
+
- lib/jade-sql/compiler/errors.rb
|
|
47
|
+
- lib/jade-sql/compiler/selectable.rb
|
|
43
48
|
- lib/jade-sql/runtime.rb
|
|
49
|
+
- lib/jade-sql/schema_drift.rb
|
|
44
50
|
- lib/jade-sql/sql.jd
|
|
51
|
+
- lib/jade-sql/sql/expr.jd
|
|
52
|
+
- lib/jade-sql/sql/json.jd
|
|
45
53
|
- lib/jade-sql/sql/loader.jd
|
|
46
|
-
- lib/jade-sql/sql/mutation.jd
|
|
47
54
|
- lib/jade-sql/sql/query.jd
|
|
48
55
|
- lib/jade-sql/sql/uuid.jd
|
|
56
|
+
- lib/jade-sql/sql/write.jd
|
|
49
57
|
- lib/jade-sql/tasks.rake
|
|
50
58
|
- lib/jade-sql/uuid_runtime.rb
|
|
51
59
|
- lib/jade-sql/version.rb
|
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
module Sql.Mutation exposing (
|
|
2
|
-
Mutation,
|
|
3
|
-
delete,
|
|
4
|
-
delete_all,
|
|
5
|
-
insert,
|
|
6
|
-
insert_all,
|
|
7
|
-
returning,
|
|
8
|
-
timestamped,
|
|
9
|
-
to_sql,
|
|
10
|
-
update,
|
|
11
|
-
update_all,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
import Sql exposing (
|
|
15
|
-
Assignment(..),
|
|
16
|
-
Expr(..),
|
|
17
|
-
Renderable,
|
|
18
|
-
Selector(..),
|
|
19
|
-
Table,
|
|
20
|
-
TableRef(..),
|
|
21
|
-
pk_values,
|
|
22
|
-
set_,
|
|
23
|
-
to_assigns,
|
|
24
|
-
)
|
|
25
|
-
import Sql.Query exposing (Q)
|
|
26
|
-
import Decode exposing (Value)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
type MutationKind
|
|
30
|
-
= InsertK
|
|
31
|
-
| UpdateK
|
|
32
|
-
| DeleteK
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
struct Mutation(ret, c) = {
|
|
36
|
-
kind: MutationKind,
|
|
37
|
-
table: TableRef,
|
|
38
|
-
cols: c,
|
|
39
|
-
rows: List(List(Assignment)),
|
|
40
|
-
wheres: List(Expr(Bool)),
|
|
41
|
-
returning_selector: Selector(ret)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def insert(value: a, t: Table(c, m)) -> Mutation(Int, c)
|
|
46
|
-
Mutation(
|
|
47
|
-
InsertK,
|
|
48
|
-
TableRef(t.name, t.alias_),
|
|
49
|
-
t.alias_ |> t.cols,
|
|
50
|
-
[to_assigns(value)],
|
|
51
|
-
[],
|
|
52
|
-
Selector([], []),
|
|
53
|
-
)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def insert_all(values: List(a), t: Table(c, m)) -> Mutation(Int, c)
|
|
58
|
-
Mutation(
|
|
59
|
-
InsertK,
|
|
60
|
-
TableRef(t.name, t.alias_),
|
|
61
|
-
t.alias_ |> t.cols,
|
|
62
|
-
List.map(values, (v) -> { to_assigns(v) }),
|
|
63
|
-
[],
|
|
64
|
-
Selector([], []),
|
|
65
|
-
)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def update(value: a, t: Table(c, m)) -> Mutation(Int, c)
|
|
70
|
-
pk_pred = pk_predicate(t.pk_columns, pk_values(value))
|
|
71
|
-
|
|
72
|
-
Mutation(
|
|
73
|
-
UpdateK,
|
|
74
|
-
TableRef(t.name, t.alias_),
|
|
75
|
-
t.alias_ |> t.cols,
|
|
76
|
-
[to_assigns(value)],
|
|
77
|
-
[pk_pred],
|
|
78
|
-
Selector([], []),
|
|
79
|
-
)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def delete(value: a, t: Table(c, m)) -> Mutation(Int, c)
|
|
84
|
-
pk_pred = pk_predicate(t.pk_columns, pk_values(value))
|
|
85
|
-
|
|
86
|
-
Mutation(
|
|
87
|
-
DeleteK,
|
|
88
|
-
TableRef(t.name, t.alias_),
|
|
89
|
-
t.alias_ |> t.cols,
|
|
90
|
-
[],
|
|
91
|
-
[pk_pred],
|
|
92
|
-
Selector([], []),
|
|
93
|
-
)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def update_all(
|
|
98
|
-
t: Table(c, m),
|
|
99
|
-
pred_fn: c -> Expr(Bool),
|
|
100
|
-
build: c -> List(Assignment),
|
|
101
|
-
) -> Mutation(Int, c)
|
|
102
|
-
cols_ = t.alias_ |> t.cols
|
|
103
|
-
pred = cols_ |> pred_fn
|
|
104
|
-
|
|
105
|
-
Mutation(
|
|
106
|
-
UpdateK,
|
|
107
|
-
TableRef(t.name, t.alias_),
|
|
108
|
-
cols_,
|
|
109
|
-
[cols_ |> build],
|
|
110
|
-
[pred],
|
|
111
|
-
Selector([], []),
|
|
112
|
-
)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def delete_all(t: Table(c, m), pred_fn: c -> Expr(Bool)) -> Mutation(Int, c)
|
|
117
|
-
cols_ = t.alias_ |> t.cols
|
|
118
|
-
pred = cols_ |> pred_fn
|
|
119
|
-
|
|
120
|
-
Mutation(
|
|
121
|
-
DeleteK,
|
|
122
|
-
TableRef(t.name, t.alias_),
|
|
123
|
-
cols_,
|
|
124
|
-
[],
|
|
125
|
-
[pred],
|
|
126
|
-
Selector([], []),
|
|
127
|
-
)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def returning(m: Mutation(a, c), build: c -> Q(Selector(b))) -> Mutation(b, c)
|
|
132
|
-
Mutation(m.kind, m.table, m.cols, m.rows, m.wheres, build(m.cols).result)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# Opt-in ActiveRecord-style timestamps: fills created_at + updated_at on
|
|
137
|
-
# insert and updated_at on update, with the app clock at execute time.
|
|
138
|
-
# The "$JADE_SQL_NOW$" token carries no param; the runtime swaps it for a
|
|
139
|
-
# single UTC timestamp literal per statement (so created_at == updated_at).
|
|
140
|
-
# A plain `insert`/`update` without this stays timestamp-free.
|
|
141
|
-
def timestamped(m: Mutation(ret, c)) -> Mutation(ret, c)
|
|
142
|
-
case m.kind
|
|
143
|
-
in InsertK then with_rows(m, List.map(m.rows, append_insert_stamps))
|
|
144
|
-
in UpdateK then with_rows(m, List.map(m.rows, append_update_stamps))
|
|
145
|
-
in DeleteK then m
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def append_insert_stamps(row: List(Assignment)) -> List(Assignment)
|
|
151
|
-
row ++ [now_assignment("created_at"), now_assignment("updated_at")]
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def append_update_stamps(row: List(Assignment)) -> List(Assignment)
|
|
156
|
-
row ++ [now_assignment("updated_at")]
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def now_assignment(col: String) -> Assignment
|
|
161
|
-
Assignment(col, "$JADE_SQL_NOW$", [])
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def with_rows(m: Mutation(ret, c), rows: List(List(Assignment))) -> Mutation(ret, c)
|
|
166
|
-
Mutation(m.kind, m.table, m.cols, rows, m.wheres, m.returning_selector)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def pk_predicate(cols: List(String), values: List(Value)) -> Expr(Bool)
|
|
171
|
-
sql = cols
|
|
172
|
-
|> List.map((col) -> { col ++ " = ?" })
|
|
173
|
-
|> String.join(" AND ")
|
|
174
|
-
|
|
175
|
-
Expr(sql, values)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def render_row(row: List(Assignment)) -> String
|
|
180
|
-
cols_part = row
|
|
181
|
-
|> List.map((a) -> { a.value_sql })
|
|
182
|
-
|> String.join(", ")
|
|
183
|
-
|
|
184
|
-
"(" ++ cols_part ++ ")"
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def returning_clause(s: Selector(ret)) -> String
|
|
189
|
-
List.empty?(s.columns_sql) ? "" : " RETURNING " ++ String.join(s.columns_sql, ", ")
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def insert_cols_str(rows: List(List(Assignment))) -> String
|
|
194
|
-
case rows
|
|
195
|
-
in [] then ""
|
|
196
|
-
in [first | _]
|
|
197
|
-
"(" ++ String.join(List.map(first, (a) -> { a.col }), ", ") ++ ")"
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def insert_params(rows: List(List(Assignment))) -> List(Value)
|
|
203
|
-
List.fold(
|
|
204
|
-
rows,
|
|
205
|
-
[],
|
|
206
|
-
(acc, row) -> { acc ++ List.fold(row, [], (acc2, a) -> { acc2 ++ a.params }) },
|
|
207
|
-
)
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def set_sqls_for(rows: List(List(Assignment))) -> List(String)
|
|
212
|
-
case rows
|
|
213
|
-
in [first | _] then List.map(first, (a) -> { a.col ++ " = " ++ a.value_sql })
|
|
214
|
-
in [] then []
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def set_params_for(rows: List(List(Assignment))) -> List(Value)
|
|
220
|
-
case rows
|
|
221
|
-
in [first | _] then List.fold(first, [], (acc, a) -> { acc ++ a.params })
|
|
222
|
-
in [] then []
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def where_sql(wheres: List(Expr(Bool))) -> String
|
|
228
|
-
List.empty?(wheres)
|
|
229
|
-
? ""
|
|
230
|
-
: " WHERE " ++ String.join(List.map(wheres, (w) -> { w.sql }), " AND ")
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def where_params(wheres: List(Expr(Bool))) -> List(Value)
|
|
235
|
-
List.fold(wheres, [], (acc, w) -> { acc ++ w.params })
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def strip_table_alias(sql: String, alias_: String) -> String
|
|
240
|
-
String.replace(sql, alias_ ++ ".", "")
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def render_insert(m: Mutation(ret, c)) -> (String, List(Value))
|
|
245
|
-
cols_str = insert_cols_str(m.rows)
|
|
246
|
-
values_str = m.rows
|
|
247
|
-
|> List.map(render_row)
|
|
248
|
-
|> String.join(", ")
|
|
249
|
-
returning_ = strip_table_alias(returning_clause(m.returning_selector), m.table.alias_)
|
|
250
|
-
sql = "INSERT INTO "
|
|
251
|
-
++ m.table.name
|
|
252
|
-
++ " "
|
|
253
|
-
++ cols_str
|
|
254
|
-
++ " VALUES "
|
|
255
|
-
++ values_str
|
|
256
|
-
++ returning_
|
|
257
|
-
|
|
258
|
-
Tuple.Tuple2(sql, insert_params(m.rows) ++ m.returning_selector.params)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def render_update(m: Mutation(ret, c)) -> (String, List(Value))
|
|
263
|
-
sets = set_sqls_for(m.rows)
|
|
264
|
-
|
|
265
|
-
List.empty?(sets) ? render_no_op_update(m) : render_update_set(m, sets)
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
def render_no_op_update(m: Mutation(ret, c)) -> (String, List(Value))
|
|
270
|
-
where = strip_table_alias(where_sql(m.wheres), m.table.alias_)
|
|
271
|
-
cols = List.empty?(m.returning_selector.columns_sql)
|
|
272
|
-
? "1"
|
|
273
|
-
: strip_table_alias(
|
|
274
|
-
String.join(m.returning_selector.columns_sql, ", "),
|
|
275
|
-
m.table.alias_,
|
|
276
|
-
)
|
|
277
|
-
sql = "SELECT " ++ cols ++ " FROM " ++ m.table.name ++ where
|
|
278
|
-
|
|
279
|
-
Tuple.Tuple2(sql, m.returning_selector.params ++ where_params(m.wheres))
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
def render_update_set(
|
|
284
|
-
m: Mutation(ret, c),
|
|
285
|
-
sets: List(String),
|
|
286
|
-
) -> (String, List(Value))
|
|
287
|
-
set_clause = "SET " ++ String.join(sets, ", ")
|
|
288
|
-
where = strip_table_alias(where_sql(m.wheres), m.table.alias_)
|
|
289
|
-
returning_ = strip_table_alias(returning_clause(m.returning_selector), m.table.alias_)
|
|
290
|
-
sql = "UPDATE "
|
|
291
|
-
++ m.table.name
|
|
292
|
-
++ " "
|
|
293
|
-
++ set_clause
|
|
294
|
-
++ where
|
|
295
|
-
++ returning_
|
|
296
|
-
|
|
297
|
-
Tuple.Tuple2(
|
|
298
|
-
sql,
|
|
299
|
-
set_params_for(m.rows) ++ where_params(m.wheres) ++ m.returning_selector.params,
|
|
300
|
-
)
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def render_delete(m: Mutation(ret, c)) -> (String, List(Value))
|
|
307
|
-
where = strip_table_alias(where_sql(m.wheres), m.table.alias_)
|
|
308
|
-
returning_ = strip_table_alias(returning_clause(m.returning_selector), m.table.alias_)
|
|
309
|
-
sql = "DELETE FROM "
|
|
310
|
-
++ m.table.name
|
|
311
|
-
++ where
|
|
312
|
-
++ returning_
|
|
313
|
-
|
|
314
|
-
Tuple.Tuple2(sql, where_params(m.wheres) ++ m.returning_selector.params)
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
def to_sql(m: Mutation(ret, c)) -> (String, List(Value))
|
|
319
|
-
case m.kind
|
|
320
|
-
in InsertK then render_insert(m)
|
|
321
|
-
in UpdateK then render_update(m)
|
|
322
|
-
in DeleteK then render_delete(m)
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
implements Renderable(Mutation(ret, c)) with
|
|
328
|
-
render: to_sql
|
|
329
|
-
end
|