@@ -2,16 +2,155 @@ import * as sync_rules from '@powersync/service-sync-rules';
2
2
import { ExpressionType } from '@powersync/service-sync-rules' ;
3
3
import { ColumnDescriptor } from '@powersync/service-core' ;
4
4
import mysql from 'mysql2' ;
5
+ import { JSONBig , JsonContainer } from '@powersync/service-jsonbig' ;
6
+ import { ColumnDefinition , TableMapEntry } from '@powersync/mysql-zongji' ;
5
7
6
- export function toSQLiteRow ( row : Record < string , any > , columns ?: Map < string , ColumnDescriptor > ) : sync_rules . SqliteRow {
8
+ export enum ADDITIONAL_MYSQL_TYPES {
9
+ DATETIME2 = 18 ,
10
+ TIMESTAMP2 = 17 ,
11
+ BINARY = 100 ,
12
+ VARBINARY = 101 ,
13
+ TEXT = 102
14
+ }
15
+
16
+ export const MySQLTypesMap : { [ key : number ] : string } = { } ;
17
+ for ( const [ name , code ] of Object . entries ( mysql . Types ) ) {
18
+ MySQLTypesMap [ code as number ] = name ;
19
+ }
20
+ for ( const [ name , code ] of Object . entries ( ADDITIONAL_MYSQL_TYPES ) ) {
21
+ MySQLTypesMap [ code as number ] = name ;
22
+ }
23
+
24
+ export function toColumnDescriptors ( columns : mysql . FieldPacket [ ] ) : Map < string , ColumnDescriptor > ;
25
+ export function toColumnDescriptors ( tableMap : TableMapEntry ) : Map < string , ColumnDescriptor > ;
26
+
27
+ export function toColumnDescriptors ( columns : mysql . FieldPacket [ ] | TableMapEntry ) : Map < string , ColumnDescriptor > {
28
+ const columnMap = new Map < string , ColumnDescriptor > ( ) ;
29
+ if ( Array . isArray ( columns ) ) {
30
+ for ( const column of columns ) {
31
+ columnMap . set ( column . name , toColumnDescriptorFromFieldPacket ( column ) ) ;
32
+ }
33
+ } else {
34
+ for ( const column of columns . columns ) {
35
+ columnMap . set ( column . name , toColumnDescriptorFromDefinition ( column ) ) ;
36
+ }
37
+ }
38
+
39
+ return columnMap ;
40
+ }
41
+
42
+ export function toColumnDescriptorFromFieldPacket ( column : mysql . FieldPacket ) : ColumnDescriptor {
43
+ let typeId = column . type ! ;
44
+ const BINARY_FLAG = 128 ;
45
+ const MYSQL_ENUM_FLAG = 256 ;
46
+ const MYSQL_SET_FLAG = 2048 ;
47
+
48
+ switch ( column . type ) {
49
+ case mysql . Types . STRING :
50
+ if ( ( ( column . flags as number ) & BINARY_FLAG ) !== 0 ) {
51
+ typeId = ADDITIONAL_MYSQL_TYPES . BINARY ;
52
+ } else if ( ( ( column . flags as number ) & MYSQL_ENUM_FLAG ) !== 0 ) {
53
+ typeId = mysql . Types . ENUM ;
54
+ } else if ( ( ( column . flags as number ) & MYSQL_SET_FLAG ) !== 0 ) {
55
+ typeId = mysql . Types . SET ;
56
+ }
57
+ break ;
58
+
59
+ case mysql . Types . VAR_STRING :
60
+ typeId = ( ( column . flags as number ) & BINARY_FLAG ) !== 0 ? ADDITIONAL_MYSQL_TYPES . VARBINARY : column . type ;
61
+ break ;
62
+ case mysql . Types . BLOB :
63
+ typeId = ( ( column . flags as number ) & BINARY_FLAG ) === 0 ? ADDITIONAL_MYSQL_TYPES . TEXT : column . type ;
64
+ break ;
65
+ }
66
+
67
+ const columnType = MySQLTypesMap [ typeId ] ;
68
+
69
+ return {
70
+ name : column . name ,
71
+ type : columnType ,
72
+ typeId : typeId
73
+ } ;
74
+ }
75
+
76
+ export function toColumnDescriptorFromDefinition ( column : ColumnDefinition ) : ColumnDescriptor {
77
+ let typeId = column . type ;
78
+
79
+ switch ( column . type ) {
80
+ case mysql . Types . STRING :
81
+ typeId = ! column . charset ? ADDITIONAL_MYSQL_TYPES . BINARY : column . type ;
82
+ break ;
83
+ case mysql . Types . VAR_STRING :
84
+ case mysql . Types . VARCHAR :
85
+ typeId = ! column . charset ? ADDITIONAL_MYSQL_TYPES . VARBINARY : column . type ;
86
+ break ;
87
+ case mysql . Types . BLOB :
88
+ typeId = column . charset ? ADDITIONAL_MYSQL_TYPES . TEXT : column . type ;
89
+ break ;
90
+ }
91
+
92
+ const columnType = MySQLTypesMap [ typeId ] ;
93
+
94
+ return {
95
+ name : column . name ,
96
+ type : columnType ,
97
+ typeId : typeId
98
+ } ;
99
+ }
100
+
101
+ export function toSQLiteRow ( row : Record < string , any > , columns : Map < string , ColumnDescriptor > ) : sync_rules . SqliteRow {
7
102
for ( let key in row ) {
8
- if ( row [ key ] instanceof Date ) {
9
- const column = columns ?. get ( key ) ;
10
- if ( column ?. typeId == mysql . Types . DATE ) {
11
- // Only parse the date part
12
- row [ key ] = row [ key ] . toISOString ( ) . split ( 'T' ) [ 0 ] ;
13
- } else {
14
- row [ key ] = row [ key ] . toISOString ( ) ;
103
+ // We are very much expecting the column to be there
104
+ const column = columns . get ( key ) ! ;
105
+
106
+ if ( row [ key ] !== null ) {
107
+ switch ( column . typeId ) {
108
+ case mysql . Types . DATE :
109
+ // Only parse the date part
110
+ row [ key ] = row [ key ] . toISOString ( ) . split ( 'T' ) [ 0 ] ;
111
+ break ;
112
+ case mysql . Types . DATETIME :
113
+ case ADDITIONAL_MYSQL_TYPES . DATETIME2 :
114
+ case mysql . Types . TIMESTAMP :
115
+ case ADDITIONAL_MYSQL_TYPES . TIMESTAMP2 :
116
+ row [ key ] = row [ key ] . toISOString ( ) ;
117
+ break ;
118
+ case mysql . Types . JSON :
119
+ if ( typeof row [ key ] === 'string' ) {
120
+ row [ key ] = new JsonContainer ( row [ key ] ) ;
121
+ }
122
+ break ;
123
+ case mysql . Types . BIT :
124
+ case mysql . Types . BLOB :
125
+ case mysql . Types . TINY_BLOB :
126
+ case mysql . Types . MEDIUM_BLOB :
127
+ case mysql . Types . LONG_BLOB :
128
+ case ADDITIONAL_MYSQL_TYPES . BINARY :
129
+ case ADDITIONAL_MYSQL_TYPES . VARBINARY :
130
+ row [ key ] = new Uint8Array ( Object . values ( row [ key ] ) ) ;
131
+ break ;
132
+ case mysql . Types . LONGLONG :
133
+ if ( typeof row [ key ] === 'string' ) {
134
+ row [ key ] = BigInt ( row [ key ] ) ;
135
+ } else if ( typeof row [ key ] === 'number' ) {
136
+ // Zongji returns BIGINT as a number when it can be represented as a number
137
+ row [ key ] = BigInt ( row [ key ] ) ;
138
+ }
139
+ break ;
140
+ case mysql . Types . TINY :
141
+ case mysql . Types . SHORT :
142
+ case mysql . Types . LONG :
143
+ case mysql . Types . INT24 :
144
+ // Handle all integer values a BigInt
145
+ if ( typeof row [ key ] === 'number' ) {
146
+ row [ key ] = BigInt ( row [ key ] ) ;
147
+ }
148
+ break ;
149
+ case mysql . Types . SET :
150
+ // Convert to JSON array from string
151
+ const values = row [ key ] . split ( ',' ) ;
152
+ row [ key ] = JSONBig . stringify ( values ) ;
153
+ break ;
15
154
}
16
155
}
17
156
}
0 commit comments