-
-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathcolumn.lisp
143 lines (131 loc) · 4.86 KB
/
column.lisp
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
(in-package :cl-user)
(defpackage mito.dao.column
(:use #:cl
#:mito.util)
(:import-from #:mito.class.column
#:table-column-class
#:table-column-type)
(:import-from #:local-time)
(:import-from #:cl-ppcre)
(:export #:dao-table-column-class
#:dao-table-column-inflate
#:dao-table-column-deflate
#:inflate-for-col-type
#:deflate-for-col-type))
(in-package :mito.dao.column)
(defparameter *conc-name* nil)
(defclass dao-table-column-class (table-column-class)
((inflate :type (or function null)
:initarg :inflate)
(deflate :type (or function null)
:initarg :deflate)))
(defmethod initialize-instance :around ((object dao-table-column-class) &rest rest-initargs
&key name readers writers inflate deflate
&allow-other-keys)
(when *conc-name*
(let ((accessor (intern
(format nil "~:@(~A~A~)" *conc-name* name)
*package*)))
(unless readers
(pushnew accessor readers)
(setf (getf rest-initargs :readers) readers))
(unless writers
(pushnew `(setf ,accessor) writers)
(setf (getf rest-initargs :writers) writers))))
(when inflate
(setf (getf rest-initargs :inflate) (eval inflate)))
(when deflate
(setf (getf rest-initargs :deflate) (eval deflate)))
(apply #'call-next-method object rest-initargs))
(defgeneric dao-table-column-inflate (column value)
(:method ((column dao-table-column-class) value)
(if (slot-boundp column 'inflate)
(funcall (slot-value column 'inflate) value)
(inflate-for-col-type
(table-column-type column)
value))))
(defgeneric dao-table-column-deflate (column value)
(:method ((column dao-table-column-class) value)
(if (slot-boundp column 'deflate)
(funcall (slot-value column 'deflate) value)
(deflate-for-col-type
(table-column-type column)
value))))
(defgeneric inflate-for-col-type (col-type value)
(:method (col-type value)
(declare (ignore col-type))
(identity value))
(:method ((col-type cons) value)
(inflate-for-col-type (first col-type) value))
(:method ((col-type (eql :datetime)) value)
(etypecase value
(integer
(local-time:universal-to-timestamp value))
(float
(multiple-value-bind (sec nsec)
(truncate value)
(local-time:universal-to-timestamp sec :nsec (* (round (* nsec 1000000)) 1000))))
(string
(local-time:parse-timestring value :date-time-separator #\Space))
(null nil)))
(:method ((col-type (eql :date)) value)
(etypecase value
(integer
(local-time:universal-to-timestamp value))
(string
(ppcre:register-groups-bind ((#'parse-integer year month day))
("^(\\d{4})-(\\d{2})-(\\d{2})$" value)
(local-time:universal-to-timestamp
(encode-universal-time 0 0 0 day month year))))
(null nil)))
(:method ((col-type (eql :timestamp)) value)
(inflate-for-col-type :datetime value))
(:method ((col-type (eql :timestamptz)) value)
(inflate-for-col-type :datetime value))
(:method ((col-type (eql :time)) value)
(flet ((v (key)
(second (assoc key value))))
(if (consp value)
(format nil "~2,'0D:~2,'0D:~2,'0D~:[.~3,'0D~;~]"
(v :hours) (v :minutes) (v :seconds) (= (v :microseconds) 0) (v :microseconds))
value)))
(:method ((col-type (eql :boolean)) value)
(cond
;; MySQL & SQLite3
((typep value 'integer)
(not (= value 0)))
;; PostgreSQL
((typep value 'boolean)
value)
(t
(error "Unexpected value for boolean column: ~S" value)))))
(defvar *db-datetime-format*
'((:year 4) #\- (:month 2) #\- (:day 2) #\Space (:hour 2) #\: (:min 2) #\: (:sec 2) #\. (:usec 6) :gmt-offset-or-z))
(defvar *db-datetime-format-with-out-timezone*
'((:year 4) #\- (:month 2) #\- (:day 2) #\Space (:hour 2) #\: (:min 2) #\: (:sec 2) #\. (:usec 6)))
(defvar *db-date-format*
'((:year 4) #\- (:month 2) #\- (:day 2)))
(defgeneric deflate-for-col-type (col-type value)
(:method (col-type value)
(declare (ignore col-type))
(identity value))
(:method ((col-type cons) value)
(deflate-for-col-type (first col-type) value))
(:method ((col-type (eql :datetime)) value)
(etypecase value
(integer
(local-time:universal-to-timestamp value))
(local-time:timestamp
value)
(string value)
(null nil)))
(:method ((col-type (eql :date)) value)
(etypecase value
(local-time:timestamp
value)
(string value)
(null nil)))
(:method ((col-type (eql :timestamp)) value)
(deflate-for-col-type :datetime value))
(:method ((col-type (eql :timestamptz)) value)
(deflate-for-col-type :datetime value)))