Skip to content

Commit 6fff3c8

Browse files
committed
initial foreign-key support
1 parent 39a10ba commit 6fff3c8

File tree

6 files changed

+158
-17
lines changed

6 files changed

+158
-17
lines changed

deta-lib/private/dialect/postgresql.rkt

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,21 @@
4848
(display (quote/standard table))
4949

5050
(displayln "(")
51-
(define n-fields (length fields))
5251
(for ([i (in-naturals 1)]
5352
[f (in-list fields)])
54-
(emit-field-ddl f (= i n-fields)))
53+
(emit-field-ddl f (= i 1)))
54+
(for ([i (in-naturals 1)]
55+
[f (in-list fields)])
56+
(emit-field-constraints f))
5557
(displayln ")")]
5658

5759
[(drop-table table)
5860
(display "DROP TABLE IF EXISTS ")
5961
(displayln (quote/standard table))]))))
6062

61-
(define (emit-field-ddl f last?)
63+
(define (emit-field-ddl f first?)
64+
(unless first?
65+
(display ","))
6266
(define type
6367
(if (field-auto-increment? f)
6468
"SERIAL"
@@ -75,10 +79,21 @@
7579
(display " PRIMARY KEY"))
7680

7781
(when (field-unique? f)
78-
(display " UNIQUE"))
79-
80-
(unless last?
81-
(displayln ",")))
82+
(display " UNIQUE")))
83+
84+
(define (emit-field-constraints f)
85+
(display ",")
86+
(when (field-foreign-key? f)
87+
(display " FOREIGN KEY")
88+
(display "(")
89+
(display (quote/standard (field-name f)))
90+
(display ")")
91+
(display " REFERENCES ")
92+
(define fk (field-foreign-key f))
93+
(display (quote/standard (foreign-key-schema fk)))
94+
(display "(")
95+
(display (quote/standard (foreign-key-field fk)))
96+
(display ")")))
8297

8398
(define (emit-expr e)
8499
(emit-expr/standard e))

deta-lib/private/dialect/sqlite3.rkt

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,23 @@
5252
(display "CREATE TABLE IF NOT EXISTS ")
5353
(display (quote/standard table))
5454

55-
(displayln "(")
55+
(display "(")
5656
(define n-fields (length fields))
5757
(for ([i (in-naturals 1)]
5858
[f (in-list fields)])
59-
(emit-field-ddl f (= i n-fields)))
59+
(emit-field-ddl f (= i 1)))
60+
(for ([i (in-naturals 1)]
61+
[f (in-list fields)])
62+
(emit-field-constraints f))
6063
(displayln ")")]
6164

6265
[(drop-table table)
6366
(display "DROP TABLE IF EXISTS ")
6467
(displayln (quote/standard table))]))))
6568

66-
(define (emit-field-ddl f last?)
69+
(define (emit-field-ddl f first?)
70+
(unless first?
71+
(display ","))
6772
(display (quote/standard (field-name f)))
6873
(display " ")
6974
(display (type-declaration (field-type f) 'sqlite3))
@@ -78,10 +83,21 @@
7883
(display " AUTOINCREMENT"))
7984

8085
(when (field-unique? f)
81-
(display " UNIQUE"))
82-
83-
(unless last?
84-
(displayln ",")))
86+
(display " UNIQUE")))
87+
88+
(define (emit-field-constraints f)
89+
(when (field-foreign-key? f)
90+
(display ",")
91+
(display " FOREIGN KEY")
92+
(display "(")
93+
(display (quote/standard (field-name f)))
94+
(display ")")
95+
(display " REFERENCES ")
96+
(define fk (field-foreign-key f))
97+
(display (quote/standard (foreign-key-schema fk)))
98+
(display "(")
99+
(display (quote/standard (foreign-key-field fk)))
100+
(display ")")))
85101

86102
(define (emit-expr e)
87103
(match e

deta-lib/private/field.rkt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
make-field
88
(struct-out field)
99

10+
make-foreign-key
11+
(struct-out foreign-key)
12+
1013
id->column-name)
1114

1215
(struct field
@@ -18,6 +21,8 @@
1821
setter
1922
updater
2023
primary-key?
24+
foreign-key?
25+
foreign-key
2126
auto-increment?
2227
nullable?
2328
unique?
@@ -31,6 +36,8 @@
3136
#:setter setter
3237
#:updater updater
3338
#:primary-key? primary-key?
39+
#:foreign-key? foreign-key?
40+
#:foreign-key foreign-key
3441
#:auto-increment? auto-increment?
3542
#:nullable? nullable?
3643
#:unique? unique?
@@ -43,11 +50,21 @@
4350
setter
4451
updater
4552
primary-key?
53+
foreign-key?
54+
foreign-key
4655
auto-increment?
4756
nullable?
4857
unique?
4958
virtual?))
5059

60+
(struct foreign-key
61+
(schema
62+
field))
63+
64+
(define (make-foreign-key #:schema schema
65+
#:field field)
66+
(foreign-key schema field))
67+
5168
(define (id->column-name id)
5269
(let* ([name (cond
5370
[(symbol? id) (symbol->string id)]

deta-lib/private/schema.rkt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
pre-persist-hook
2121
pre-delete-hook
2222
fields
23-
primary-key))
23+
primary-key
24+
foreign-key))
2425

2526
(define (make-schema #:id id
2627
#:table table
@@ -39,6 +40,9 @@
3940
"at most one field may be marked as a #:primary-key"
4041
"bad fields" (map field-id pk-fields)))
4142

43+
(define fk-fields
44+
(filter field-foreign-key? fields))
45+
4246
(define the-schema
4347
(schema id
4448
table
@@ -49,7 +53,8 @@
4953
pre-persist-hook
5054
pre-delete-hook
5155
fields
52-
(findf field-primary-key? pk-fields)))
56+
(findf field-primary-key? pk-fields)
57+
(findf field-foreign-key? fk-fields)))
5358

5459
(begin0 the-schema
5560
(unless virtual?

deta-lib/schema.rkt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272

7373
(define-template-metafunction (make-fld-maker stx)
7474
(syntax-parse stx
75-
[(_ struct-id fld-id fld-name fld-type fld-pk? fld-ai? fld-nullable? fld-unique? fld-virtual?)
75+
[(_ struct-id fld-id fld-name fld-type fld-pk? fld-fk? fld-fk fld-ai? fld-nullable? fld-unique? fld-virtual?)
7676
(with-syntax ([getter-id (format-id #'struct-id "~a-~a" #'struct-id #'fld-id)]
7777
[setter-id (format-id #'struct-id "set-~a-~a" #'struct-id #'fld-id)]
7878
[updater-id (format-id #'struct-id "update-~a-~a" #'struct-id #'fld-id)])
@@ -83,6 +83,8 @@
8383
#:setter setter-id
8484
#:updater updater-id
8585
#:primary-key? fld-pk?
86+
#:foreign-key? fld-fk?
87+
#:foreign-key fld-fk
8688
#:auto-increment? fld-ai?
8789
#:nullable? fld-nullable?
8890
#:unique? fld-unique?
@@ -111,8 +113,14 @@
111113
(optional-arg ... ...)
112114
struct-pred-id))]))
113115

116+
(define-syntax-class fk
117+
(pattern (schema:id field:id)
118+
#:with ctor-arg (with-syntax* ([schema-id (format-id #'schema "~a-schema" #'schema)])
119+
#'(foreign-key (schema-table schema-id) (id->column-name 'field)))))
120+
114121
(define-syntax-class fld
115122
(pattern (id:id type:expr (~alt (~optional (~and #:primary-key primary-key))
123+
(~optional (~seq #:foreign-key foreign-key:fk))
116124
(~optional (~and #:auto-increment auto-increment))
117125
(~optional (~and #:nullable nullable))
118126
(~optional (~and #:unique unique))
@@ -126,6 +134,10 @@
126134
#:with required? (if (or (attribute auto-increment)
127135
(attribute nullable)) #'#f #'t)
128136
#:with primary-key? (if (attribute primary-key) #'#t #'#f)
137+
#:with foreign-key? (if (attribute foreign-key) #'#t #'#f)
138+
#:with foreign-key-ctor-arg (if (attribute foreign-key)
139+
(attribute foreign-key.ctor-arg)
140+
#'#f)
129141
#:with auto-increment? (if (attribute auto-increment) #'#t #'#f)
130142
#:with nullable? (if (attribute nullable) #'#t #'#f)
131143
#:with unique? (if (attribute unique) #'#t #'#f)
@@ -143,6 +155,7 @@
143155
[else #'(ctor-kwd id)]))
144156

145157
(pattern ((id:id default:expr) type:expr (~alt (~optional (~and #:primary-key primary-key))
158+
(~optional (~seq #:foreign-key foreign-key:fk))
146159
(~optional (~and #:auto-increment auto-increment))
147160
(~optional (~and #:nullable nullable))
148161
(~optional (~and #:unique unique))
@@ -164,6 +177,10 @@
164177

165178
#:with required? #'#f
166179
#:with primary-key? (if (attribute primary-key) #'#t #'#f)
180+
#:with foreign-key? (if (attribute foreign-key) #'#t #'#f)
181+
#:with foreign-key-ctor-arg (if (attribute foreign-key)
182+
(attribute foreign-key.ctor-arg)
183+
#'#f)
167184
#:with auto-increment? (if (attribute auto-increment) #'#t #'#f)
168185
#:with nullable? (if (attribute nullable) #'#t #'#f)
169186
#:with unique? (if (attribute unique) #'#t #'#f)
@@ -239,6 +256,8 @@
239256
f.name
240257
f.type
241258
f.primary-key?
259+
f.foreign-key?
260+
f.foreign-key-ctor-arg
242261
f.auto-increment?
243262
f.nullable?
244263
f.unique?

examples/music.rkt

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#lang racket/base
2+
3+
(require db
4+
deta
5+
threading)
6+
7+
(define-schema artist
8+
([id id/f #:primary-key #:auto-increment]
9+
[name string/f #:unique]))
10+
11+
(define-schema album
12+
([id id/f #:primary-key #:auto-increment]
13+
[title string/f #:unique]
14+
[artist-id id/f #:foreign-key (artist id)]
15+
[year-published integer/f #:nullable]))
16+
17+
(define conn
18+
(sqlite3-connect #:database 'memory))
19+
20+
(void
21+
(create-all! conn)
22+
23+
(insert! conn
24+
(make-album #:title "Nevermind"
25+
#:artist-id (artist-id (insert-one! conn (make-artist #:name "Nirvana")))
26+
#:year-published 1991)
27+
(make-album #:title "Achtung Baby"
28+
#:artist-id (artist-id (insert-one! conn (make-artist #:name "U2")))
29+
#:year-published 1991)
30+
(make-album #:title "The Miseducation of Lauryn Hill"
31+
#:artist-id (artist-id (insert-one! conn (make-artist #:name "Lauryn Hill")))
32+
#:year-published 1998)))
33+
34+
(define (albums-before year)
35+
(~> (from album #:as a)
36+
(where (< a.year-published ,year))))
37+
38+
(define (albums-between start-year end-year)
39+
(~> (from album #:as a)
40+
(where (between a.year-published ,start-year ,end-year))))
41+
42+
(displayln "Albums published before 1995:")
43+
(for ([a (in-entities conn (albums-before 1995))])
44+
(displayln (album-title a)))
45+
46+
(displayln "")
47+
(displayln "Albums published between 1990 and 2000:")
48+
(for ([a (in-entities conn (albums-between 1990 2000))])
49+
(displayln (album-title a)))
50+
51+
(define-schema album-with-artists
52+
#:virtual
53+
([id id/f #:primary-key #:auto-increment]
54+
[title string/f #:unique]
55+
[artist-id id/f #:foreign-key (artist id)]
56+
[year-published integer/f #:nullable]
57+
[name string/f]))
58+
59+
(displayln "")
60+
(displayln "All albums with artist names:")
61+
(for ([a (in-entities conn (~> (from album #:as al)
62+
(join artist
63+
#:as at
64+
#:on (= al.artist_id at.id))
65+
(select al.* at.name)
66+
(project-onto album-with-artists-schema)))])
67+
(displayln (format "Artist: ~v, album: ~v"
68+
(album-with-artists-name a)
69+
(album-with-artists-title a))))

0 commit comments

Comments
 (0)