3
3
*/
4
4
var mysql = require ( 'mysql' ) ;
5
5
var jdb = require ( 'jugglingdb' ) ;
6
+ var EnumFactory = require ( './enumFactory' ) . EnumFactory ;
7
+
6
8
7
9
exports . initialize = function initializeSchema ( schema , callback ) {
8
10
if ( ! mysql ) return ;
9
11
10
12
var s = schema . settings ;
13
+
14
+ if ( s . collation ) {
15
+ s . charset = s . collation . substr ( 0 , s . collation . indexOf ( '_' ) ) ; // Charset should be first 'chunk' of collation.
16
+ } else {
17
+ s . collation = 'utf8mb4_general_ci' ;
18
+ s . charset = 'utf8mb4' ;
19
+ }
20
+
21
+ s . supportBigNumbers = ( s . supportBigNumbers || false ) ;
22
+ s . timezone = ( s . timezone || 'local' ) ;
23
+
11
24
schema . client = mysql . createConnection ( {
12
25
host : s . host || 'localhost' ,
13
26
port : s . port || 3306 ,
14
27
user : s . username ,
15
28
password : s . password ,
29
+ timezone : s . timezone ,
16
30
debug : s . debug ,
17
- socketPath : s . socketPath
31
+ socketPath : s . socketPath ,
32
+ charset : s . collation . toUpperCase ( ) , // Correct by docs despite seeming odd.
33
+ supportBigNumbers : s . supportBigNumbers
18
34
} ) ;
19
35
20
36
schema . client . on ( 'error' , function ( err ) {
@@ -24,22 +40,30 @@ exports.initialize = function initializeSchema(schema, callback) {
24
40
schema . adapter = new MySQL ( schema . client ) ;
25
41
schema . adapter . schema = schema ;
26
42
27
- // schema.client.query('SET TIME_ZONE = "+04:00"', callback);
28
43
schema . client . query ( 'USE `' + s . database + '`' , function ( err ) {
29
- if ( err && err . message . match ( / ( ^ | : ) u n k n o w n d a t a b a s e / i) ) {
30
- var dbName = s . database ;
31
- schema . client . query ( 'CREATE DATABASE ' + dbName , function ( error ) {
32
- if ( ! error ) {
33
- schema . client . query ( 'USE ' + s . database , callback ) ;
34
- } else {
35
- throw error ;
36
- }
37
- } ) ;
44
+ if ( err ) {
45
+ if ( err . message . match ( / ( ^ | : ) u n k n o w n d a t a b a s e / i) ) {
46
+ var dbName = s . database ;
47
+ var charset = s . charset ;
48
+ var collation = s . collation ;
49
+ var q = 'CREATE DATABASE ' + dbName + ' CHARACTER SET ' + charset + ' COLLATE ' + collation ;
50
+ schema . client . query ( q , function ( error ) {
51
+ if ( ! error ) {
52
+ schema . client . query ( 'USE ' + s . database , callback ) ;
53
+ } else {
54
+ throw error ;
55
+ }
56
+ } ) ;
57
+ } else throw err ;
38
58
} else callback ( ) ;
39
59
} ) ;
40
60
41
61
// MySQL specific column types
42
62
schema . constructor . registerType ( function Point ( ) { } ) ;
63
+
64
+ schema . EnumFactory = EnumFactory ; // factory for Enums. Note that currently Enums can not be registered.
65
+
66
+
43
67
} ;
44
68
45
69
/**
@@ -175,8 +199,8 @@ MySQL.prototype.toDatabase = function (prop, val) {
175
199
} else {
176
200
return val ;
177
201
}
178
- }
179
- }
202
+ }
203
+ }
180
204
if ( ! prop ) return val ;
181
205
if ( prop . type . name === 'Number' ) return Number ( val ) ;
182
206
if ( prop . type . name === 'Date' ) {
@@ -187,6 +211,7 @@ MySQL.prototype.toDatabase = function (prop, val) {
187
211
return '"' + dateToMysql ( val ) + '"' ;
188
212
}
189
213
if ( prop . type . name == "Boolean" ) return val ? 1 : 0 ;
214
+ if ( typeof prop . type === 'function' ) return this . client . escape ( prop . type ( val ) ) ;
190
215
return this . client . escape ( val . toString ( ) ) ;
191
216
} ;
192
217
@@ -204,7 +229,7 @@ MySQL.prototype.fromDatabase = function (model, data) {
204
229
val = new Date ( val . toString ( ) . replace ( / G M T .* $ / , 'GMT' ) ) ;
205
230
break ;
206
231
case 'Boolean' :
207
- val = new Boolean ( val ) ;
232
+ val = Boolean ( val ) ;
208
233
break ;
209
234
}
210
235
}
@@ -238,7 +263,6 @@ MySQL.prototype.all = function all(model, filter, callback) {
238
263
}
239
264
240
265
}
241
- debugger ;
242
266
243
267
this . query ( sql , function ( err , data ) {
244
268
if ( err ) {
@@ -267,15 +291,15 @@ MySQL.prototype.all = function all(model, filter, callback) {
267
291
} else if ( conds [ key ] && conds [ key ] . constructor . name === 'Object' ) {
268
292
var condType = Object . keys ( conds [ key ] ) [ 0 ] ;
269
293
var sqlCond = keyEscaped ;
270
- if ( ( condType == 'inq' || condType == 'nin' ) && val . length == 0 ) {
294
+ if ( ( condType == 'inq' || condType == 'nin' ) && val . length == 0 ) {
271
295
cs . push ( condType == 'inq' ? 0 : 1 ) ;
272
296
return true ;
273
297
}
274
298
switch ( condType ) {
275
299
case 'gt' :
276
300
sqlCond += ' > ' ;
277
301
break ;
278
- case 'gte' :
302
+ case 'gte' :
279
303
sqlCond += ' >= ' ;
280
304
break ;
281
305
case 'lt' :
@@ -297,7 +321,6 @@ MySQL.prototype.all = function all(model, filter, callback) {
297
321
sqlCond += ' != ' ;
298
322
break ;
299
323
}
300
-
301
324
sqlCond += ( condType == 'inq' || condType == 'nin' ) ? '(' + val + ')' : val ;
302
325
cs . push ( sqlCond ) ;
303
326
} else if ( conds [ key ] && conds [ key ] . constructor . name === 'Array' ) {
@@ -308,7 +331,7 @@ MySQL.prototype.all = function all(model, filter, callback) {
308
331
309
332
sqlCond += ')' ;
310
333
cs . push ( sqlCond ) ;
311
- } else {
334
+ } else {
312
335
cs . push ( keyEscaped + ' = ' + val ) ;
313
336
}
314
337
} ) ;
@@ -528,9 +551,17 @@ MySQL.prototype.alterTable = function (model, actualFields, actualIndexes, done,
528
551
}
529
552
530
553
function changed ( newSettings , oldSettings ) {
531
- if ( oldSettings . Null === 'YES' && ( newSettings . allowNull === false || newSettings . null === false ) ) return true ;
532
- if ( oldSettings . Null === 'NO' && ! ( newSettings . allowNull === false || newSettings . null === false ) ) return true ;
533
- if ( oldSettings . Type . toUpperCase ( ) !== datatype ( newSettings ) ) return true ;
554
+ if ( oldSettings . Null === 'YES' ) { // Used to allow null and does not now.
555
+ if ( newSettings . allowNull === false ) return true ;
556
+ if ( newSettings . null === false ) return true ;
557
+ }
558
+ if ( oldSettings . Null === 'NO' ) { // Did not allow null and now does.
559
+ if ( newSettings . allowNull === true ) return true ;
560
+ if ( newSettings . null === true ) return true ;
561
+ if ( newSettings . null === undefined && newSettings . allowNull === undefined ) return true ;
562
+ }
563
+
564
+ if ( oldSettings . Type . toUpperCase ( ) !== datatype ( newSettings ) . toUpperCase ( ) ) return true ;
534
565
return false ;
535
566
}
536
567
} ;
@@ -597,8 +628,9 @@ MySQL.prototype.indexSettingsSQL = function (model, prop) {
597
628
598
629
MySQL . prototype . propertySettingsSQL = function ( model , prop ) {
599
630
var p = this . _models [ model ] . properties [ prop ] ;
600
- return datatype ( p ) + ' ' +
631
+ var line = datatype ( p ) + ' ' +
601
632
( p . allowNull === false || p [ 'null' ] === false ? 'NOT NULL' : 'NULL' ) ;
633
+ return line ;
602
634
} ;
603
635
604
636
function datatype ( p ) {
@@ -607,24 +639,183 @@ function datatype(p) {
607
639
default :
608
640
case 'String' :
609
641
case 'JSON' :
610
- dt = 'VARCHAR(' + ( p . limit || 255 ) + ')' ;
642
+ dt = columnType ( p , 'VARCHAR' ) ;
643
+ dt = stringOptionsByType ( p , dt ) ;
611
644
break ;
612
645
case 'Text' :
613
- dt = 'TEXT' ;
646
+ dt = columnType ( p , 'TEXT' ) ;
647
+ dt = stringOptionsByType ( p , dt ) ;
614
648
break ;
615
649
case 'Number' :
616
- dt = 'INT(' + ( p . limit || 11 ) + ')' ;
650
+ dt = columnType ( p , 'INT' ) ;
651
+ dt = numericOptionsByType ( p , dt ) ;
617
652
break ;
618
653
case 'Date' :
619
- dt = 'DATETIME' ;
654
+ dt = columnType ( p , 'DATETIME' ) ; // Currently doesn't need options.
620
655
break ;
621
656
case 'Boolean' :
622
- dt = 'TINYINT(1)' ;
657
+ dt = 'TINYINT(1)' ;
623
658
break ;
624
659
case 'Point' :
625
- dt = 'POINT' ;
660
+ dt = 'POINT' ;
661
+ break ;
662
+ case 'Enum' :
663
+ dt = 'ENUM(' + p . type . _string + ')' ;
664
+ dt = stringOptions ( p , dt ) ; // Enum columns can have charset/collation.
626
665
break ;
627
666
}
628
667
return dt ;
629
668
}
630
669
670
+ function columnType ( p , defaultType ) {
671
+ var dt = defaultType ;
672
+ if ( p . dataType ) {
673
+ dt = String ( p . dataType ) ;
674
+ }
675
+ return dt ;
676
+ }
677
+
678
+ function stringOptionsByType ( p , dt ) {
679
+ switch ( dt . toLowerCase ( ) ) {
680
+ default :
681
+ case 'varchar' :
682
+ case 'char' :
683
+ dt += '(' + ( p . limit || 255 ) + ')' ;
684
+ break ;
685
+
686
+ case 'text' :
687
+ case 'tinytext' :
688
+ case 'mediumtext' :
689
+ case 'longtext' :
690
+
691
+ break ;
692
+ }
693
+ dt = stringOptions ( p , dt ) ;
694
+ return dt ;
695
+ }
696
+
697
+ function stringOptions ( p , dt ) {
698
+ if ( p . charset ) {
699
+ dt += " CHARACTER SET " + p . charset ;
700
+ }
701
+ if ( p . collation ) {
702
+ dt += " COLLATE " + p . collation ;
703
+ }
704
+ return dt ;
705
+ }
706
+
707
+ function numericOptionsByType ( p , dt ) {
708
+ switch ( dt . toLowerCase ( ) ) {
709
+ default :
710
+ case 'tinyint' :
711
+ case 'smallint' :
712
+ case 'mediumint' :
713
+ case 'int' :
714
+ case 'integer' :
715
+ case 'bigint' :
716
+ dt = integerOptions ( p , dt ) ;
717
+ break ;
718
+
719
+ case 'decimal' :
720
+ case 'numeric' :
721
+ dt = fixedPointOptions ( p , dt ) ;
722
+ break ;
723
+
724
+ case 'float' :
725
+ case 'double' :
726
+ dt = floatingPointOptions ( p , dt ) ;
727
+ break ;
728
+ }
729
+ dt = unsigned ( p , dt ) ;
730
+ return dt ;
731
+ }
732
+
733
+ function floatingPointOptions ( p , dt ) {
734
+ var precision = 16 ;
735
+ var scale = 8 ;
736
+ if ( p . precision ) {
737
+ precision = Number ( p . precision ) ;
738
+ }
739
+ if ( p . scale ) {
740
+ scale = Number ( p . scale ) ;
741
+ }
742
+ if ( p . precision && p . scale ) {
743
+ dt += '(' + precision + ',' + scale + ')' ;
744
+ } else if ( p . precision ) {
745
+ dt += '(' + precision + ')' ;
746
+ }
747
+ return dt ;
748
+ }
749
+
750
+ /* @TODO : Change fixed point to use an arbitrary precision arithmetic library. */
751
+ /* Currently fixed point will lose precision because it's turned to non-fixed in */
752
+ /* JS. Also, defaulting column to (9,2) and not allowing non-specified 'DECIMAL' */
753
+ /* declaration which would default to DECIMAL(10,0). Instead defaulting to (9,2). */
754
+ function fixedPointOptions ( p , dt ) {
755
+ var precision = 9 ;
756
+ var scale = 2 ;
757
+ if ( p . precision ) {
758
+ precision = Number ( p . precision ) ;
759
+ }
760
+ if ( p . scale ) {
761
+ scale = Number ( p . scale ) ;
762
+ }
763
+ dt += '(' + precision + ',' + scale + ')' ;
764
+ return dt ;
765
+ }
766
+
767
+ function integerOptions ( p , dt ) {
768
+ var tmp = 0 ;
769
+ if ( p . display || p . limit ) {
770
+ tmp = Number ( p . display || p . limit ) ;
771
+ }
772
+ if ( tmp > 0 ) {
773
+ dt += '(' + tmp + ')' ;
774
+ } else if ( p . unsigned ) {
775
+ switch ( dt . toLowerCase ( ) ) {
776
+ default :
777
+ case 'int' :
778
+ dt += '(10)' ;
779
+ break ;
780
+ case 'mediumint' :
781
+ dt += '(8)' ;
782
+ break ;
783
+ case 'smallint' :
784
+ dt += '(5)' ;
785
+ break ;
786
+ case 'tinyint' :
787
+ dt += '(3)' ;
788
+ break ;
789
+ case 'bigint' :
790
+ dt += '(20)' ;
791
+ break ;
792
+ }
793
+ } else {
794
+ switch ( dt . toLowerCase ( ) ) {
795
+ default :
796
+ case 'int' :
797
+ dt += '(11)' ;
798
+ break ;
799
+ case 'mediumint' :
800
+ dt += '(9)' ;
801
+ break ;
802
+ case 'smallint' :
803
+ dt += '(6)' ;
804
+ break ;
805
+ case 'tinyint' :
806
+ dt += '(4)' ;
807
+ break ;
808
+ case 'bigint' :
809
+ dt += '(20)' ;
810
+ break ;
811
+ }
812
+ }
813
+ return dt ;
814
+ }
815
+
816
+ function unsigned ( p , dt ) {
817
+ if ( p . unsigned ) {
818
+ dt += ' UNSIGNED' ;
819
+ }
820
+ return dt ;
821
+ }
0 commit comments