peasys-ruby 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/active_record/connection_adapters/peasys/column.rb +20 -0
- data/lib/active_record/connection_adapters/peasys/database_statements.rb +209 -0
- data/lib/active_record/connection_adapters/peasys/quoting.rb +99 -0
- data/lib/active_record/connection_adapters/peasys/schema_creation.rb +27 -0
- data/lib/active_record/connection_adapters/peasys/schema_definitions.rb +10 -0
- data/lib/active_record/connection_adapters/peasys/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/peasys/schema_statements.rb +431 -0
- data/lib/active_record/connection_adapters/peasys_adapter.rb +207 -0
- data/lib/arel/visitors/peasys.rb +53 -0
- data/lib/pea_client.rb +481 -477
- data/lib/pea_exception.rb +30 -30
- data/lib/pea_response.rb +299 -299
- data/lib/peasys-ruby.rb +11 -0
- metadata +67 -8
data/lib/pea_client.rb
CHANGED
|
@@ -1,478 +1,482 @@
|
|
|
1
|
-
require 'socket'
|
|
2
|
-
require 'json'
|
|
3
|
-
require 'uri'
|
|
4
|
-
require 'net/http'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# +
|
|
15
|
-
# +
|
|
16
|
-
# +
|
|
17
|
-
# +
|
|
18
|
-
# +
|
|
19
|
-
# +
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# raises a
|
|
23
|
-
# raises a
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
#
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
#
|
|
260
|
-
#
|
|
261
|
-
#
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
#
|
|
279
|
-
#
|
|
280
|
-
#
|
|
281
|
-
#
|
|
282
|
-
#
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
#
|
|
302
|
-
#
|
|
303
|
-
#
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
#
|
|
318
|
-
#
|
|
319
|
-
#
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
description =
|
|
331
|
-
description = description.
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
@tcp_client.
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
def
|
|
353
|
-
@
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def
|
|
357
|
-
@
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
def
|
|
361
|
-
@
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
def
|
|
365
|
-
@
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
def
|
|
369
|
-
@
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
def
|
|
373
|
-
@
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
def
|
|
377
|
-
@
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
pointer +=
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
pointer
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
pointer
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
pointer
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
data +=
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
|
|
1
|
+
require 'socket'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PeaClient
|
|
8
|
+
@@end_pack = "dipsjbiemg"
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Initialize a new instance of the PeaClient class. Initiates a connexion with the AS/400 server.
|
|
12
|
+
#
|
|
13
|
+
# Params:
|
|
14
|
+
# +partition_name+:: DNS name (name of the partition) of the remote AS/400 server.
|
|
15
|
+
# +port+:: Port used for the data exchange between the client and the server.
|
|
16
|
+
# +username+:: Username of the AS/400 profile used for connexion.
|
|
17
|
+
# +password+:: Password of the AS/400 profile used for connexion.
|
|
18
|
+
# +id_client+:: ID of the client account on the DIPS website.
|
|
19
|
+
# +online_version+:: Set to true if you want to use the online version of Peasys (<see cref="https://dips400.com/docs/connexion"/>).
|
|
20
|
+
# +retrieve_statistics+:: Set to true if you want the statistics of the license key use to be collect.
|
|
21
|
+
#
|
|
22
|
+
# raises a PeaInvalidCredentialsError if credentials are empty.
|
|
23
|
+
# raises a PeaConnexionError if an error occured during the connexion process.
|
|
24
|
+
# raises a PeaInvalidCredentialsError if IBMi credentials are invalid.
|
|
25
|
+
|
|
26
|
+
def initialize(ip_adress, partition_name, port, username, password, id_client, online_version, retrieve_statistics)
|
|
27
|
+
|
|
28
|
+
if ip_adress.empty? || username.empty? || password.empty?
|
|
29
|
+
raise PeaInvalidCredentialsError.new("Parameters of the PeaClient should not be either null or empty")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@ip_adress = ip_adress
|
|
33
|
+
@id_client = id_client
|
|
34
|
+
@partition_name = partition_name
|
|
35
|
+
@username = username
|
|
36
|
+
@password = password
|
|
37
|
+
@port = port
|
|
38
|
+
@online_version = online_version
|
|
39
|
+
@retrieve_statistics = retrieve_statistics
|
|
40
|
+
@connexion_status = "ds"
|
|
41
|
+
@connexion_message = "ds"
|
|
42
|
+
|
|
43
|
+
token = "xqdsg27010wmca6052009050000000IDSP1tiupozxreybjhlk"
|
|
44
|
+
if online_version
|
|
45
|
+
begin
|
|
46
|
+
uri = URI("https://dips400.com/api/license-key/retrieve-token/#{partition_name}/#{id_client}")
|
|
47
|
+
res = Net::HTTP.get_response(uri)
|
|
48
|
+
|
|
49
|
+
data = JSON.parse(res.body)
|
|
50
|
+
token = data["token"]
|
|
51
|
+
is_valid = data["isValid"]
|
|
52
|
+
|
|
53
|
+
if not(is_valid)
|
|
54
|
+
raise PeaInvalidLicenseKeyError.new("Your subscription is not valid, visit https://dips400.com/account/subscriptions for more information.")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
rescue PeaInvalidLicenseKeyError => e
|
|
58
|
+
raise e
|
|
59
|
+
rescue StandardError => error
|
|
60
|
+
# If dips400.com doesn't respond, let's try an affline verification with the offline token
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
begin
|
|
65
|
+
@tcp_client = TCPSocket.new ip_adress, port
|
|
66
|
+
rescue => error
|
|
67
|
+
raise PeaConnexionError.new(error.full_message)
|
|
68
|
+
else
|
|
69
|
+
login = username.ljust(10, " ") + token.ljust(50, " ") + password
|
|
70
|
+
@tcp_client.send(login, 0)
|
|
71
|
+
@connexion_status = @tcp_client.read(1)
|
|
72
|
+
|
|
73
|
+
case @connexion_status
|
|
74
|
+
when "1"
|
|
75
|
+
@connexion_message = "Connected"
|
|
76
|
+
@connexion_status = 1
|
|
77
|
+
when "2"
|
|
78
|
+
@connexion_message = "Unable to set profile, check profile validity."
|
|
79
|
+
@connexion_status = 2
|
|
80
|
+
raise PeaConnexionError.new("Unable to set profile, check profile validity.")
|
|
81
|
+
when "3"
|
|
82
|
+
@connexion_message = "Invalid credential"
|
|
83
|
+
@connexion_status = 1
|
|
84
|
+
raise PeaInvalidCredentialsError.new("Invalid userName or password, check again")
|
|
85
|
+
when "B"
|
|
86
|
+
@connexion_message = "Peasys Online : your token connexion is no longer valid, retry to connect."
|
|
87
|
+
@connexion_status = 5
|
|
88
|
+
raise PeaConnexionError.new("Peasys Online : your token connexion is no longer valid, retry to connect.")
|
|
89
|
+
when "D"
|
|
90
|
+
@connexion_message = "Peasys Online : the partition name you provided doesn't match the actual name of the machine."
|
|
91
|
+
@connexion_status = 6
|
|
92
|
+
raise PeaConnexionError.new("Peasys Online : the partition name you provided doesn't match the actual name of the machine.")
|
|
93
|
+
when "E"
|
|
94
|
+
@connexion_message = "You reached the max number of simultaneously connected peasys users for that partition and license key. Contact us for upgrading your license."
|
|
95
|
+
@connexion_status = 7
|
|
96
|
+
raise PeaConnexionError.new("You reached the max number of simultaneously connected peasys users for that partition and license key. Contact us for upgrading your license.")
|
|
97
|
+
when "F"
|
|
98
|
+
@connexion_message = "Your license is no longer valid. Subscribe to another license in order to continue using Peasys."
|
|
99
|
+
@connexion_status = 8
|
|
100
|
+
raise PeaConnexionError.new("Your license is no longer valid. Subscribe to another license in order to continue using Peasys.")
|
|
101
|
+
when "0" , "A" , "C" , "G"
|
|
102
|
+
@connexion_message = "Error linked to DIPS source code. Please, contact us immediatly to fix the issue."
|
|
103
|
+
@connexion_status = -1
|
|
104
|
+
raise PeaConnexionError.new("Error linked to DIPS source code. Please, contact us immediatly to fix the issue.")
|
|
105
|
+
else
|
|
106
|
+
raise PeaConnexionError.new("Exception during connexion process, contact us for more informations")
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
##
|
|
112
|
+
# Sends the SELECT SQL query to the server that execute it and retrieve the desired data.
|
|
113
|
+
#
|
|
114
|
+
# Params:
|
|
115
|
+
# +query+:: SQL query that should start with the SELECT keyword.
|
|
116
|
+
#
|
|
117
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty or doesn't start with SELECT.
|
|
118
|
+
|
|
119
|
+
def execute_select(query)
|
|
120
|
+
if query.empty?
|
|
121
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
122
|
+
end
|
|
123
|
+
if !query.upcase.start_with?("SELECT") && !query.upcase.start_with?("WITH")
|
|
124
|
+
raise PeaInvalidSyntaxQueryError.new("Query should start with the SELECT or WITH SQL keyword")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
result, list_name, nb_row, sql_state, sql_message = build_data(query)
|
|
128
|
+
|
|
129
|
+
return PeaSelectResponse.new(sql_message.eql?("00000"), sql_message, sql_state, result, nb_row, list_name)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
##
|
|
133
|
+
# Sends the UPDATE SQL query to the server that execute it and retrieve the desired data.
|
|
134
|
+
#
|
|
135
|
+
# Params:
|
|
136
|
+
# +query+:: SQL query that should start with the UPDATE keyword.
|
|
137
|
+
#
|
|
138
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty or doesn't start with UPDATE.
|
|
139
|
+
|
|
140
|
+
def execute_update(query)
|
|
141
|
+
if query.empty?
|
|
142
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
143
|
+
end
|
|
144
|
+
if !query.upcase.start_with?("UPDATE")
|
|
145
|
+
raise PeaInvalidSyntaxQueryError.new("Query should start with the UPDATE SQL keyword")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
nb_row, sql_state, sql_message = modify_table(query)
|
|
149
|
+
has_succeeded = sql_state.eql?("00000") || sql_state.eql?("01504")
|
|
150
|
+
|
|
151
|
+
return PeaUpdateResponse.new(has_succeeded, sql_message, sql_state, nb_row)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
##
|
|
155
|
+
# Sends the CREATE SQL query to the server that execute it and retrieve the desired data.
|
|
156
|
+
#
|
|
157
|
+
# Params:
|
|
158
|
+
# +query+:: SQL query that should start with the CREATE keyword.
|
|
159
|
+
#
|
|
160
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty or doesn't start with CREATE.
|
|
161
|
+
|
|
162
|
+
def execute_create(query)
|
|
163
|
+
if query.empty?
|
|
164
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
165
|
+
end
|
|
166
|
+
if !query.upcase.start_with?("CREATE")
|
|
167
|
+
raise PeaInvalidSyntaxQueryError.new("Query should start with the CREATE SQL keyword")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
nb_row, sql_state, sql_message = modify_table(query)
|
|
171
|
+
|
|
172
|
+
query_words = query.split(' ')
|
|
173
|
+
tb_schema = Hash.new()
|
|
174
|
+
if query_words[1].upcase.eql?("TABLE")
|
|
175
|
+
names = query_words[2].split('/')
|
|
176
|
+
tb_query = """SELECT COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, LENGTH, NUMERIC_SCALE, IS_NULLABLE, IS_UPDATABLE, NUMERIC_PRECISION FROM
|
|
177
|
+
QSYS2.SYSCOLUMNS WHERE SYSTEM_TABLE_NAME = '""" + names[1].upcase + "' AND SYSTEM_TABLE_SCHEMA = '" + names[0].upcase + "'"
|
|
178
|
+
|
|
179
|
+
result, list_name, nb_row, sql_state, sql_message = build_data(tb_query)
|
|
180
|
+
|
|
181
|
+
for i in 0..(nb_row - 1) do
|
|
182
|
+
tb_schema["column_name"] = ColumnInfo.new(result["COLUMN_NAME"][i], result["ORDINAL_POSITION"][i], result["DATA_TYPE"][i], result["LENGTH"][i],
|
|
183
|
+
result["NUMERIC_SCALE"][i], result["IS_NULLABLE"][i], result["IS_UPDATABLE"][i], result["NUMERIC_PRECISION"][i])
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
case query_words[1].upcase
|
|
188
|
+
when "TABLE"
|
|
189
|
+
return PeaCreateResponse.new(sql_message.eql?("00000"), sql_message, sql_state, "", "", tb_schema)
|
|
190
|
+
when "INDEX"
|
|
191
|
+
return PeaCreateResponse.new(sql_message.eql?("00000"), sql_message, sql_state, "", query_words[2], "")
|
|
192
|
+
when "DATABASE"
|
|
193
|
+
return PeaCreateResponse.new(sql_message.eql?("00000"), sql_message, sql_state, query_words[2], "", "")
|
|
194
|
+
else
|
|
195
|
+
raise PeaConnexionError.new("Exception during connexion process, contact us for more informations")
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
##
|
|
200
|
+
# Sends the DELETE SQL query to the server that execute it and retrieve the desired data.
|
|
201
|
+
#
|
|
202
|
+
# Params:
|
|
203
|
+
# +query+:: SQL query that should start with the DELETE keyword.
|
|
204
|
+
#
|
|
205
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty or doesn't start with DELETE.
|
|
206
|
+
|
|
207
|
+
def execute_delete(query)
|
|
208
|
+
if query.empty?
|
|
209
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
210
|
+
end
|
|
211
|
+
if !query.upcase.start_with?("DELETE")
|
|
212
|
+
raise PeaInvalidSyntaxQueryError.new("Query should start with the DELETE SQL keyword")
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
nb_row, sql_state, sql_message = modify_table(query)
|
|
216
|
+
|
|
217
|
+
return PeaDeleteResponse.new(sql_message.eql?("00000"), sql_message, sql_state, nb_row)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
##
|
|
221
|
+
# Sends the ALTER SQL query to the server that execute it and retrieve the desired data.
|
|
222
|
+
#
|
|
223
|
+
# Params:
|
|
224
|
+
# +query+:: SQL query that should start with the ALTER keyword.
|
|
225
|
+
#
|
|
226
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty or doesn't start with ALTER.
|
|
227
|
+
|
|
228
|
+
def execute_alter(query, retrieve_table_schema)
|
|
229
|
+
if query.empty?
|
|
230
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
231
|
+
end
|
|
232
|
+
if !query.upcase.start_with?("ALTER")
|
|
233
|
+
raise PeaInvalidSyntaxQueryError.new("Query should start with the ALTER SQL keyword")
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
nb_row, sql_state, sql_message = modify_table(query)
|
|
237
|
+
|
|
238
|
+
tb_schema = Hash.new()
|
|
239
|
+
if retrieve_table_schema
|
|
240
|
+
query_words = query.split(' ')
|
|
241
|
+
names = query_words[2].split('/')
|
|
242
|
+
tb_query = """SELECT COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, LENGTH, NUMERIC_SCALE, IS_NULLABLE, IS_UPDATABLE, NUMERIC_PRECISION FROM
|
|
243
|
+
QSYS2.SYSCOLUMNS WHERE SYSTEM_TABLE_NAME = '""" + names[1].upcase + "' AND SYSTEM_TABLE_SCHEMA = '" + names[0].upcase + "'"
|
|
244
|
+
|
|
245
|
+
result, list_name, nb_row, sql_state, sql_message = build_data(tb_query)
|
|
246
|
+
|
|
247
|
+
for i in 0..(nb_row - 1) do
|
|
248
|
+
tb_schema["column_name"] = ColumnInfo.new(result["COLUMN_NAME"][i], result["ORDINAL_POSITION"][i], result["DATA_TYPE"][i], result["LENGTH"][i],
|
|
249
|
+
result["NUMERIC_SCALE"][i], result["IS_NULLABLE"][i], result["IS_UPDATABLE"][i], result["NUMERIC_PRECISION"][i])
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
return PeaAlterResponse.new(sql_message.eql?("00000"), sql_message, sql_state, tb_schema)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
##
|
|
257
|
+
# Sends the DROP SQL query to the server that execute it and retrieve the desired data.
|
|
258
|
+
#
|
|
259
|
+
# Params:
|
|
260
|
+
# +query+:: SQL query that should start with the DROP keyword.
|
|
261
|
+
#
|
|
262
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty or doesn't start with DROP.
|
|
263
|
+
|
|
264
|
+
def execute_drop(query)
|
|
265
|
+
if query.empty?
|
|
266
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
267
|
+
end
|
|
268
|
+
if !query.upcase.start_with?("DROP")
|
|
269
|
+
raise PeaInvalidSyntaxQueryError.new("Query should start with the DROP SQL keyword")
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
nb_row, sql_state, sql_message = modify_table(query)
|
|
273
|
+
|
|
274
|
+
return PeaDropResponse.new(sql_message.eql?("00000"), sql_message, sql_state)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
##
|
|
278
|
+
# Sends the INSERT SQL query to the server that execute it and retrieve the desired data.
|
|
279
|
+
#
|
|
280
|
+
# Params:
|
|
281
|
+
# +query+:: SQL query that should start with the INSERT keyword.
|
|
282
|
+
#
|
|
283
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty or doesn't start with INSERT.
|
|
284
|
+
|
|
285
|
+
def execute_insert(query)
|
|
286
|
+
if query.empty?
|
|
287
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
288
|
+
end
|
|
289
|
+
if !query.upcase.start_with?("INSERT")
|
|
290
|
+
raise PeaInvalidSyntaxQueryError.new("Query should start with the INSERT SQL keyword")
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
nb_row, sql_state, sql_message = modify_table(query)
|
|
294
|
+
|
|
295
|
+
return PeaInsertResponse.new(sql_message.eql?("00000"), sql_message, sql_state, nb_row)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
##
|
|
299
|
+
# Sends the SQL query to the server that execute it and retrieve the desired data.
|
|
300
|
+
#
|
|
301
|
+
# Params:
|
|
302
|
+
# +query+:: SQL query.
|
|
303
|
+
#
|
|
304
|
+
# raises a PeaInvalidSyntaxQueryError if query is empty.
|
|
305
|
+
|
|
306
|
+
def execute_sql(query)
|
|
307
|
+
if query.empty?
|
|
308
|
+
raise PeaInvalidSyntaxQueryError.new("Query should not be either null or empty")
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
nb_row, sql_state, sql_message = modify_table(query)
|
|
312
|
+
|
|
313
|
+
return PeaResponse.new(sql_message.eql?("00000"), sql_message, sql_state)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
##
|
|
317
|
+
# Sends a OS/400 command to the server and retreives the potential warning messages.
|
|
318
|
+
#
|
|
319
|
+
# Params:
|
|
320
|
+
# +command+:: The command that will be sent to the server.
|
|
321
|
+
|
|
322
|
+
def execute_command(command)
|
|
323
|
+
data = retrieve_data("exas" + command + @@end_pack)
|
|
324
|
+
description_offset = 112
|
|
325
|
+
result = Array.new()
|
|
326
|
+
|
|
327
|
+
/C[A-Z]{2}[0-9]{4}/.match(data) do |m|
|
|
328
|
+
|
|
329
|
+
if !m[0].start_with?("CPI") && m.offset(0)[0] + description_offset < data.length
|
|
330
|
+
description = data[m.offset(0)[0] + description_offset..(data.length - (m.offset(0)[0] + description_offset))]
|
|
331
|
+
description = description[0..description.index(".")]
|
|
332
|
+
description = description.gsub(/[^a-zA-Z0-9 ._'*:-]/, '')
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
result.append(m[0] + " " + description)
|
|
336
|
+
|
|
337
|
+
end
|
|
338
|
+
return PeaCommandResponse.new(result)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
##
|
|
342
|
+
# Closes the TCP connexion with the server.
|
|
343
|
+
|
|
344
|
+
def disconnect()
|
|
345
|
+
return if @tcp_client.nil? || @tcp_client.closed?
|
|
346
|
+
@tcp_client.send("stopdipsjbiemg", 0)
|
|
347
|
+
@tcp_client.close()
|
|
348
|
+
rescue IOError
|
|
349
|
+
# Socket already closed, ignore
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def id_client
|
|
353
|
+
@id_client
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def partition_name
|
|
357
|
+
@partition_name
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def username
|
|
361
|
+
@username
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def port
|
|
365
|
+
@port
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def online_version
|
|
369
|
+
@online_version
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def retrieve_statistics
|
|
373
|
+
@retrieve_statistics
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def connexion_status
|
|
377
|
+
@connexion_status
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def connexion_message
|
|
381
|
+
@connexion_message
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
private
|
|
385
|
+
|
|
386
|
+
def build_data(query)
|
|
387
|
+
header = retrieve_data("geth" + query + @@end_pack)
|
|
388
|
+
sql_state = "00000"
|
|
389
|
+
sql_message = "SELECT correctly executed"
|
|
390
|
+
|
|
391
|
+
begin
|
|
392
|
+
header_data = JSON.parse(header)
|
|
393
|
+
rescue
|
|
394
|
+
sql_state = header[1..5];
|
|
395
|
+
sql_message = header[6..];
|
|
396
|
+
else
|
|
397
|
+
list_name = Array.new()
|
|
398
|
+
list_type = Array.new()
|
|
399
|
+
list_prec = Array.new()
|
|
400
|
+
list_scal = Array.new();
|
|
401
|
+
|
|
402
|
+
header_data.each do |item|
|
|
403
|
+
list_name = list_name.append(item["name"].strip)
|
|
404
|
+
list_type = list_type.append(item["type"].to_i)
|
|
405
|
+
list_prec = list_prec.append(item["prec"].to_i)
|
|
406
|
+
list_scal = list_scal.append(item["scal"].to_i)
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
nb_col = list_name.length
|
|
411
|
+
sum_precision = 0
|
|
412
|
+
result = Hash.new()
|
|
413
|
+
list_prec.each_index do |i|
|
|
414
|
+
sum_precision += list_prec[i]
|
|
415
|
+
result[list_name[i]] = Array.new()
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
data = retrieve_data("getd" + query + @@end_pack)
|
|
419
|
+
|
|
420
|
+
# sends statistics
|
|
421
|
+
|
|
422
|
+
nb_row = data.length / sum_precision;
|
|
423
|
+
pointer = 0;
|
|
424
|
+
while pointer != data.length
|
|
425
|
+
for m in 0..(nb_col - 1) do
|
|
426
|
+
scale = list_scal[m]
|
|
427
|
+
precision = list_prec[m]
|
|
428
|
+
type = list_type[m]
|
|
429
|
+
name = list_name[m]
|
|
430
|
+
|
|
431
|
+
if ((type == 484 && scale != 0) || (type == 485 && scale != 0) || (type == 488 && scale != 0) || (type == 489 && scale != 0)) # numeric packed
|
|
432
|
+
temp_float_data = data[pointer..(pointer + precision - 1)].to_f / 10 ** scale
|
|
433
|
+
pointer += precision;
|
|
434
|
+
result[name].append(temp_float_data.to_s);
|
|
435
|
+
elsif (type == 492 || type == 493) # long
|
|
436
|
+
result[name].append(data[pointer..(pointer + 20 - 1)].strip);
|
|
437
|
+
pointer += 20;
|
|
438
|
+
elsif (type == 496 || type == 497) # int
|
|
439
|
+
result[name].append(data[pointer..(pointer + 10 - 1)].strip);
|
|
440
|
+
pointer += 10;
|
|
441
|
+
elsif (type == 500 || type == 501) # short
|
|
442
|
+
result[name].append(data[pointer..(pointer + 5 - 1)].strip);
|
|
443
|
+
pointer += 5;
|
|
444
|
+
else # string, date, time, timestamp
|
|
445
|
+
result[name].append(data[pointer..(pointer + precision - 1)].strip);
|
|
446
|
+
pointer += precision;
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
return result, list_name, nb_row, sql_state, sql_message
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def modify_table(query)
|
|
454
|
+
header = retrieve_data("updt" + query + @@end_pack)
|
|
455
|
+
sql_state = header[1..5]
|
|
456
|
+
sql_message = header[6..]
|
|
457
|
+
|
|
458
|
+
# retrieve stats
|
|
459
|
+
|
|
460
|
+
row_count = 0
|
|
461
|
+
if query.upcase.start_with?("INSERT") || query.upcase.start_with?("UPDATE") || query.upcase.start_with?("DELETE")
|
|
462
|
+
row_count = sql_state.eql?("00000") ? sql_message[..1].to_i : 0
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
return row_count, sql_state, sql_message
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def retrieve_data(command)
|
|
469
|
+
|
|
470
|
+
@tcp_client.send(command, 0)
|
|
471
|
+
|
|
472
|
+
data = ""
|
|
473
|
+
if command.start_with?("geth") || command.start_with?("updt")
|
|
474
|
+
data += "["
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
while data.length < @@end_pack.length || data[(data.length - @@end_pack.length)..(data.length-1)] != @@end_pack
|
|
478
|
+
data += @tcp_client.read(1)
|
|
479
|
+
end
|
|
480
|
+
return data[0..(data.length - (@@end_pack.length + 1))]
|
|
481
|
+
end
|
|
478
482
|
end
|