1 ! Copyright (C) 2008 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: io.files io.files.temp kernel tools.test db db.tuples classes
4 db.types continuations namespaces math math.ranges
5 prettyprint calendar sequences db.sqlite math.intervals
6 db.postgresql accessors random math.bitwise system
7 math.ranges strings urls fry db.tuples.private db.private ;
10 : sqlite-db ( -- sqlite-db )
11 "tuples-test.db" temp-file <sqlite-db> ;
13 : test-sqlite ( quot -- )
16 "tuples-test.db" temp-file <sqlite-db> _ with-db
20 : postgresql-db ( -- postgresql-db )
24 "thepasswordistrust" >>password
25 "factor-test" >>database ;
27 : test-postgresql ( quot -- )
29 os windows? cpu x86.64? and [
30 [ ] [ postgresql-db _ with-db ] unit-test
34 ! These words leak resources, but are useful for interactivel testing
35 : sqlite-test-db ( -- )
36 sqlite-db db-open db-connection set ;
38 : postgresql-test-db ( -- )
39 postgresql-db db-open db-connection set ;
41 TUPLE: person the-id the-name the-number the-real
42 ts date time blob factor-blob url ;
44 : <person> ( name age real ts date time blob factor-blob url -- person )
56 : <user-assigned-person> ( id name age real ts date time blob factor-blob url -- person )
66 [ ] [ person recreate-table ] unit-test
67 [ ] [ person ensure-table ] unit-test
68 [ ] [ person drop-table ] unit-test
69 [ ] [ person create-table ] unit-test
70 [ person create-table ] must-fail
71 [ ] [ person ensure-table ] unit-test
73 [ ] [ person1 get insert-tuple ] unit-test
75 [ 1 ] [ person1 get the-id>> ] unit-test
77 [ ] [ person1 get 200 >>the-number drop ] unit-test
79 [ ] [ person1 get update-tuple ] unit-test
81 [ T{ person f 1 "billy" 200 3.14 } ]
82 [ T{ person f 1 } select-tuple ] unit-test
83 [ ] [ person2 get insert-tuple ] unit-test
86 T{ person f 1 "billy" 200 3.14 }
87 T{ person f 2 "johnny" 10 3.14 }
89 ] [ T{ person f f f f 3.14 } select-tuples ] unit-test
92 T{ person f 1 "billy" 200 3.14 }
93 T{ person f 2 "johnny" 10 3.14 }
95 ] [ T{ person f } select-tuples ] unit-test
99 T{ person f 2 "johnny" 10 3.14 }
101 ] [ T{ person f f f 10 3.14 } select-tuples ] unit-test
104 [ ] [ person1 get delete-tuples ] unit-test
105 [ f ] [ T{ person f 1 } select-tuple ] unit-test
107 [ ] [ person3 get insert-tuple ] unit-test
117 T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
118 T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
119 T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
120 B{ 115 116 111 114 101 105 110 97 98 108 111 98 }
122 ] [ T{ person f 3 } select-tuple ] unit-test
124 [ ] [ person4 get insert-tuple ] unit-test
133 T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
134 T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
135 T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
137 H{ { 1 2 } { 3 4 } { 5 "lol" } }
138 URL" http://www.google.com/search?hl=en&q=trailer+park+boys&btnG=Google+Search"
140 ] [ T{ person f 4 } select-tuple ] unit-test
142 [ ] [ person drop-table ] unit-test ;
144 : db-assigned-person-schema ( -- )
147 { "the-id" "ID" +db-assigned-id+ }
148 { "the-name" "NAME" { VARCHAR 256 } +not-null+ }
149 { "the-number" "AGE" INTEGER { +default+ 0 } }
150 { "the-real" "REAL" DOUBLE { +default+ 0.3 } }
151 { "ts" "TS" TIMESTAMP }
155 { "factor-blob" "FB" FACTOR-BLOB }
158 "billy" 10 3.14 f f f f f f <person> person1 set
159 "johnny" 10 3.14 f f f f f f <person> person2 set
161 T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
162 T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
163 T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
164 B{ 115 116 111 114 101 105 110 97 98 108 111 98 } f f <person> person3 set
166 T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
167 T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
168 T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
169 f H{ { 1 2 } { 3 4 } { 5 "lol" } } URL" http://www.google.com/search?hl=en&q=trailer+park+boys&btnG=Google+Search" <person> person4 set ;
171 : user-assigned-person-schema ( -- )
174 { "the-id" "ID" INTEGER +user-assigned-id+ }
175 { "the-name" "NAME" { VARCHAR 256 } +not-null+ }
176 { "the-number" "AGE" INTEGER { +default+ 0 } }
177 { "the-real" "REAL" DOUBLE { +default+ 0.3 } }
178 { "ts" "TS" TIMESTAMP }
182 { "factor-blob" "FB" FACTOR-BLOB }
185 1 "billy" 10 3.14 f f f f f f <user-assigned-person> person1 set
186 2 "johnny" 10 3.14 f f f f f f <user-assigned-person> person2 set
188 T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
189 T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
190 T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
191 B{ 115 116 111 114 101 105 110 97 98 108 111 98 }
192 f f <user-assigned-person> person3 set
194 T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
195 T{ timestamp f 2008 11 22 0 0 0 T{ duration f 0 0 0 0 0 0 } }
196 T{ timestamp f 0 0 0 12 34 56 T{ duration f 0 0 0 0 0 0 } }
197 f H{ { 1 2 } { 3 4 } { 5 "lol" } } URL" http://www.google.com/search?hl=en&q=trailer+park+boys&btnG=Google+Search" <user-assigned-person> person4 set ;
200 TUPLE: paste n summary author channel mode contents timestamp annotations ;
201 TUPLE: annotation n paste-id summary author mode contents ;
205 { "n" "ID" +db-assigned-id+ }
206 { "summary" "SUMMARY" TEXT }
207 { "author" "AUTHOR" TEXT }
208 { "channel" "CHANNEL" TEXT }
209 { "mode" "MODE" TEXT }
210 { "contents" "CONTENTS" TEXT }
211 { "timestamp" "DATE" TIMESTAMP }
212 { "annotations" { +has-many+ annotation } }
215 : annotation-schema-foreign-key ( -- )
216 annotation "ANNOTATION"
218 { "n" "ID" +db-assigned-id+ }
219 { "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" } }
220 { "summary" "SUMMARY" TEXT }
221 { "author" "AUTHOR" TEXT }
222 { "mode" "MODE" TEXT }
223 { "contents" "CONTENTS" TEXT }
224 } define-persistent ;
226 : annotation-schema-foreign-key-not-null ( -- )
227 annotation "ANNOTATION"
229 { "n" "ID" +db-assigned-id+ }
230 { "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" } +not-null+ }
231 { "summary" "SUMMARY" TEXT }
232 { "author" "AUTHOR" TEXT }
233 { "mode" "MODE" TEXT }
234 { "contents" "CONTENTS" TEXT }
235 } define-persistent ;
237 : annotation-schema-cascade ( -- )
238 annotation "ANNOTATION"
240 { "n" "ID" +db-assigned-id+ }
241 { "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" }
242 +on-delete+ +cascade+ }
243 { "summary" "SUMMARY" TEXT }
244 { "author" "AUTHOR" TEXT }
245 { "mode" "MODE" TEXT }
246 { "contents" "CONTENTS" TEXT }
247 } define-persistent ;
249 : annotation-schema-restrict ( -- )
250 annotation "ANNOTATION"
252 { "n" "ID" +db-assigned-id+ }
253 { "paste-id" "PASTE_ID" INTEGER { +foreign-id+ paste "ID" } }
254 { "summary" "SUMMARY" TEXT }
255 { "author" "AUTHOR" TEXT }
256 { "mode" "MODE" TEXT }
257 { "contents" "CONTENTS" TEXT }
258 } define-persistent ;
260 : test-paste-schema ( -- )
261 [ ] [ paste ensure-table ] unit-test
262 [ ] [ annotation ensure-table ] unit-test
263 [ ] [ annotation drop-table ] unit-test
264 [ ] [ paste drop-table ] unit-test
265 [ ] [ paste create-table ] unit-test
266 [ ] [ annotation create-table ] unit-test
273 "contents1" >>contents
281 "annotation1" >>summary
283 "annotation contents" >>contents
287 : test-foreign-key ( -- )
288 [ ] [ annotation-schema-foreign-key ] unit-test
290 [ paste new 1 >>n delete-tuples ] must-fail ;
292 : test-foreign-key-not-null ( -- )
293 [ ] [ annotation-schema-foreign-key-not-null ] unit-test
295 [ paste new 1 >>n delete-tuples ] must-fail ;
297 : test-cascade ( -- )
298 [ ] [ annotation-schema-cascade ] unit-test
300 [ ] [ paste new 1 >>n delete-tuples ] unit-test
301 [ 0 ] [ paste new select-tuples length ] unit-test ;
303 : test-restrict ( -- )
304 [ ] [ annotation-schema-restrict ] unit-test
306 [ paste new 1 >>n delete-tuples ] must-fail ;
308 [ test-foreign-key ] test-sqlite
309 [ test-foreign-key-not-null ] test-sqlite
310 [ test-cascade ] test-sqlite
311 [ test-restrict ] test-sqlite
313 [ test-foreign-key ] test-postgresql
314 [ test-foreign-key-not-null ] test-postgresql
315 [ test-cascade ] test-postgresql
316 [ test-restrict ] test-postgresql
318 : test-repeated-insert
319 [ ] [ person ensure-table ] unit-test
320 [ ] [ person1 get insert-tuple ] unit-test
321 [ person1 get insert-tuple ] must-fail ;
323 TUPLE: serialize-me id data ;
325 : test-serialize ( -- )
326 serialize-me "SERIALIZED"
328 { "id" "ID" +db-assigned-id+ }
329 { "data" "DATA" FACTOR-BLOB }
331 [ serialize-me drop-table ] [ drop ] recover
332 [ ] [ serialize-me create-table ] unit-test
334 [ ] [ T{ serialize-me f f H{ { 1 2 } } } insert-tuple ] unit-test
336 { T{ serialize-me f 1 H{ { 1 2 } } } }
337 ] [ T{ serialize-me f 1 } select-tuples ] unit-test ;
339 TUPLE: exam id name score ;
341 : random-exam ( -- exam )
343 6 [ CHAR: a CHAR: z [a,b] random ] replicate >string
347 : test-intervals ( -- )
351 { "idd" "ID" +db-assigned-id+ }
352 { "named" "NAME" TEXT }
353 { "score" "SCORE" INTEGER }
356 seq>> { "idd" "named" } =
361 { "id" "ID" +db-assigned-id+ }
362 { "name" "NAME" TEXT }
363 { "score" "SCORE" INTEGER }
365 [ exam drop-table ] [ drop ] recover
366 [ ] [ exam create-table ] unit-test
368 [ ] [ T{ exam f f "Kyle" 100 } insert-tuple ] unit-test
369 [ ] [ T{ exam f f "Stan" 80 } insert-tuple ] unit-test
370 [ ] [ T{ exam f f "Kenny" 60 } insert-tuple ] unit-test
371 [ ] [ T{ exam f f "Cartman" 41 } insert-tuple ] unit-test
374 [ T{ exam { name IGNORE } { score IGNORE } } select-tuples length ] unit-test
377 [ T{ exam { name IGNORE } { score IGNORE } } select-tuples first score>> ] unit-test
379 [ T{ exam { name IGNORE } { score IGNORE } { id IGNORE } } select-tuples first score>> ] [ class>> "EXAM" = ] must-fail-with
383 T{ exam f 3 "Kenny" 60 }
384 T{ exam f 4 "Cartman" 41 }
387 T{ exam f f f T{ interval f { 0 t } { 70 t } } } select-tuples
393 T{ exam f T{ interval f { 3 f } { 4 f } } f } select-tuples
397 T{ exam f 4 "Cartman" 41 }
400 T{ exam f T{ interval f { 3 f } { 4 t } } f } select-tuples
404 T{ exam f 3 "Kenny" 60 }
407 T{ exam f T{ interval f { 3 t } { 4 f } } f } select-tuples
411 T{ exam f 3 "Kenny" 60 }
412 T{ exam f 4 "Cartman" 41 }
415 T{ exam f T{ interval f { 3 t } { 4 t } } f } select-tuples
420 T{ exam f 1 "Kyle" 100 }
421 T{ exam f 2 "Stan" 80 }
424 T{ exam f f { "Stan" "Kyle" } } select-tuples
429 T{ exam f 1 "Kyle" 100 }
430 T{ exam f 2 "Stan" 80 }
431 T{ exam f 3 "Kenny" 60 }
434 T{ exam f T{ range f 1 3 1 } } select-tuples
439 T{ exam f 2 "Stan" 80 }
440 T{ exam f 3 "Kenny" 60 }
441 T{ exam f 4 "Cartman" 41 }
444 T{ exam f T{ interval f { 2 t } { 1.0/0.0 f } } } select-tuples
449 T{ exam f 1 "Kyle" 100 }
452 T{ exam f T{ interval f { -1.0/0.0 t } { 2 f } } } select-tuples
457 T{ exam f 1 "Kyle" 100 }
458 T{ exam f 2 "Stan" 80 }
459 T{ exam f 3 "Kenny" 60 }
460 T{ exam f 4 "Cartman" 41 }
463 T{ exam f T{ interval f { -1.0/0.0 t } { 1/0. f } } } select-tuples
468 T{ exam f 1 "Kyle" 100 }
469 T{ exam f 2 "Stan" 80 }
470 T{ exam f 3 "Kenny" 60 }
471 T{ exam f 4 "Cartman" 41 }
474 T{ exam } select-tuples
477 [ 4 ] [ T{ exam } count-tuples ] unit-test
479 [ ] [ T{ exam { score 10 } } insert-tuple ] unit-test
482 [ T{ exam { name NULL } } select-tuples first score>> ] unit-test ;
484 TUPLE: bignum-test id m n o ;
485 : <bignum-test> ( m n o -- obj )
492 bignum-test "BIGNUM_TEST"
494 { "id" "ID" +db-assigned-id+ }
495 { "m" "M" BIG-INTEGER }
496 { "n" "N" UNSIGNED-BIG-INTEGER }
497 { "o" "O" SIGNED-BIG-INTEGER }
499 [ bignum-test drop-table ] ignore-errors
500 [ ] [ bignum-test ensure-table ] unit-test
501 [ ] [ 63 2^ 1- dup dup <bignum-test> insert-tuple ] unit-test ;
504 ! [ T{ bignum-test f 1
505 ! -9223372036854775808 9223372036854775808 -9223372036854775808 } ]
506 ! [ T{ bignum-test f 1 } select-tuple ] unit-test ;
508 TUPLE: secret n message ;
514 { "n" "ID" +random-id+ system-random-generator }
515 { "message" "MESSAGE" TEXT }
518 [ ] [ secret recreate-table ] unit-test
520 [ t ] [ f "kilroy was here" <secret> [ insert-tuple ] keep n>> integer? ] unit-test
522 [ ] [ f "kilroy was here2" <secret> insert-tuple ] unit-test
524 [ ] [ f "kilroy was here3" <secret> insert-tuple ] unit-test
527 T{ secret } select-tuples
528 first message>> "kilroy was here" head?
532 T{ secret } select-tuples length 3 =
535 [ db-assigned-person-schema test-tuples ] test-sqlite
536 [ user-assigned-person-schema test-tuples ] test-sqlite
537 [ user-assigned-person-schema test-repeated-insert ] test-sqlite
538 [ test-bignum ] test-sqlite
539 [ test-serialize ] test-sqlite
540 [ test-intervals ] test-sqlite
541 [ test-random-id ] test-sqlite
543 [ db-assigned-person-schema test-tuples ] test-postgresql
544 [ user-assigned-person-schema test-tuples ] test-postgresql
545 [ user-assigned-person-schema test-repeated-insert ] test-postgresql
546 [ test-bignum ] test-postgresql
547 [ test-serialize ] test-postgresql
548 [ test-intervals ] test-postgresql
549 [ test-random-id ] test-postgresql
551 TUPLE: does-not-persist ;
554 [ does-not-persist create-sql-statement ]
555 [ class \ not-persistent = ] must-fail-with
559 [ does-not-persist create-sql-statement ]
560 [ class \ not-persistent = ] must-fail-with
564 TUPLE: suparclass id a ;
567 { "id" "ID" +db-assigned-id+ }
571 TUPLE: subbclass < suparclass b ;
573 subbclass "SUBCLASS" {
577 TUPLE: fubbclass < subbclass ;
579 fubbclass "FUBCLASS" { } define-persistent
581 : test-db-inheritance ( -- )
582 [ ] [ subbclass ensure-table ] unit-test
583 [ ] [ fubbclass ensure-table ] unit-test
586 subbclass new 5 >>a "hi" >>b dup insert-tuple id>> "id" set
590 subbclass new "id" get >>id select-tuple
591 [ subbclass? ] [ b>> ] [ a>> ] tri
594 [ ] [ fubbclass new 0 >>a "hi" >>b insert-tuple ] unit-test
596 [ t ] [ fubbclass new select-tuples [ fubbclass? ] all? ] unit-test ;
598 [ test-db-inheritance ] test-sqlite
599 [ test-db-inheritance ] test-postgresql
602 TUPLE: string-encoding-test id string ;
604 string-encoding-test "STRING_ENCODING_TEST" {
605 { "id" "ID" +db-assigned-id+ }
606 { "string" "STRING" TEXT }
609 : test-string-encoding ( -- )
610 [ ] [ string-encoding-test ensure-table ] unit-test
613 string-encoding-test new
614 "\u{copyright-sign}\u{bengali-letter-cha}" >>string
615 [ insert-tuple ] [ id>> "id" set ] bi
618 [ "\u{copyright-sign}\u{bengali-letter-cha}" ] [
619 string-encoding-test new "id" get >>id select-tuple string>>
622 [ test-string-encoding ] test-sqlite
623 [ test-string-encoding ] test-postgresql
625 ! Don't comment these out. These words must infer
626 \ bind-tuple must-infer
627 \ insert-tuple must-infer
628 \ update-tuple must-infer
629 \ delete-tuples must-infer
630 \ select-tuple must-infer
631 \ define-persistent must-infer
632 \ ensure-table must-infer
633 \ create-table must-infer
634 \ drop-table must-infer
636 : test-queries ( -- )
637 [ ] [ exam ensure-table ] unit-test
638 [ ] [ 1000 [ random-exam insert-tuple ] times ] unit-test
641 T{ exam { score T{ interval { from { 0 t } } { to { 100 t } } } } }
643 5 >>limit select-tuples length
646 TUPLE: compound-foo a b c ;
648 compound-foo "COMPOUND_FOO"
650 { "a" "A" INTEGER +user-assigned-id+ }
651 { "b" "B" INTEGER +user-assigned-id+ }
655 : test-compound-primary-key ( -- )
656 [ ] [ compound-foo ensure-table ] unit-test
657 [ ] [ compound-foo drop-table ] unit-test
658 [ ] [ compound-foo create-table ] unit-test
659 [ ] [ 1 2 3 compound-foo boa insert-tuple ] unit-test
660 [ 1 2 3 compound-foo boa insert-tuple ] must-fail
661 [ ] [ 2 3 4 compound-foo boa insert-tuple ] unit-test
662 [ T{ compound-foo { a 2 } { b 3 } { c 4 } } ]
663 [ compound-foo new 4 >>c select-tuple ] unit-test ;
665 [ test-compound-primary-key ] test-sqlite
666 [ test-compound-primary-key ] test-postgresql