aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/library/lux/world/db/sql.lux
blob: 24673eea40335db60e27d26845acb65206c36406 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
... This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
... If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

(.require
 [library
  [lux (.except Source Definition function and or not int)
   [control
    [monad (.only do)]]
   [data
    [number
     ["i" int]]
    ["[0]" text (.use "[1]#[0]" equivalence)
     ["%" \\format (.only format)]]
    [collection
     ["[0]" list (.use "[1]#[0]" functor)]]]
   [meta
    [macro
     ["[0]" template]]]
   ["[0]" type (.only)
    ["[0]" nominal (.except def)]]]])

(def parenthesize
  (-> Text Text)
  (text.enclosed ["(" ")"]))

... Kind
(with_template [<declaration>]
  [(nominal.def .public <declaration> Any)]

  [Literal']
  [Column']
  [Placeholder']
  [(Value' kind)]
  
  [Function']
  
  [Condition']

  [Index']
  
  [Table']
  [View']
  [Source']
  [DB']
  
  [No_Limit] [With_Limit]
  [No_Offset] [With_Offset]
  [Order']
  [No_Order] [With_Order]
  [No_Group] [With_Group]
  [(Query' order group limit offset)]

  [Command']

  [No_Where] [With_Where] [Without_Where]
  [No_Having] [With_Having] [Without_Having]
  [(Action' where having kind)]
  
  [(Schema' kind)]
  [Definition']
  [(Statement' kind)]
  )

(type .public Alias
  Text)

(def .public no_alias Alias "")

(nominal.def .public (SQL kind)
  Text

  ... SQL
  (with_template [<declaration> <kind>]
    [(type .public <declaration>
       (SQL <kind>))]

    [Literal (Value' Literal')]
    [Column (Value' Column')]
    [Placeholder (Value' Placeholder')]
    [Value (Value' Any)]
    
    [Function Function']
    [Condition Condition']

    [Index Index']
    
    [Table Table']
    [View View']
    [Source Source']
    [DB DB']
    
    [Order Order']

    [(Schema kind) (Schema' kind)]
    
    [(Query where having order group limit offset) (Statement' (Action' where having (Query' order group limit offset)))]
    [(Command where having) (Statement' (Action' where having Command'))]
    [(Action where having kind) (Statement' (Action' where having kind))]

    [Definition (Statement' Definition')]
    [Statement (Statement' Any)]
    )

  (def Base_Query
    (type.literal
     (Query No_Where No_Having No_Order No_Group No_Limit No_Offset)))
  
  (def Any_Query
    (type.literal
     (Query Any Any Any Any Any Any)))

  ... Only use this function for debugging purposes.
  ... Do not use this function to actually execute SQL code.
  (def .public read
    (-> (SQL Any) Text)
    (|>> representation))

  (def .public (sql action)
    (-> Statement Text)
    (format (representation action) ";"))

  (def listing
    (-> (List (SQL Any)) Text)
    (|>> (list#each (|>> representation))
         (text.interposed ", ")))

  ... Value
  (def .public ? Placeholder (abstraction "?"))

  (def literal
    (-> Text Literal)
    (|>> abstraction))

  (def .public null Literal (..literal "NULL"))

  (def .public (int value)
    (-> Int Literal)
    (..literal (if (i.< +0 value)
                 (%.int value)
                 (%.nat (.nat value)))))

  (def .public function
    (-> Text Function)
    (|>> abstraction))

  (def .public (call function parameters)
    (-> Function (List Value) Value)
    (abstraction (format (representation function)
                         (..parenthesize (..listing parameters)))))

  ... Condition
  (with_template [<name> <sql_op>]
    [(def .public (<name> reference sample)
       (-> Value Value Condition)
       (abstraction
        (..parenthesize
         (format (representation sample)
                 " " <sql_op> " "
                 (representation reference)))))]

    [=      "="]
    [<>     "<>"]
    [is?    "IS"]
    [>      ">"]
    [>=     ">="]
    [<      "<"]
    [<=     "<="]
    [like?  "LIKE"]
    [ilike? "ILIKE"]
    )

  (def .public (between from to sample)
    (-> Value Value Value Condition)
    (abstraction
     (..parenthesize
      (format (representation sample)
              " BETWEEN " (representation from)
              " AND " (representation to)))))

  (def .public (in options value)
    (-> (List Value) Value Condition)
    (abstraction
     (format (representation value)
             " IN "
             (..parenthesize (listing options)))))

  (with_template [<func_name> <sql_op>]
    [(def .public (<func_name> left right)
       (-> Condition Condition Condition)
       (abstraction
        (format (..parenthesize (representation left))
                " " <sql_op> " "
                (..parenthesize (representation right)))))]

    [and "AND"]
    [or  "OR"]
    )

  (with_template [<name> <type> <sql>]
    [(def .public <name>
       (-> <type> Condition)
       (|>> representation ..parenthesize (format <sql> " ") abstraction))]

    [not    Condition "NOT"]
    [exists Any_Query "EXISTS"]
    )

  ... Query
  (with_template [<name> <type> <decoration>]
    [(def .public <name>
       (-> <type> Source)
       (|>> representation <decoration> abstraction))]

    [from_table Table     (<|)]
    [from_view  View      (<|)]
    [from_query Any_Query ..parenthesize]
    )

  (with_template [<func_name> <op>]
    [(def .public (<func_name> columns source)
       (-> (List [Column Alias]) Source Base_Query)
       (abstraction
        (format <op>
                " "
                (when columns
                  {.#End}
                  "*"
                  
                  _
                  (|> columns
                      (list#each (.function (_ [column alias])
                                   (if (text#= ..no_alias alias)
                                     (representation column)
                                     (format (representation column) " AS " alias))))
                      (text.interposed ", ")))
                " FROM " (representation source))))]


    [select          "SELECT"]
    [select_distinct "SELECT DISTINCT"]
    )

  (with_template [<name> <join_text>]
    [(def .public (<name> table condition prev)
       (-> Table Condition Base_Query Base_Query)
       (abstraction
        (format (representation prev)
                " " <join_text> " "
                (representation table)
                " ON " (representation condition))))]

    [inner_join      "INNER JOIN"]
    [left_join       "LEFT JOIN"]
    [right_join      "RIGHT JOIN"]
    [full_outer_join "FULL OUTER JOIN"]
    )

  (with_template [<function> <sql_op>]
    [(def .public (<function> left right)
       (-> Any_Query Any_Query (Query Without_Where Without_Having No_Order No_Group No_Limit No_Offset))
       (abstraction
        (format (representation left)
                " " <sql_op> " "
                (representation right))))]

    [union     "UNION"]
    [union_all "UNION ALL"]
    [intersect "INTERSECT"]
    )

  (with_template [<name> <sql> <variables> <input> <output>]
    [(`` (def .public (<name> value query)
           (All (_ (,, (template.spliced <variables>)))
             (-> Nat <input> <output>))
           (abstraction
            (format (representation query)
                    " " <sql> " "
                    (%.nat value)))))]

    [limit "LIMIT" [where having order group offset]
     (Query where having order group No_Limit offset)
     (Query where having order group With_Limit offset)]

    [offset "OFFSET" [where having order group limit]
     (Query where having order group limit No_Offset)
     (Query where having order group limit With_Offset)]
    )

  (with_template [<name> <sql>]
    [(def .public <name>
       Order
       (abstraction <sql>))]

    [ascending  "ASC"]
    [descending "DESC"]
    )

  (def .public (order_by pairs query)
    (All (_ where having group limit offset)
      (-> (List [Value Order])
          (Query where having No_Order group limit offset)
          (Query where having With_Order group limit offset)))
    (when pairs
      {.#End}
      (|> query representation abstraction)
      
      _
      (abstraction
       (format (representation query)
               " ORDER BY "
               (|> pairs
                   (list#each (.function (_ [value order])
                                (format (representation value) " " (representation order))))
                   (text.interposed ", "))))))

  (def .public (group_by pairs query)
    (All (_ where having order limit offset)
      (-> (List Value)
          (Query where having order No_Group limit offset)
          (Query where having order With_Group limit offset)))
    (when pairs
      {.#End}
      (|> query representation abstraction)
      
      _
      (abstraction
       (format (representation query)
               " GROUP BY "
               (..listing pairs)))))

  ... Command
  (def .public (insert table columns rows)
    (-> Table (List Column) (List (List Value)) (Command Without_Where Without_Having))
    (abstraction
     (format "INSERT INTO " (representation table) " "
             (..parenthesize (..listing columns))
             " VALUES "
             (|> rows
                 (list#each (|>> ..listing ..parenthesize))
                 (text.interposed ", "))
             )))

  (def .public (update table pairs)
    (-> Table (List [Column Value]) (Command No_Where No_Having))
    (abstraction (format "UPDATE " (representation table)
                         (when pairs
                           {.#End}
                           ""
                           
                           _
                           (format " SET " (|> pairs
                                               (list#each (.function (_ [column value])
                                                            (format (representation column) "=" (representation value))))
                                               (text.interposed ", ")))))))

  (def .public delete
    (-> Table (Command No_Where No_Having))
    (|>> representation (format "DELETE FROM ") abstraction))

  ... Action
  (def .public (where condition prev)
    (All (_ kind having)
      (-> Condition (Action No_Where having kind) (Action With_Where having kind)))
    (abstraction
     (format (representation prev)
             " WHERE "
             (representation condition))))

  (def .public (having condition prev)
    (All (_ where kind)
      (-> Condition (Action where No_Having kind) (Action where With_Having kind)))
    (abstraction
     (format (representation prev)
             " HAVING "
             (representation condition))))

  ... Schema
  (def .public type
    (-> Text (Schema Value))
    (|>> abstraction))
  
  (with_template [<name> <attr>]
    [(def .public (<name> attr)
       (-> (Schema Value) (Schema Value))
       (abstraction
        (format (representation attr) " " <attr>)))]

    [unique   "UNIQUE"]
    [not_null "NOT NULL"]
    [stored   "STORED"]
    )

  (def .public (default value attr)
    (-> Value (Schema Value) (Schema Value))
    (abstraction
     (format (representation attr) " DEFAULT " (representation value))))

  (def .public (define_column name type)
    (-> Column (Schema Value) (Schema Column))
    (abstraction
     (format (representation name) " " (representation type))))

  (def .public (auto_increment offset column)
    (-> Int (Schema Column) (Schema Column))
    (abstraction
     (format (representation column) " AUTO_INCREMENT=" (representation (..int offset)))))

  (def .public (create_table or_replace? table columns)
    (-> Bit Table (List (Schema Column)) Definition)
    (let [command (if or_replace?
                    "CREATE OR REPLACE TABLE"
                    "CREATE TABLE IF NOT EXISTS")]
      (abstraction
       (format command " " (representation table)
               (..parenthesize (..listing columns))))))

  (def .public (create_table_as table query)
    (-> Table Any_Query Definition)
    (abstraction
     (format "CREATE TABLE " (representation table) " AS " (representation query))))

  (with_template [<name> <sql>]
    [(def .public (<name> table)
       (-> Table Definition)
       (abstraction
        (format <sql> " TABLE " (representation table))))]
    
    [drop     "DROP"]
    [truncate "TRUNCATE"]
    )

  (def .public (add_column table column)
    (-> Table (Schema Column) Definition)
    (abstraction
     (format "ALTER TABLE " (representation table) " ADD " (representation column))))

  (def .public (drop_column table column)
    (-> Table Column Definition)
    (abstraction
     (format "ALTER TABLE " (representation table) " DROP COLUMN " (representation column))))

  (with_template [<name> <type>]
    [(def .public (<name> name)
       (-> Text <type>)
       (abstraction name))]

    [column Column]
    [table Table]
    [view View]
    [index Index]
    [db DB]
    )

  (with_template [<name> <type> <sql>]
    [(def .public <name>
       (-> <type> Definition)
       (|>> representation (format <sql> " ") abstraction))]

    [create_db  DB    "CREATE DATABASE"]
    [drop_db    DB    "DROP DATABASE"]
    [drop_view  View  "DROP VIEW"]
    )

  (with_template [<name> <sql>]
    [(def .public (<name> view query)
       (-> View Any_Query Definition)
       (abstraction
        (format <sql> " " (representation view) " AS " (representation query))))]

    [create_view            "CREATE VIEW"]
    [create_or_replace_view "CREATE OR REPLACE VIEW"]
    )

  (def .public (create_index index table unique? columns)
    (-> Index Table Bit (List Column) Definition)
    (abstraction
     (format "CREATE " (if unique? "UNIQUE" "") " INDEX " (representation index)
             " ON " (representation table) " " (..parenthesize (..listing columns)))))

  (def .public (with alias query body)
    (All (_ where having order group limit offset)
      (-> Table Any_Query
          (Query where having order group limit offset)
          (Query where having order group limit offset)))
    (abstraction
     (format "WITH " (representation alias)
             " AS " (..parenthesize (representation query))
             " " (representation body))))
  )