1
+ import { ExasolDriver } from '@exasol/exasol-driver-ts' ;
2
+ import { Node } from '../../model/interface/node' ;
3
+ import { IConnection } from './connection' ;
4
+ import { Console } from '../../common/Console' ;
5
+ import { EventEmitter } from 'events' ;
6
+ import { ConnectionNode } from '../../model/database/connectionNode' ;
7
+ import { DatabaseType } from '../../common/constants' ;
8
+ import { DbTreeDataProvider } from '../../provider/treeDataProvider' ;
9
+ import { CommandKey } from '../../model/interface/node' ;
10
+ import { ConnectionManager } from '../../service/connectionManager' ;
11
+ import { GlobalState } from '../../common/state' ;
12
+ import { CacheKey } from '../../common/constants' ;
13
+ import { WebSocket } from 'ws' ;
14
+ import { DatabaseCache } from '../common/databaseCache' ;
15
+ import { SchemaNode } from '../../model/database/schemaNode' ;
16
+ import * as vscode from 'vscode' ;
17
+
18
+ type queryCallback = ( error : Error | null , rows ?: any [ ] , fields ?: any [ ] ) => void ;
19
+
20
+ export class ExasolConnection extends IConnection {
21
+ private driver : ExasolDriver ;
22
+ protected node : Node ;
23
+ private connected : boolean ;
24
+ private websocket : WebSocket | null = null ;
25
+
26
+ constructor ( node : Node ) {
27
+ super ( ) ;
28
+ this . node = node ;
29
+ this . node . host = node . host || '127.0.0.1' ;
30
+ this . node . port = node . port || 8563 ;
31
+ this . node . user = node . user || 'sys' ;
32
+ this . node . password = node . password || '' ;
33
+ this . connected = false ;
34
+ }
35
+
36
+ public connect ( callback : ( err : Error ) => void ) : void {
37
+ if ( this . connected ) {
38
+ callback ( null ) ;
39
+ return ;
40
+ }
41
+
42
+ Console . log ( '[Exasol] Attempting to connect to: ' + this . node . host ) ;
43
+
44
+ const websocketFactory = ( url : string ) => {
45
+ if ( this . websocket ) {
46
+ return this . websocket ;
47
+ }
48
+
49
+ this . websocket = new WebSocket ( url ) ;
50
+ this . websocket . on ( 'error' , ( ) => this . websocket = null ) ;
51
+ this . websocket . on ( 'close' , ( ) => {
52
+ this . websocket = null ;
53
+ this . connected = false ;
54
+ } ) ;
55
+
56
+ return this . websocket ;
57
+ } ;
58
+
59
+ this . driver = new ExasolDriver (
60
+ websocketFactory ,
61
+ {
62
+ host : String ( this . node . host . trim ( ) ) ,
63
+ port : Number ( this . node . port ) ,
64
+ user : String ( this . node . user ) ,
65
+ password : String ( this . node . password ) ,
66
+ clientName : 'VSCode Database Client' ,
67
+ clientVersion : '3.9.8' ,
68
+ autocommit : true ,
69
+ encryption : true ,
70
+ compression : false ,
71
+ fetchSize : 1000 ,
72
+ resultSetMaxRows : 100000
73
+ }
74
+ ) ;
75
+
76
+ this . driver . connect ( )
77
+ . then ( ( ) => {
78
+ this . connected = true ;
79
+ callback ( null ) ;
80
+ } )
81
+ . catch ( error => {
82
+ this . connected = false ;
83
+ callback ( error ) ;
84
+ } ) ;
85
+ }
86
+
87
+ public query ( sql : string , callback ?: queryCallback ) : void | EventEmitter ;
88
+ public query ( sql : string , values : any , callback ?: queryCallback ) : void | EventEmitter ;
89
+ public query ( sql : any , values ?: any , callback ?: any ) {
90
+ if ( ! callback && values instanceof Function ) {
91
+ callback = values ;
92
+ }
93
+
94
+ const event = new EventEmitter ( ) ;
95
+
96
+ const executeQuery = ( ) => {
97
+ try {
98
+ this . driver . query ( sql ) . then ( result => {
99
+ const rows = result . getRows ( ) || [ ] ;
100
+ const columns = result . getColumns ( ) || [ ] ;
101
+
102
+ Console . log ( '[Exasol] Query result:' ) ;
103
+ Console . log ( '[Exasol] Columns: ' + JSON . stringify ( columns , null , 2 ) ) ;
104
+ Console . log ( '[Exasol] Row count: ' + rows . length ) ;
105
+ Console . log ( '[Exasol] Rows: ' + JSON . stringify ( rows , null , 2 ) ) ;
106
+
107
+ if ( ! callback ) {
108
+ if ( rows . length === 0 ) {
109
+ event . emit ( "end" ) ;
110
+ }
111
+ for ( let i = 1 ; i <= rows . length ; i ++ ) {
112
+ const row = rows [ i - 1 ] ;
113
+ event . emit ( "result" , this . convertToDump ( row ) , rows . length === i ) ;
114
+ }
115
+ } else {
116
+ // 将结果转换为标准格式
117
+ const fields = columns . map ( col => ( {
118
+ name : col . name ,
119
+ dataType : col . dataType
120
+ } ) ) ;
121
+
122
+ // 如果是非 SELECT 语句
123
+ if ( ! columns . length ) {
124
+ callback ( null , { affectedRows : rows . length } ) ;
125
+ } else {
126
+ // 将行数据转换为对象格式,并处理特殊的 schema 查询
127
+ const formattedRows = rows . map ( ( row , rowIndex ) => {
128
+ const obj : { [ key : string ] : any } = { } ;
129
+ // 处理数组格式的行数据
130
+ if ( Array . isArray ( row ) ) {
131
+ columns . forEach ( ( col , colIndex ) => {
132
+ const value = row [ colIndex ] ;
133
+ if ( col . name === 'SCHEMA_NAME' ) {
134
+ obj . schema = value ;
135
+ obj . Database = value ;
136
+ } else if ( col . name === 'TABLE_NAME' ) {
137
+ obj . name = value ;
138
+ } else if ( col . name === 'COLUMN_NAME' ) {
139
+ obj . name = value ;
140
+ } else if ( col . name === 'COLUMN_TYPE' ) {
141
+ obj . type = value ;
142
+ obj . simpleType = value ;
143
+ } else if ( col . name === 'IS_NULLABLE' ) {
144
+ obj . nullable = value ;
145
+ } else if ( col . name === 'COLUMN_DEFAULT' ) {
146
+ obj . defaultValue = value ;
147
+ } else if ( col . name === 'name' ) {
148
+ obj . name = value ;
149
+ } else if ( col . name === 'type' ) {
150
+ obj . type = value ;
151
+ obj . simpleType = value ;
152
+ } else {
153
+ obj [ col . name ] = value ;
154
+ }
155
+ } ) ;
156
+ } else {
157
+ // 处理对象格式的行数据
158
+ if ( row . SCHEMA_NAME ) {
159
+ obj . schema = row . SCHEMA_NAME ;
160
+ obj . Database = row . SCHEMA_NAME ;
161
+ }
162
+ if ( row . TABLE_NAME ) {
163
+ obj . name = row . TABLE_NAME ;
164
+ }
165
+ if ( row . COLUMN_NAME ) {
166
+ obj . name = row . COLUMN_NAME ;
167
+ }
168
+ if ( row . COLUMN_TYPE ) {
169
+ obj . type = row . COLUMN_TYPE ;
170
+ obj . simpleType = row . COLUMN_TYPE ;
171
+ }
172
+ if ( row . IS_NULLABLE ) {
173
+ obj . nullable = row . IS_NULLABLE ;
174
+ }
175
+ if ( row . COLUMN_DEFAULT ) {
176
+ obj . defaultValue = row . COLUMN_DEFAULT ;
177
+ }
178
+ if ( row . name ) {
179
+ obj . name = row . name ;
180
+ }
181
+ if ( row . type ) {
182
+ obj . type = row . type ;
183
+ obj . simpleType = row . type ;
184
+ }
185
+ Object . keys ( row ) . forEach ( key => {
186
+ if ( ! [ 'name' , 'type' ] . includes ( key ) ) {
187
+ obj [ key ] = row [ key ] ;
188
+ }
189
+ } ) ;
190
+ }
191
+ return obj ;
192
+ } ) ;
193
+
194
+ // 如果是查询 schema 列表,确保每个结果都有 schema 字段
195
+ if ( sql . includes ( 'SYS.EXA_SCHEMAS' ) ) {
196
+ Console . log ( '[Exasol] Schema list: ' + JSON . stringify ( formattedRows ) ) ;
197
+ }
198
+
199
+ callback ( null , formattedRows , fields ) ;
200
+ }
201
+ }
202
+ } ) . catch ( err => {
203
+ if ( callback ) {
204
+ callback ( err ) ;
205
+ }
206
+ event . emit ( "error" , err . message ) ;
207
+ } ) ;
208
+ } catch ( err ) {
209
+ if ( callback ) {
210
+ callback ( err ) ;
211
+ }
212
+ event . emit ( "error" , err . message ) ;
213
+ }
214
+ } ;
215
+
216
+ if ( ! this . connected ) {
217
+ this . connect ( ( err ) => {
218
+ if ( err ) {
219
+ if ( callback ) {
220
+ callback ( err ) ;
221
+ }
222
+ event . emit ( "error" , err . message ) ;
223
+ } else {
224
+ executeQuery ( ) ;
225
+ }
226
+ } ) ;
227
+ } else {
228
+ executeQuery ( ) ;
229
+ }
230
+
231
+ return event ;
232
+ }
233
+
234
+ public close ( callback ?: ( err : Error ) => void ) : void {
235
+ if ( this . driver ) {
236
+ this . driver . close ( )
237
+ . then ( ( ) => {
238
+ this . connected = false ;
239
+ if ( this . websocket ) {
240
+ this . websocket . close ( ) ;
241
+ this . websocket = null ;
242
+ }
243
+ if ( callback ) callback ( null ) ;
244
+ } )
245
+ . catch ( error => {
246
+ if ( callback ) callback ( error ) ;
247
+ } ) ;
248
+ } else if ( callback ) {
249
+ callback ( null ) ;
250
+ }
251
+ }
252
+
253
+ public end ( callback ?: ( err : Error ) => void ) : void {
254
+ this . close ( callback ) ;
255
+ }
256
+
257
+ public beginTransaction ( callback ?: ( err : Error ) => void ) : void {
258
+ this . query ( 'START TRANSACTION' , callback ) ;
259
+ }
260
+
261
+ public commit ( callback ?: ( err : Error ) => void ) : void {
262
+ this . query ( 'COMMIT' , callback ) ;
263
+ }
264
+
265
+ public rollback ( callback ?: ( err : Error ) => void ) : void {
266
+ this . query ( 'ROLLBACK' , callback ) ;
267
+ }
268
+
269
+ public isAlive ( ) : boolean {
270
+ return this . connected ;
271
+ }
272
+ }
0 commit comments