activerecord-advantage-adapter 0.1.1 → 0.1.2
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 +7 -0
- data/activerecord-advantage-adapter.gemspec +49 -0
- data/lib/active_record/connection_adapters/advantage_adapter.rb +558 -532
- data/lib/arel/visitors/advantage.rb +88 -39
- data/test/connection.rb +23 -23
- metadata +69 -42
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4f45753b6d821481bef646fe79203244d7b69c726b9fe82229e8b312b946c65b
|
|
4
|
+
data.tar.gz: 737097d811c4f196d6a8a4136d17d9b218f6a7294ebf550f78cd22d88ad6edfa
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 01f7b781263c979760673854136779bf9a118ab37490ec1b1ed4d77a4e6f98aa0fdc9c78f5cb09cc87eb2a13acbc1f25f2479639f987c73a16ac120a7b03b430
|
|
7
|
+
data.tar.gz: 054a2d014d38699df8f6b5baa585e8c787632e3b53120ffe2ef98fc3816d8bbe368750eff8c4d461c79dee302db7f2647a293797bc2cd2ea60039353a3954e9a
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
|
|
4
|
+
pkg_version = "0.1.2"
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "activerecord-advantage-adapter"
|
|
8
|
+
spec.version = pkg_version
|
|
9
|
+
spec.authors = ["Edgar Sherman", "Jon Adams"]
|
|
10
|
+
spec.email = ["advantage@sybase.com", "t12nslookup@googlemail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{ActiveRecord driver for Advantage}
|
|
13
|
+
spec.description = %q{ActiveRecord driver for the Advantage Database connector}
|
|
14
|
+
spec.homepage = "http://devzone.advantagedatabase.com"
|
|
15
|
+
spec.license = "Apache-2.0"
|
|
16
|
+
|
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
19
|
+
if spec.respond_to?(:metadata)
|
|
20
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
21
|
+
|
|
22
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
23
|
+
# Changed to the github project, as this is the actively maintained source, now.
|
|
24
|
+
spec.metadata["source_code_uri"] = "https://github.com/t12nslookup/activerecord-advantage-adapter/"
|
|
25
|
+
spec.metadata["changelog_uri"] = "https://github.com/t12nslookup/activerecord-advantage-adapter/blob/master/CHANGELOG.md"
|
|
26
|
+
else
|
|
27
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
|
28
|
+
"public gem pushes."
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Specify which files should be added to the gem when it is released.
|
|
32
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
33
|
+
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
|
34
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
35
|
+
end
|
|
36
|
+
spec.files = Dir["{test,lib}/**/*",
|
|
37
|
+
"LICENSE",
|
|
38
|
+
"README",
|
|
39
|
+
"activerecord-advantage-adapter.gemspec"]
|
|
40
|
+
spec.require_paths = ["lib"]
|
|
41
|
+
|
|
42
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
|
43
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
44
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
45
|
+
|
|
46
|
+
spec.add_runtime_dependency "advantage", "~> 0.1", ">= 0.1.2"
|
|
47
|
+
# spec.add_runtime_dependency 'activerecord', '>= 3.2.0'
|
|
48
|
+
|
|
49
|
+
end
|
|
@@ -1,532 +1,558 @@
|
|
|
1
|
-
#====================================================
|
|
2
|
-
#
|
|
3
|
-
# Copyright 2008-2010 iAnywhere Solutions, Inc.
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
#
|
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
-
#
|
|
16
|
-
# See the License for the specific language governing permissions and
|
|
17
|
-
# limitations under the License.
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#====================================================
|
|
22
|
-
|
|
23
|
-
require
|
|
24
|
-
require
|
|
25
|
-
|
|
26
|
-
# Singleton class to hold a valid instance of the AdvantageInterface across all connections
|
|
27
|
-
class ADS
|
|
28
|
-
include Singleton
|
|
29
|
-
attr_accessor :api
|
|
30
|
-
|
|
31
|
-
def initialize
|
|
32
|
-
require
|
|
33
|
-
@api = Advantage::AdvantageInterface.new()
|
|
34
|
-
raise LoadError, "Could not load ACE library" if Advantage::API.ads_initialize_interface(@api) == 0
|
|
35
|
-
raise LoadError, "Could not initialize ACE library" if @api.ads_init() == 0
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
module ActiveRecord
|
|
40
|
-
class Base
|
|
41
|
-
DEFAULT_CONFIG = { :username =>
|
|
42
|
-
# Main connection function to Advantage
|
|
43
|
-
# Connection Adapter takes four parameters:
|
|
44
|
-
# * :database (required, no default). Corresponds to "Data Source=" in connection string
|
|
45
|
-
# * :username (optional, default to 'adssys'). Correspons to "User ID=" in connection string
|
|
46
|
-
# * :password (optional, deafult to '')
|
|
47
|
-
# * :options (optional, defaults to ''). Corresponds to any additional options in connection string
|
|
48
|
-
|
|
49
|
-
def self.advantage_connection(config)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
connection_string
|
|
56
|
-
connection_string += "
|
|
57
|
-
connection_string += "
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
attr_reader :
|
|
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
|
-
def
|
|
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
|
-
def
|
|
230
|
-
execute(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
#
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
end
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
# Return a list of indexes
|
|
343
|
-
# EJS - Is there a way to get info without DD?
|
|
344
|
-
def indexes(table_name, name = nil) #:nodoc:
|
|
345
|
-
sql = "SELECT name, INDEX_OPTIONS & 1 AS [unique], index_expression FROM SYSTEM.INDEXES WHERE parent = '#{table_name}'"
|
|
346
|
-
select(sql, name).map do |row|
|
|
347
|
-
index = IndexDefinition.new(table_name, row[
|
|
348
|
-
index.unique = row[
|
|
349
|
-
index.columns = row[
|
|
350
|
-
index
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
# Return the primary key
|
|
355
|
-
def primary_key(table_name) #:nodoc:
|
|
356
|
-
sql = "SELECT COLUMN_NAME FROM (EXECUTE PROCEDURE sp_GetBestRowIdentifier( NULL, NULL, '#{table_name}', NULL, FALSE)) as gbri"
|
|
357
|
-
rs = select(sql)
|
|
358
|
-
if !rs.nil? and !rs[0].nil?
|
|
359
|
-
strip_or_self(rs[0][
|
|
360
|
-
else
|
|
361
|
-
nil
|
|
362
|
-
end
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
# Drop an index
|
|
366
|
-
def remove_index(table_name, options={}) #:nodoc:
|
|
367
|
-
execute "DROP INDEX #{quote_table_name(table_name)}.#{quote_column_name(index_name(table_name, options))}"
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
# Rename a table
|
|
371
|
-
#EJS - can be done without dd?
|
|
372
|
-
def rename_table(name, new_name) #:nodoc:
|
|
373
|
-
execute "EXECUTE PROCEDURE sp_RenameDDObject(#{quote_table_name(name)} , #{quote_table_name(new_name)}, 1 /* ADS_DD_TABLE_OBJECT */, 0 /* Rename File */)"
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
# Helper function to retrieve the columns current type
|
|
377
|
-
def get_column_type(table_name, column_name) #:nodoc:
|
|
378
|
-
|
|
379
|
-
SELECT
|
|
380
|
-
CASE
|
|
381
|
-
WHEN type_name = 'VARCHAR' or type_name = 'CHAR' or type_name = 'CICHAR' or
|
|
382
|
-
type_name = 'NVARCHAR' or type_name = 'NCHAR' or type_name = 'VARBINARY'
|
|
383
|
-
THEN CAST(TRIM(type_name) + '(' + TRIM(CAST(column_size AS SQL_CHAR)) + ')' AS SQL_CHAR)
|
|
384
|
-
WHEN type_name = 'NUMERIC'
|
|
385
|
-
THEN CAST(
|
|
386
|
-
|
|
387
|
-
TRIM(type_name
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
1
|
+
#====================================================
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2008-2010 iAnywhere Solutions, Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
#
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
#
|
|
19
|
+
#
|
|
20
|
+
#
|
|
21
|
+
#====================================================
|
|
22
|
+
|
|
23
|
+
require "active_record/connection_adapters/abstract_adapter"
|
|
24
|
+
require "arel/visitors/advantage.rb"
|
|
25
|
+
|
|
26
|
+
# Singleton class to hold a valid instance of the AdvantageInterface across all connections
|
|
27
|
+
class ADS
|
|
28
|
+
include Singleton
|
|
29
|
+
attr_accessor :api
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
require "advantage" unless defined? Advantage
|
|
33
|
+
@api = Advantage::AdvantageInterface.new()
|
|
34
|
+
raise LoadError, "Could not load ACE library" if Advantage::API.ads_initialize_interface(@api) == 0
|
|
35
|
+
raise LoadError, "Could not initialize ACE library" if @api.ads_init() == 0
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
module ActiveRecord
|
|
40
|
+
class Base
|
|
41
|
+
DEFAULT_CONFIG = { :username => "adssys", :password => nil }
|
|
42
|
+
# Main connection function to Advantage
|
|
43
|
+
# Connection Adapter takes four parameters:
|
|
44
|
+
# * :database (required, no default). Corresponds to "Data Source=" in connection string
|
|
45
|
+
# * :username (optional, default to 'adssys'). Correspons to "User ID=" in connection string
|
|
46
|
+
# * :password (optional, deafult to '')
|
|
47
|
+
# * :options (optional, defaults to ''). Corresponds to any additional options in connection string
|
|
48
|
+
|
|
49
|
+
def self.advantage_connection(config)
|
|
50
|
+
config = DEFAULT_CONFIG.merge(config)
|
|
51
|
+
|
|
52
|
+
raise ArgumentError, "No data source was given. Please add a :database option." unless config.has_key?(:database)
|
|
53
|
+
|
|
54
|
+
connection_string = "data source=#{config[:database]};User ID=#{config[:username]};"
|
|
55
|
+
connection_string += "Password=#{config[:password]};" unless config[:password].nil?
|
|
56
|
+
connection_string += "#{config[:options]};" unless config[:options].nil?
|
|
57
|
+
connection_string += "DateFormat=YYYY-MM-DD;"
|
|
58
|
+
|
|
59
|
+
db = ADS.instance.api.ads_new_connection()
|
|
60
|
+
|
|
61
|
+
ConnectionAdapters::AdvantageAdapter.new(db, logger, connection_string)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
module ConnectionAdapters
|
|
66
|
+
class AdvantageException < StandardError
|
|
67
|
+
attr_reader :errno
|
|
68
|
+
attr_reader :sql
|
|
69
|
+
|
|
70
|
+
def initialize(message, errno, sql)
|
|
71
|
+
super(message)
|
|
72
|
+
@errno = errno
|
|
73
|
+
@sql = sql
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class AdvantageColumn < Column
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
# Overridden to handle Advantage integer, varchar, binary, and timestamp types
|
|
81
|
+
def simplified_type(field_type)
|
|
82
|
+
case field_type
|
|
83
|
+
when /logical/i
|
|
84
|
+
:boolean
|
|
85
|
+
when /varchar/i, /char/i, /memo/i
|
|
86
|
+
:string
|
|
87
|
+
when /long binary/i
|
|
88
|
+
:binary
|
|
89
|
+
when /timestamp/i
|
|
90
|
+
:datetime
|
|
91
|
+
when /short|integer/i, /autoinc/i
|
|
92
|
+
:integer
|
|
93
|
+
else
|
|
94
|
+
super
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# JAD Is this helpful?
|
|
99
|
+
def initialize_type_map(m = type_map)
|
|
100
|
+
m.alias_type %r(memo)i, "char"
|
|
101
|
+
m.alias_type %r(long binary)i, "binary"
|
|
102
|
+
m.alias_type %r(integer)i, "int"
|
|
103
|
+
m.alias_type %r(short)i, "int"
|
|
104
|
+
m.alias_type %r(autoinc)i, "int"
|
|
105
|
+
super
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class AdvantageAdapter < AbstractAdapter
|
|
110
|
+
def initialize(connection, logger, connection_string = "") #:nodoc:
|
|
111
|
+
super(connection, logger)
|
|
112
|
+
@prepared_statements = false
|
|
113
|
+
@auto_commit = true
|
|
114
|
+
@affected_rows = 0
|
|
115
|
+
@connection_string = connection_string
|
|
116
|
+
@visitor = Arel::Visitors::Advantage.new self
|
|
117
|
+
connect!
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def adapter_name #:nodoc:
|
|
121
|
+
"Advantage"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def supports_migrations? #:nodoc:
|
|
125
|
+
true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def requires_reloading? #:nodoc:
|
|
129
|
+
true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def active? #:nodoc:
|
|
133
|
+
ADS.instance.api.ads_execute_immediate(@connection, "SELECT 1 FROM SYSTEM.IOTA") == 1
|
|
134
|
+
rescue
|
|
135
|
+
false
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def disconnect! #:nodoc:
|
|
139
|
+
result = ADS.instance.api.ads_disconnect(@connection)
|
|
140
|
+
super
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def reconnect! #:nodoc:
|
|
144
|
+
disconnect!
|
|
145
|
+
connect!
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def supports_count_distinct? #:nodoc:
|
|
149
|
+
true
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def supports_autoincrement? #:nodoc:
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Used from StackOverflow question 1000688
|
|
157
|
+
# Strip alone will return NIL if the string is not altered. In that case,
|
|
158
|
+
# still return the string.
|
|
159
|
+
def strip_or_self(str) #:nodoc:
|
|
160
|
+
str.strip! || str if str
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Maps native ActiveRecord/Ruby types into ADS types
|
|
164
|
+
def native_database_types #:nodoc:
|
|
165
|
+
{
|
|
166
|
+
:primary_key => "AUTOINC PRIMARY KEY CONSTRAINT NOT NULL",
|
|
167
|
+
:string => { :name => "varchar", :limit => 255 },
|
|
168
|
+
:text => { :name => "memo" },
|
|
169
|
+
:integer => { :name => "integer" },
|
|
170
|
+
:float => { :name => "float" },
|
|
171
|
+
:decimal => { :name => "numeric" },
|
|
172
|
+
:datetime => { :name => "timestamp" },
|
|
173
|
+
:timestamp => { :name => "timestamp" },
|
|
174
|
+
:time => { :name => "time" },
|
|
175
|
+
:date => { :name => "date" },
|
|
176
|
+
:binary => { :name => "blob" },
|
|
177
|
+
:boolean => { :name => "logical" },
|
|
178
|
+
}
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Applies quotations around column names in generated queries
|
|
182
|
+
def quote_column_name(name) #:nodoc:
|
|
183
|
+
%Q("#{name}")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def quoted_true #:nodoc:
|
|
187
|
+
"1"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def quoted_false #:nodoc:
|
|
191
|
+
"0"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Translate the exception if possible
|
|
195
|
+
def translate_exception(exception, message) #:nodoc:
|
|
196
|
+
return super unless exception.respond_to?(:errno)
|
|
197
|
+
case exception.errno
|
|
198
|
+
when 2121
|
|
199
|
+
if exception.sql !~ /^SELECT/i
|
|
200
|
+
raise ActiveRecord::ActiveRecordError.new(message)
|
|
201
|
+
else
|
|
202
|
+
super
|
|
203
|
+
end
|
|
204
|
+
when 7076
|
|
205
|
+
raise InvalidForeignKey.new(message, exception)
|
|
206
|
+
when 7057
|
|
207
|
+
raise RecordNotUnique.new(message, exception)
|
|
208
|
+
else
|
|
209
|
+
super
|
|
210
|
+
end
|
|
211
|
+
super
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# The database update function.
|
|
215
|
+
def update_sql(sql, name = nil) #:nodoc:
|
|
216
|
+
execute(sql, name)
|
|
217
|
+
return @affected_rows
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# The database delete function.
|
|
221
|
+
def delete_sql(sql, name = nil) #:nodoc:
|
|
222
|
+
execute(sql, name)
|
|
223
|
+
return @affected_rows
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# The database insert function.
|
|
227
|
+
# ActiveRecord requires that insert_sql returns the primary key of the row just inserted. In most cases, this can be accomplished
|
|
228
|
+
# by immediatly querying the @@identity property. If the @@identity property is 0, then passed id_value is used
|
|
229
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
|
230
|
+
execute(sql, name)
|
|
231
|
+
identity = last_inserted_id(nil)
|
|
232
|
+
retval = id_value if retval == 0
|
|
233
|
+
return retval
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# The Database insert function as part of the rails changes
|
|
237
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) #:nodoc:
|
|
238
|
+
log(sql, "insert", binds) { exec_query(sql, binds) }
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# The Database update function as part of the rails changes
|
|
242
|
+
def exec_update(sql, name = nil, binds = []) #:nodoc:
|
|
243
|
+
log(sql, "update", binds) { exec_query(sql, binds) }
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# The Database delete function as part of the rails changes
|
|
247
|
+
def exec_delete(sql, name = nil, binds = []) #:nodoc:
|
|
248
|
+
log(sql, "delete", binds) { exec_query(sql, binds) }
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def exec_query(sql, name = "SQL", binds = [])
|
|
252
|
+
cols, record = execute(sql, name)
|
|
253
|
+
ActiveRecord::Result.new(cols, record)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Retrieve the last AutoInc ID
|
|
257
|
+
def last_inserted_id(result) #:nodoc:
|
|
258
|
+
rs = ADS.instance.api.ads_execute_direct(@connection, "SELECT LASTAUTOINC( CONNECTION ) FROM SYSTEM.IOTA")
|
|
259
|
+
raise ActiveRecord::StatementInvalid.new("#{ADS.instance.api.ads_error(@connection)}:#{sql}") if rs.nil?
|
|
260
|
+
ADS.instance.api.ads_fetch_next(rs)
|
|
261
|
+
retval, identity = ADS.instance.api.ads_get_column(rs, 0)
|
|
262
|
+
ADS.instance.api.ads_free_stmt(rs)
|
|
263
|
+
identity
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Returns a query as an array of arrays
|
|
267
|
+
def select_rows(sql, name = nil)
|
|
268
|
+
exec_query(sql, name).rows
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Begin a transaction
|
|
272
|
+
def begin_db_transaction #:nodoc:
|
|
273
|
+
ADS.instance.api.AdsBeginTransaction(@connection)
|
|
274
|
+
@auto_commit = false
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Commit the transaction
|
|
278
|
+
def commit_db_transaction #:nodoc:
|
|
279
|
+
ADS.instance.api.ads_commit(@connection)
|
|
280
|
+
@auto_commit = true
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Rollback the transaction
|
|
284
|
+
def rollback_db_transaction #:nodoc:
|
|
285
|
+
ADS.instance.api.ads_rollback(@connection)
|
|
286
|
+
@auto_commit = true
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def add_lock!(sql, options) #:nodoc:
|
|
290
|
+
sql
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Advantage does not support sizing of integers based on the sytax INTEGER(size).
|
|
294
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
|
295
|
+
if native = native_database_types[type]
|
|
296
|
+
if type == :integer
|
|
297
|
+
column_type_sql = "integer"
|
|
298
|
+
elsif type == :string and !limit.nil?
|
|
299
|
+
"varchar (#{limit})"
|
|
300
|
+
else
|
|
301
|
+
super(type, limit, precision, scale)
|
|
302
|
+
end
|
|
303
|
+
else
|
|
304
|
+
super(type, limit, precision, scale)
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Retrieve a list of Tables
|
|
309
|
+
def data_source_sql(name = nil, type = nil) #:nodoc:
|
|
310
|
+
"SELECT table_name from (EXECUTE PROCEDURE sp_GetTables( NULL, NULL, '#{name}', 'TABLE' )) spgc where table_cat <> 'system';"
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Retrieve a list of Tables
|
|
314
|
+
def tables(name = nil) #:nodoc:
|
|
315
|
+
sql = "EXECUTE PROCEDURE sp_GetTables( NULL, NULL, NULL, 'TABLE' );"
|
|
316
|
+
select(sql, name).map { |row| strip_or_self(row["TABLE_NAME"]) }
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Return a list of columns
|
|
320
|
+
def columns(table_name, name = nil) #:nodoc:
|
|
321
|
+
table_structure(table_name).map do |field|
|
|
322
|
+
if Rails::VERSION::MAJOR > 4
|
|
323
|
+
AdvantageColumn.new(strip_or_self(field["COLUMN_NAME"]),
|
|
324
|
+
field["COLUMN_DEF"],
|
|
325
|
+
SqlTypeMetadata.new(sql_type: strip_or_self(field["TYPE_NAME"])),
|
|
326
|
+
field["NULLABLE"])
|
|
327
|
+
elsif Rails::VERSION::MAJOR == 4
|
|
328
|
+
AdvantageColumn.new(strip_or_self(field["COLUMN_NAME"]),
|
|
329
|
+
field["COLUMN_DEF"],
|
|
330
|
+
lookup_cast_type(strip_or_self(field["TYPE_NAME"])),
|
|
331
|
+
strip_or_self(field["TYPE_NAME"]),
|
|
332
|
+
field["NULLABLE"])
|
|
333
|
+
else
|
|
334
|
+
AdvantageColumn.new(strip_or_self(field["COLUMN_NAME"]),
|
|
335
|
+
field["COLUMN_DEF"],
|
|
336
|
+
strip_or_self(field["TYPE_NAME"]),
|
|
337
|
+
field["NULLABLE"])
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Return a list of indexes
|
|
343
|
+
# EJS - Is there a way to get info without DD?
|
|
344
|
+
def indexes(table_name, name = nil) #:nodoc:
|
|
345
|
+
sql = "SELECT name, INDEX_OPTIONS & 1 AS [unique], index_expression FROM SYSTEM.INDEXES WHERE parent = '#{table_name}'"
|
|
346
|
+
select(sql, name).map do |row|
|
|
347
|
+
index = IndexDefinition.new(table_name, row["name"])
|
|
348
|
+
index.unique = row["unique"] == 1
|
|
349
|
+
index.columns = row["index_expression"]
|
|
350
|
+
index
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Return the primary key
|
|
355
|
+
def primary_key(table_name) #:nodoc:
|
|
356
|
+
sql = "SELECT COLUMN_NAME FROM (EXECUTE PROCEDURE sp_GetBestRowIdentifier( NULL, NULL, '#{table_name}', NULL, FALSE)) as gbri"
|
|
357
|
+
rs = select(sql)
|
|
358
|
+
if !rs.nil? and !rs[0].nil?
|
|
359
|
+
strip_or_self(rs[0]["COLUMN_NAME"])
|
|
360
|
+
else
|
|
361
|
+
nil
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Drop an index
|
|
366
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
|
367
|
+
execute "DROP INDEX #{quote_table_name(table_name)}.#{quote_column_name(index_name(table_name, options))}"
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Rename a table
|
|
371
|
+
#EJS - can be done without dd?
|
|
372
|
+
def rename_table(name, new_name) #:nodoc:
|
|
373
|
+
execute "EXECUTE PROCEDURE sp_RenameDDObject(#{quote_table_name(name)} , #{quote_table_name(new_name)}, 1 /* ADS_DD_TABLE_OBJECT */, 0 /* Rename File */)"
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Helper function to retrieve the columns current type
|
|
377
|
+
def get_column_type(table_name, column_name) #:nodoc:
|
|
378
|
+
sql = <<-SQL
|
|
379
|
+
SELECT
|
|
380
|
+
CASE
|
|
381
|
+
WHEN type_name = 'VARCHAR' or type_name = 'CHAR' or type_name = 'CICHAR' or
|
|
382
|
+
type_name = 'NVARCHAR' or type_name = 'NCHAR' or type_name = 'VARBINARY'
|
|
383
|
+
THEN CAST(TRIM(type_name) + '(' + TRIM(CAST(column_size AS SQL_CHAR)) + ')' AS SQL_CHAR)
|
|
384
|
+
WHEN type_name = 'NUMERIC' and decimal_digits = 0
|
|
385
|
+
THEN CAST('INTEGER(' + TRIM(CAST(column_size AS SQL_CHAR)) + ')' AS SQL_CHAR)
|
|
386
|
+
WHEN type_name = 'NUMERIC' or type_name = 'DOUBLE' or type_name = 'CURDOUBLE'
|
|
387
|
+
THEN CAST(TRIM(type_name) + '(' + TRIM(CAST(column_size AS SQL_CHAR)) + ',' + TRIM(CAST(decimal_digits AS SQL_CHAR)) + ')' AS SQL_CHAR)
|
|
388
|
+
ELSE
|
|
389
|
+
TRIM(type_name COLLATE ads_default_cs)
|
|
390
|
+
END AS "domain"
|
|
391
|
+
from (EXECUTE PROCEDURE sp_GetColumns( NULL, NULL, '#{table_name}', NULL)) as spgc
|
|
392
|
+
WHERE COLUMN_NAME = '#{column_name}'
|
|
393
|
+
SQL
|
|
394
|
+
rs = select(sql)
|
|
395
|
+
if !rs.nil? and !rs[0].nil?
|
|
396
|
+
rs[0]["domain"]
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Change a columns defaults.
|
|
401
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
|
402
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{get_column_type(table_name, column_name)} DEFAULT #{quote(default)}"
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Change a columns nullability
|
|
406
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
|
407
|
+
unless null || default.nil?
|
|
408
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
|
409
|
+
end
|
|
410
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{get_column_type(table_name, column_name)} CONSTRAINT #{null ? "" : "NOT"} NULL")
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Alter a column
|
|
414
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
|
415
|
+
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, type_options[:limit], type_options[:precision], type_options[:scale])}"
|
|
416
|
+
add_column_options!(add_column_sql, options)
|
|
417
|
+
execute(add_column_sql)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Add column options
|
|
421
|
+
def add_column_options!(sql, options) #:nodoc:
|
|
422
|
+
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
|
|
423
|
+
# must explicitly check for :null to allow change_column to work on migrations
|
|
424
|
+
if options[:null] == false
|
|
425
|
+
sql << " CONSTRAINT NOT NULL"
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Rename a column
|
|
430
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
|
431
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{type_to_sql(type, type_options[:limit], type_options[:precision], type_options[:scale])}"
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# Drop a column from a table
|
|
435
|
+
def remove_column(table_name, column_name) #:nodoc:
|
|
436
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
protected
|
|
440
|
+
|
|
441
|
+
# Execute a query
|
|
442
|
+
def select(sql, name = nil, binds = []) #:nodoc:
|
|
443
|
+
if Rails::VERSION::MAJOR >= 4
|
|
444
|
+
exec_query(sql, name, binds)
|
|
445
|
+
else
|
|
446
|
+
exec_query(sql, name, binds).to_hash
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# Queries the structure of a table including the columns names, defaults, type, and nullability
|
|
451
|
+
# ActiveRecord uses the type to parse scale and precision information out of the types. As a result,
|
|
452
|
+
# chars, varchars, binary, nchars, nvarchars must all be returned in the form <i>type</i>(<i>width</i>)
|
|
453
|
+
# numeric and decimal must be returned in the form <i>type</i>(<i>width</i>, <i>scale</i>)
|
|
454
|
+
# Nullability is returned as 0 (no nulls allowed) or 1 (nulls allowed)
|
|
455
|
+
# Alos, ActiveRecord expects an autoincrement column to have default value of NULL
|
|
456
|
+
def table_structure(table_name)
|
|
457
|
+
# sql = "SELECT COLUMN_NAME, IIF(COLUMN_DEF = 'NULL', null, COLUMN_DEF) as COLUMN_DEF, IIF(TYPE_NAME = 'NUMERIC' and DECIMAL_DIGITS = 0, 'INTEGER', TYPE_NAME) as TYPE_NAME, NULLABLE from (EXECUTE PROCEDURE sp_GetColumns( NULL, NULL, '#{table_name}', NULL )) spgc where table_cat <> 'system';"
|
|
458
|
+
sql = "SELECT COLUMN_NAME, IIF(COLUMN_DEF = 'NULL', null, COLUMN_DEF) as COLUMN_DEF, TYPE_NAME, NULLABLE from (EXECUTE PROCEDURE sp_GetColumns( NULL, NULL, '#{table_name}', NULL )) spgc where table_cat <> 'system';"
|
|
459
|
+
structure = exec_query(sql, :skip_logging)
|
|
460
|
+
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure == false
|
|
461
|
+
structure
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# Required to prevent DEFAULT NULL being added to primary keys
|
|
465
|
+
def options_include_default?(options)
|
|
466
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
private
|
|
470
|
+
|
|
471
|
+
# Connect
|
|
472
|
+
def connect! #:nodoc:
|
|
473
|
+
result = ADS.instance.api.ads_connect(@connection, @connection_string)
|
|
474
|
+
if result != 1
|
|
475
|
+
error = ADS.instance.api.ads_error(@connection)
|
|
476
|
+
raise ActiveRecord::ActiveRecordError.new("#{error}: Cannot Establish Connection")
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# The database execution function
|
|
481
|
+
def query(sql, name = nil, binds = []) #:nodoc:
|
|
482
|
+
if name == :skip_logging
|
|
483
|
+
execute(sql, binds)
|
|
484
|
+
else
|
|
485
|
+
log(sql, name, binds) { execute(sql, binds) }
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Execute a query
|
|
490
|
+
def execute(sql, name = nil, binds = []) #:nodoc:
|
|
491
|
+
return if sql.nil?
|
|
492
|
+
|
|
493
|
+
if binds.empty?
|
|
494
|
+
rs = ADS.instance.api.ads_execute_direct(@connection, sql)
|
|
495
|
+
else
|
|
496
|
+
stmt = ADS.instance.api.ads_prepare(@connection, sql)
|
|
497
|
+
# bind each of the parameters
|
|
498
|
+
# col: Parameter array. Col[0] -> Parameter info, Col[1] -> Parameter value
|
|
499
|
+
binds.each_with_index { |col, index|
|
|
500
|
+
result, param = ADS.instance.api.ads_describe_bind_param(stmt, index)
|
|
501
|
+
if result == 1
|
|
502
|
+
# For date/time/timestamp fix up the format to remove the timzone
|
|
503
|
+
if (col[0].type === :datetime or col[0].type === :timestamp or col[0] === :time) and !col[1].nil?
|
|
504
|
+
param.set_value(col[1].to_s(:db))
|
|
505
|
+
else
|
|
506
|
+
param.set_value(col[1])
|
|
507
|
+
end
|
|
508
|
+
ADS.instance.api.ads_bind_param(stmt, index, param)
|
|
509
|
+
else
|
|
510
|
+
result, errstr = ADS.instance.api.ads_error(@connection)
|
|
511
|
+
raise AdvantageException.new(errstr, result, sql)
|
|
512
|
+
end
|
|
513
|
+
} #binds.each_with_index
|
|
514
|
+
result = ADS.instance.api.ads_execute(stmt)
|
|
515
|
+
if result == 1
|
|
516
|
+
rs = stmt
|
|
517
|
+
else
|
|
518
|
+
result, errstr = ADS.instance.api.ads_error(@connection)
|
|
519
|
+
raise AdvantageException.new(errstr, result, sql)
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
if rs.nil?
|
|
523
|
+
result, errstr = ADS.instance.api.ads_error(@connection)
|
|
524
|
+
raise AdvantageException.new(errstr, result, sql)
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# the record of all the rows
|
|
528
|
+
row_record = []
|
|
529
|
+
# the column headers
|
|
530
|
+
col_headers = []
|
|
531
|
+
if (ADS.instance.api.ads_num_cols(rs) > 0)
|
|
532
|
+
while ADS.instance.api.ads_fetch_next(rs) == 1
|
|
533
|
+
max_cols = ADS.instance.api.ads_num_cols(rs)
|
|
534
|
+
row = []
|
|
535
|
+
max_cols.times do |cols|
|
|
536
|
+
# record the columns the first time through the results
|
|
537
|
+
if row_record.count == 0
|
|
538
|
+
cinfo = ADS.instance.api.ads_get_column_info(rs, cols)
|
|
539
|
+
col_headers << cinfo[2]
|
|
540
|
+
end
|
|
541
|
+
cvalue = ADS.instance.api.ads_get_column(rs, cols)
|
|
542
|
+
row << cvalue[1]
|
|
543
|
+
end
|
|
544
|
+
row_record << row
|
|
545
|
+
end
|
|
546
|
+
@affected_rows = 0
|
|
547
|
+
else
|
|
548
|
+
@affected_rows = ADS.instance.api.ads_affected_rows(rs)
|
|
549
|
+
end
|
|
550
|
+
ADS.instance.api.ads_free_stmt(rs)
|
|
551
|
+
|
|
552
|
+
# force the columns to be unique (I don't believe this does anything now)
|
|
553
|
+
col_headers.uniq!
|
|
554
|
+
return col_headers, row_record
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
end
|