Skip to content

Commit 8da8c80

Browse files
authored
Merge pull request #137 from fukamachi/self-reference
Self referencing
2 parents d8e0fab + 7965498 commit 8da8c80

File tree

5 files changed

+58
-22
lines changed

5 files changed

+58
-22
lines changed

docker-compose.yml

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,26 @@ services:
1515
- postgres
1616
- mysql
1717
environment:
18-
POSTGRES_HOST: postgres
18+
POSTGRES_HOST: mito-test-postgres
1919
POSTGRES_USER: mito
2020
POSTGRES_PASS: mito
21-
MYSQL_HOST: mysql
21+
MYSQL_HOST: mito-test-mysql
2222
MYSQL_USER: root
2323
MYSQL_PASS: mito
2424

2525
postgres:
2626
container_name: mito-postgres
2727
image: "postgres:10"
28+
hostname: mito-test-postgres
2829
restart: always
2930
environment:
3031
POSTGRES_USER: mito
3132
POSTGRES_PASSWORD: mito
3233

3334
mysql:
3435
container_name: mito-mysql
35-
image: "mysql:8"
36+
image: "mysql:8.4"
37+
hostname: mito-test-mysql
3638
restart: always
3739
command: --mysql_native_password=ON
3840
environment:

src/core/class/table.lisp

+19-9
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,37 @@
4242
(format nil "~:@(~A-~A~)" name pk-name)))
4343

4444
(defun add-referencing-slots (initargs)
45-
(let ((parent-column-map (make-hash-table :test 'eq)))
45+
(let ((parent-column-map (make-hash-table :test 'eq))
46+
(class-name (getf initargs :name)))
4647
(setf (getf initargs :direct-slots)
4748
(loop for column in (getf initargs :direct-slots)
4849
for (col-type not-null) = (multiple-value-list (parse-col-type (getf column :col-type)))
4950

5051
if (typep col-type '(and symbol (not null) (not keyword)))
5152
append
52-
(let* ((name (getf column :name))
53-
;; FIXME: find-class raises an error if the class is this same class or not defined yet.
54-
(rel-class (find-class col-type))
55-
(pk-names (table-primary-key rel-class)))
53+
(let* ((column-name (getf column :name))
54+
;; FIXME: find-class raises an error if the class is not defined yet.
55+
(pk-names (if (eq col-type class-name)
56+
(or (getf initargs :primary-key)
57+
(getf (find-if (lambda (column-def)
58+
(getf column-def :primary-key))
59+
(getf initargs :direct-slots))
60+
:name)
61+
(loop for superclass in (getf initargs :direct-superclasses)
62+
for pk-names = (table-primary-key superclass)
63+
until pk-names
64+
finally (return pk-names)))
65+
(table-primary-key (find-class col-type)))))
5666
(unless pk-names
57-
(error "Foreign class ~S has no primary keys."
58-
(class-name rel-class)))
67+
(error "Primary keys can not be determined for ~A."
68+
col-type))
5969
(rplacd (cdr column)
6070
`(:ghost t ,@(cddr column)))
6171

6272
(cons column
6373
(mapcar (lambda (pk-name)
64-
(let ((rel-column-name (rel-column-name name pk-name)))
65-
(setf (gethash rel-column-name parent-column-map) name)
74+
(let ((rel-column-name (rel-column-name column-name pk-name)))
75+
(setf (gethash rel-column-name parent-column-map) column-name)
6676
`(:name ,rel-column-name
6777
:initargs (,(intern (symbol-name rel-column-name) :keyword))
6878
:col-type ,(if not-null

src/core/dao/mixin.lisp

+5-7
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
(setf (dao-synced obj) t)
6969
obj)))
7070

71-
(defun make-relational-reader-method (func-name class slot-name rel-class)
71+
(defun make-relational-reader-method (func-name class slot-name rel-class-name)
7272
(let ((generic-function
7373
(ensure-generic-function func-name :lambda-list '(object))))
7474
(add-method
@@ -99,7 +99,7 @@
9999
(first
100100
(mito.db:retrieve-by-sql
101101
(sxql:select :*
102-
(sxql:from (sxql:make-sql-symbol (table-name rel-class)))
102+
(sxql:from (sxql:make-sql-symbol (table-name (find-class rel-class-name))))
103103
(sxql:where
104104
`(:and
105105
,@(mapcar (lambda (slot-name)
@@ -111,7 +111,7 @@
111111
child-columns)))
112112
(sxql:limit 1))))))
113113
(and result
114-
(apply #'make-dao-instance rel-class result))))))
114+
(apply #'make-dao-instance rel-class-name result))))))
115115
(setf calledp t
116116
(slot-value object slot-name) foreign-object)))))))))
117117

@@ -121,11 +121,9 @@
121121
when (and (symbolp col-type)
122122
(not (null col-type))
123123
(not (keywordp col-type)))
124-
do (let* ((name (c2mop:slot-definition-name column))
125-
;; FIXME: find-class returns NIL if the class is this same class
126-
(rel-class (find-class col-type)))
124+
do (let ((name (c2mop:slot-definition-name column)))
127125
(dolist (reader (c2mop:slot-definition-readers column))
128-
(make-relational-reader-method reader class name rel-class)))))
126+
(make-relational-reader-method reader class name col-type)))))
129127

130128
(defmethod initialize-instance :around ((class dao-table-mixin) &rest initargs
131129
&key conc-name &allow-other-keys)

t/class.lisp

+26
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,29 @@
299299
"CREATE TABLE tweet_tags (
300300
tweet1_id BIGINT UNSIGNED
301301
)"))
302+
303+
(deftest self-reference
304+
(is-table-class :mysql
305+
(defclass category ()
306+
((parent :col-type category
307+
:initarg :parent
308+
:accessor parent))
309+
(:metaclass mito:dao-table-class))
310+
"CREATE TABLE category (
311+
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
312+
parent_id BIGINT UNSIGNED NOT NULL,
313+
created_at TIMESTAMP,
314+
updated_at TIMESTAMP
315+
)")
316+
(is-table-class :postgres
317+
(defclass category ()
318+
((parent :col-type category
319+
:initarg :parent
320+
:accessor parent))
321+
(:metaclass mito:dao-table-class))
322+
"CREATE TABLE category (
323+
id BIGSERIAL NOT NULL PRIMARY KEY,
324+
parent_id BIGINT NOT NULL,
325+
created_at TIMESTAMPTZ,
326+
updated_at TIMESTAMPTZ
327+
)"))

t/migration/postgres.lisp

+3-3
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,12 @@
294294
"No migration after migrating"))
295295

296296
(testing "composite primary keys"
297-
(when (find-class 'tag nil)
298-
(setf (find-class 'tag) nil))
299-
(execute-sql "DROP TABLE IF EXISTS tag")
300297
(when (find-class 'tweets-tag nil)
301298
(setf (find-class 'tweets-tag) nil))
302299
(execute-sql "DROP TABLE IF EXISTS tweets_tag")
300+
(when (find-class 'tag nil)
301+
(setf (find-class 'tag) nil))
302+
(execute-sql "DROP TABLE IF EXISTS tag")
303303

304304
(defclass tag ()
305305
((name :col-type (:varchar 10)

0 commit comments

Comments
 (0)