db2 2.6.2 → 2.7.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.
Files changed (41) hide show
  1. data/CHANGES +17 -0
  2. data/README +79 -141
  3. data/ext/Makefile.nt32 +3 -3
  4. data/ext/Makefile.nt32.191 +212 -0
  5. data/ext/extconf.rb +75 -14
  6. data/ext/ibm_db.c +504 -47
  7. data/ext/ruby_ibm_db.h +4 -1
  8. data/ext/ruby_ibm_db_cli.c +108 -1
  9. data/ext/ruby_ibm_db_cli.h +54 -1
  10. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +423 -124
  11. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1 -1
  12. data/test/cases/adapter_test.rb +169 -164
  13. data/test/cases/associations/belongs_to_associations_test.rb +268 -43
  14. data/test/cases/associations/cascaded_eager_loading_test.rb +31 -33
  15. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +90 -156
  16. data/test/cases/associations/join_model_test.rb +100 -150
  17. data/test/cases/attribute_methods_test.rb +259 -58
  18. data/test/cases/base_test.rb +785 -138
  19. data/test/cases/calculations_test.rb +128 -8
  20. data/test/cases/migration_test.rb +680 -286
  21. data/test/cases/persistence_test.rb +642 -0
  22. data/test/cases/query_cache_test.rb +257 -0
  23. data/test/cases/relations_test.rb +1182 -0
  24. data/test/cases/schema_dumper_test.rb +41 -17
  25. data/test/cases/transaction_callbacks_test.rb +300 -0
  26. data/test/cases/validations/uniqueness_validation_test.rb +38 -22
  27. data/test/cases/xml_serialization_test.rb +408 -0
  28. data/test/config.yml +154 -0
  29. data/test/connections/native_ibm_db/connection.rb +2 -0
  30. data/test/models/warehouse_thing.rb +4 -4
  31. data/test/schema/i5/ibm_db_specific_schema.rb +3 -1
  32. data/test/schema/ids/ibm_db_specific_schema.rb +3 -1
  33. data/test/schema/luw/ibm_db_specific_schema.rb +2 -0
  34. data/test/schema/schema.rb +196 -92
  35. data/test/schema/zOS/ibm_db_specific_schema.rb +3 -1
  36. metadata +73 -68
  37. data/.gitignore +0 -1
  38. data/test/cases/associations/eager_test.rb +0 -862
  39. data/test/cases/associations/has_many_through_associations_test.rb +0 -461
  40. data/test/cases/finder_test.rb +0 -1088
  41. data/test/cases/fixtures_test.rb +0 -684
@@ -2,7 +2,7 @@
2
2
  +----------------------------------------------------------------------+
3
3
  | Licensed Materials - Property of IBM |
4
4
  | |
5
- | (C) Copyright IBM Corporation 2006, 2007, 2008, 2009, 2010 |
5
+ | (C) Copyright IBM Corporation 2006, 2007, 2008, 2009, 2010, 2012 |
6
6
  +----------------------------------------------------------------------+
7
7
  | Authors: Sushant Koduru, Lynh Nguyen, Kanchana Padmanabhan, |
8
8
  | Dan Scott, Helmut Tessarek, Kellen Bombardier, Sam Ruby |
@@ -135,6 +135,9 @@ void Init_ibm_db();
135
135
  /* Function Declarations */
136
136
 
137
137
  VALUE ibm_db_connect(int argc, VALUE *argv, VALUE self);
138
+ VALUE ibm_db_createDB(int argc, VALUE *argv, VALUE self);
139
+ VALUE ibm_db_dropDB(int argc, VALUE *argv, VALUE self);
140
+ VALUE ibm_db_createDBNX(int argc, VALUE *argv, VALUE self);
138
141
  VALUE ibm_db_commit(int argc, VALUE *argv, VALUE self);
139
142
  VALUE ibm_db_pconnect(int argc, VALUE *argv, VALUE self);
140
143
  VALUE ibm_db_autocommit(int argc, VALUE *argv, VALUE self);
@@ -2,7 +2,7 @@
2
2
  +----------------------------------------------------------------------+
3
3
  | Licensed Materials - Property of IBM |
4
4
  | |
5
- | (C) Copyright IBM Corporation 2009, 2010 |
5
+ | (C) Copyright IBM Corporation 2009, 2010, 2012 |
6
6
  +----------------------------------------------------------------------+
7
7
  | Authors: Praveen Devarao |
8
8
  +----------------------------------------------------------------------+
@@ -372,6 +372,113 @@ int _ruby_ibm_db_SQLExecDirect_helper(exec_cum_prepare_args *data) {
372
372
  return rc;
373
373
  }
374
374
 
375
+ /*
376
+ This function calls SQLCreateDb cli call
377
+ */
378
+ int _ruby_ibm_db_SQLCreateDB_helper(create_drop_db_args *data) {
379
+ int rc = 0;
380
+ #ifndef UNICODE_SUPPORT_VERSION
381
+ #ifdef _WIN32
382
+ HINSTANCE cliLib = NULL;
383
+ FARPROC sqlcreatedb;
384
+ cliLib = DLOPEN( LIBDB2 );
385
+ sqlcreatedb = DLSYM( cliLib, "SQLCreateDb" );
386
+ #elif _AIX
387
+ void *cliLib = NULL;
388
+ typedef int (*sqlcreatedbType)( SQLHDBC, SQLCHAR *, SQLINTEGER, SQLCHAR *, SQLINTEGER, SQLCHAR *, SQLINTEGER );
389
+ sqlcreatedbType sqlcreatedb;
390
+ /* On AIX CLI library is in archive. Hence we will need to specify flags in DLOPEN to load a member of the archive*/
391
+ cliLib = DLOPEN( LIBDB2, RTLD_MEMBER | RTLD_LAZY );
392
+ sqlcreatedb = (sqlcreatedbType) DLSYM( cliLib, "SQLCreateDb" );
393
+ #else
394
+ void *cliLib = NULL;
395
+ typedef int (*sqlcreatedbType)( SQLHDBC, SQLCHAR *, SQLINTEGER, SQLCHAR *, SQLINTEGER, SQLCHAR *, SQLINTEGER );
396
+ sqlcreatedbType sqlcreatedb;
397
+ cliLib = DLOPEN( LIBDB2, RTLD_LAZY );
398
+ sqlcreatedb = (sqlcreatedbType) DLSYM( cliLib, "SQLCreateDb" );
399
+ #endif
400
+ #else
401
+ #ifdef _WIN32
402
+ HINSTANCE cliLib = NULL;
403
+ FARPROC sqlcreatedb;
404
+ cliLib = DLOPEN( LIBDB2 );
405
+ sqlcreatedb = DLSYM( cliLib, "SQLCreateDbW" );
406
+ #elif _AIX
407
+ void *cliLib = NULL;
408
+ typedef int (*sqlcreatedbType)( SQLHDBC, SQLWCHAR *, SQLINTEGER, SQLWCHAR *, SQLINTEGER, SQLWCHAR *, SQLINTEGER );
409
+ sqlcreatedbType sqlcreatedb;
410
+ /* On AIX CLI library is in archive. Hence we will need to specify flags in DLOPEN to load a member of the archive*/
411
+ cliLib = DLOPEN( LIBDB2, RTLD_MEMBER | RTLD_LAZY );
412
+ sqlcreatedb = (sqlcreatedbType) DLSYM( cliLib, "SQLCreateDbW" );
413
+ #else
414
+ void *cliLib = NULL;
415
+ typedef int (*sqlcreatedbType)( SQLHDBC, SQLWCHAR *, SQLINTEGER, SQLWCHAR *, SQLINTEGER, SQLWCHAR *, SQLINTEGER );
416
+ sqlcreatedbType sqlcreatedb;
417
+ cliLib = DLOPEN( LIBDB2, RTLD_LAZY );
418
+ sqlcreatedb = (sqlcreatedbType) DLSYM( cliLib, "SQLCreateDbW" );
419
+ #endif
420
+ #endif
421
+
422
+ rc = (*sqlcreatedb)( (SQLHSTMT) data->conn_res->hdbc, data->dbName, (SQLINTEGER)data->dbName_string_len,
423
+ data->codeSet, (SQLINTEGER)data->codeSet_string_len,
424
+ data->mode, (SQLINTEGER)data->mode_string_len );
425
+ DLCLOSE( cliLib );
426
+ return rc;
427
+ }
428
+
429
+ /*
430
+ This function calls SQLDropDb cli call
431
+ */
432
+ int _ruby_ibm_db_SQLDropDB_helper(create_drop_db_args *data) {
433
+ int rc = 0;
434
+ #ifndef UNICODE_SUPPORT_VERSION
435
+ #ifdef _WIN32
436
+ HINSTANCE cliLib = NULL;
437
+ FARPROC sqldropdb;
438
+ cliLib = DLOPEN( LIBDB2 );
439
+ sqldropdb = DLSYM( cliLib, "SQLDropDb" );
440
+ #elif _AIX
441
+ void *cliLib = NULL;
442
+ typedef int (*sqldropdbType)( SQLHDBC, SQLCHAR *, SQLINTEGER);
443
+ sqldropdbType sqldropdb;
444
+ /* On AIX CLI library is in archive. Hence we will need to specify flags in DLOPEN to load a member of the archive*/
445
+ cliLib = DLOPEN( LIBDB2, RTLD_MEMBER | RTLD_LAZY );
446
+ sqldropdb = (sqldropdbType) DLSYM( cliLib, "SQLDropDb" );
447
+ #else
448
+ void *cliLib = NULL;
449
+ typedef int (*sqldropdbType)( SQLHDBC, SQLCHAR *, SQLINTEGER);
450
+ sqldropdbType sqldropdb;
451
+ cliLib = DLOPEN( LIBDB2, RTLD_LAZY );
452
+ sqldropdb = (sqldropdbType) DLSYM( cliLib, "SQLDropDb" );
453
+ #endif
454
+ #else
455
+ #ifdef _WIN32
456
+ HINSTANCE cliLib = NULL;
457
+ FARPROC sqldropdb;
458
+ cliLib = DLOPEN( LIBDB2 );
459
+ sqldropdb = DLSYM( cliLib, "SQLDropDbW" );
460
+ #elif _AIX
461
+ void *cliLib = NULL;
462
+ typedef int (*sqldropdbType)( SQLHDBC, SQLWCHAR *, SQLINTEGER);
463
+ sqldropdbType sqldropdb;
464
+ /* On AIX CLI library is in archive. Hence we will need to specify flags in DLOPEN to load a member of the archive*/
465
+ cliLib = DLOPEN( LIBDB2, RTLD_MEMBER | RTLD_LAZY );
466
+ sqldropdb = (sqldropdbType) DLSYM( cliLib, "SQLDropDbW" );
467
+ #else
468
+ void *cliLib = NULL;
469
+ typedef int (*sqldropdbType)( SQLHDBC, SQLWCHAR *, SQLINTEGER);
470
+ sqldropdbType sqldropdb;
471
+ cliLib = DLOPEN( LIBDB2, RTLD_LAZY );
472
+ sqldropdb = (sqldropdbType) DLSYM( cliLib, "SQLDropDbW" );
473
+ #endif
474
+ #endif
475
+
476
+ rc = (*sqldropdb)( (SQLHSTMT) data->conn_res->hdbc, data->dbName, (SQLINTEGER)data->dbName_string_len );
477
+
478
+ DLCLOSE( cliLib );
479
+ return rc;
480
+ }
481
+
375
482
  /*
376
483
  This function calls SQLPrepare cli call to prepare the given statement
377
484
  */
@@ -2,7 +2,7 @@
2
2
  +----------------------------------------------------------------------+
3
3
  | Licensed Materials - Property of IBM |
4
4
  | |
5
- | (C) Copyright IBM Corporation 2009, 2010 |
5
+ | (C) Copyright IBM Corporation 2009, 2010, 2012 |
6
6
  +----------------------------------------------------------------------+
7
7
  | Authors: Praveen Devarao |
8
8
  +----------------------------------------------------------------------+
@@ -11,6 +11,35 @@
11
11
  #ifndef RUBY_IBM_DB_CLI_H
12
12
  #define RUBY_IBM_DB_CLI_H
13
13
 
14
+ #ifdef _WIN32
15
+ #include <windows.h>
16
+ #else
17
+ #include <dlfcn.h>
18
+ #endif
19
+
20
+ #ifdef _WIN32
21
+ #define DLOPEN LoadLibrary
22
+ #define DLSYM GetProcAddress
23
+ #define DLCLOSE FreeLibrary
24
+ #define LIBDB2 "db2cli.dll"
25
+ #elif _AIX
26
+ #define DLOPEN dlopen
27
+ #define DLSYM dlsym
28
+ #define DLCLOSE dlclose
29
+ #ifdef __64BIT__
30
+ /*64-bit library in the archive libdb2.a*/
31
+ #define LIBDB2 "libdb2.a(shr_64.o)"
32
+ #else
33
+ /*32-bit library in the archive libdb2.a*/
34
+ #define LIBDB2 "libdb2.a(shr.o)"
35
+ #endif
36
+ #else
37
+ #define DLOPEN dlopen
38
+ #define DLSYM dlsym
39
+ #define DLCLOSE dlclose
40
+ #define LIBDB2 "libdb2.so.1"
41
+ #endif
42
+
14
43
  #include <ruby.h>
15
44
  #include <stdio.h>
16
45
  #include <string.h>
@@ -54,6 +83,8 @@ typedef struct _conn_handle_struct {
54
83
  SQLPOINTER ruby_error_state;
55
84
  SQLSMALLINT ruby_error_msg_len;
56
85
 
86
+ SQLINTEGER sqlcode;
87
+
57
88
  } conn_handle;
58
89
 
59
90
  typedef union {
@@ -114,6 +145,7 @@ typedef struct _stmt_handle_struct {
114
145
  SQLPOINTER ruby_stmt_err_msg;
115
146
  SQLPOINTER ruby_stmt_err_state;
116
147
  SQLSMALLINT ruby_stmt_err_msg_len;
148
+ SQLINTEGER sqlcode;
117
149
  } stmt_handle;
118
150
 
119
151
  /*
@@ -211,6 +243,25 @@ typedef struct _ibm_db_exec_direct_args_struct {
211
243
  long stmt_string_len;
212
244
  } exec_cum_prepare_args;
213
245
 
246
+ /*
247
+ Structure holding the necessary info to be passed to SQLCreateDB and SQLDropDB CLI call
248
+ */
249
+ typedef struct _ibm_db_create_drop_db_args_struct {
250
+ conn_handle *conn_res;
251
+ #ifdef UNICODE_SUPPORT_VERSION
252
+ SQLWCHAR *dbName;
253
+ SQLWCHAR *codeSet;
254
+ SQLWCHAR *mode;
255
+ #else
256
+ SQLCHAR *dbName;
257
+ SQLCHAR *codeSet;
258
+ SQLCHAR *mode;
259
+ #endif
260
+ long dbName_string_len;
261
+ long codeSet_string_len;
262
+ long mode_string_len;
263
+ } create_drop_db_args;
264
+
214
265
  /*
215
266
  Structure holding the necessary info to be passed to SQLParamData and SQLPutData CLI call
216
267
  */
@@ -427,5 +478,7 @@ int _ruby_ibm_db_SQLGetConnectAttr_helper(get_handle_attr_args *data);
427
478
  int _ruby_ibm_db_SQLBindFileToParam_helper(stmt_handle *stmt_res, param_node *curr);
428
479
  int _ruby_ibm_db_SQLBindParameter_helper(bind_parameter_args *data);
429
480
  void _ruby_ibm_db_Statement_level_UBF(stmt_handle *stmt_res);
481
+ int _ruby_ibm_db_SQLCreateDB_helper(create_drop_db_args *data);
482
+ int _ruby_ibm_db_SQLDropDB_helper(create_drop_db_args *data);
430
483
 
431
484
  #endif /* RUBY_IBM_DB_CLI_H */
@@ -44,10 +44,15 @@ module ActiveRecord
44
44
  # (except for a CLOB field where '' can be a value)
45
45
  if self[col.name].nil? ||
46
46
  self[col.name] == {} ||
47
+ self[col.name] == [] ||
47
48
  (self[col.name] == '' && col.type != :text)
48
49
  params << 'NULL'
49
50
  else
50
- values << self[col.name]
51
+ if self.class.serialized_attributes[col.name]
52
+ values << YAML.dump(self[col.name])
53
+ else
54
+ values << self[col.name]
55
+ end
51
56
  params << '?'
52
57
  end
53
58
  counter += 1
@@ -117,24 +122,31 @@ module ActiveRecord
117
122
  # Flag to decide if quoted literal replcement should take place. By default it is ON. Set it to OFF if using Pstmt
118
123
  set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_ON
119
124
 
120
- # Retrieves the database alias (local catalog name) or remote name
121
- # (for remote TCP/IP connections) from the +config+ hash
122
- # or raises ArgumentError in case of failure.
123
- if config.has_key?(:database)
124
- database = config[:database].to_s
125
- else
126
- raise ArgumentError, "Missing argument: a database name needs to be specified."
127
- end
128
-
129
125
  # Retrieves database user credentials from the +config+ hash
130
126
  # or raises ArgumentError in case of failure.
131
127
  if !config.has_key?(:username) || !config.has_key?(:password)
132
128
  raise ArgumentError, "Missing argument(s): Username/Password for #{config[:database]} is not specified"
133
129
  else
130
+ if(config[:username].to_s.nil? || config[:password].to_s.nil?)
131
+ raise ArgumentError, "Username/Password cannot be nil"
132
+ end
134
133
  username = config[:username].to_s
135
134
  password = config[:password].to_s
136
135
  end
137
136
 
137
+ if(config.has_key?(:dbops) && config[:dbops] == true)
138
+ return ConnectionAdapters::IBM_DBAdapter.new(nil, logger, config, {})
139
+ end
140
+
141
+ # Retrieves the database alias (local catalog name) or remote name
142
+ # (for remote TCP/IP connections) from the +config+ hash
143
+ # or raises ArgumentError in case of failure.
144
+ if config.has_key?(:database)
145
+ database = config[:database].to_s
146
+ else
147
+ raise ArgumentError, "Missing argument: a database name needs to be specified."
148
+ end
149
+
138
150
  # Providing default schema (username) when not specified
139
151
  config[:schema] = config.has_key?(:schema) ? config[:schema].to_s : config[:username].to_s
140
152
 
@@ -232,8 +244,6 @@ module ActiveRecord
232
244
  :binary
233
245
  when /smallint/i
234
246
  :boolean
235
- when /bigint/i
236
- :bigint
237
247
  when /int|serial/i
238
248
  :integer
239
249
  when /decimal|numeric|decfloat/i
@@ -481,7 +491,8 @@ module ActiveRecord
481
491
  end
482
492
  when /DB2/i # DB2 for zOS
483
493
  case server_info.DBMS_VER
484
- when /09/ # DB2 for zOS version 9
494
+ when /09/ # DB2 for zOS version 9 and version 10
495
+ when /10/
485
496
  @servertype = IBM_DB2_ZOS.new(self)
486
497
  when /08/ # DB2 for zOS version 8
487
498
  @servertype = IBM_DB2_ZOS_8.new(self)
@@ -513,6 +524,17 @@ module ActiveRecord
513
524
  else
514
525
  @start_id = 1
515
526
  end
527
+
528
+ #Check Arel version
529
+ begin
530
+ @arelVersion = Arel::VERSION.to_i
531
+ rescue
532
+ @arelVersion = 0
533
+ end
534
+
535
+ if(@arelVersion >= 3 )
536
+ @visitor = Arel::Visitors::IBM_DB.new self
537
+ end
516
538
  end
517
539
 
518
540
  # Optional connection attribute: database name space qualifier
@@ -563,6 +585,20 @@ module ActiveRecord
563
585
  end
564
586
  end
565
587
 
588
+ def self.visitor_for(pool)
589
+ Arel::Visitors::IBM_DB.new(pool)
590
+ end
591
+
592
+ def to_sql(arel, binds = [])
593
+ if arel.respond_to?(:ast)
594
+ visitor.accept(arel.ast) do
595
+ quote(*binds.shift.reverse)
596
+ end
597
+ else
598
+ arel
599
+ end
600
+ end
601
+
566
602
  # This adapter supports migrations.
567
603
  # Current limitations:
568
604
  # +rename_column+ is not currently supported by the IBM data servers
@@ -599,6 +635,10 @@ module ActiveRecord
599
635
  # It connects to the database with the initially provided credentials
600
636
  def connect
601
637
  # If the type of connection is net based
638
+ if(@username.nil? || @password.nil?)
639
+ raise ArgumentError, "Username/Password cannot be nil"
640
+ end
641
+
602
642
  begin
603
643
  if @host
604
644
  @conn_string = "DRIVER={IBM DB2 ODBC DRIVER};\
@@ -641,6 +681,7 @@ module ActiveRecord
641
681
  # * true if succesfull
642
682
  # * false if the connection is already closed
643
683
  # * nil if an error is raised
684
+ return nil if @connection.nil? || @connection == false
644
685
  IBM_DB.close(@connection) rescue nil
645
686
  end
646
687
 
@@ -679,7 +720,7 @@ module ActiveRecord
679
720
  pstmt = prepare(sql_param_hash["sqlSegment"], name)
680
721
  if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
681
722
  begin
682
- @servertype.select(sql_param_hash["sqlSegment"], name, pstmt, results)
723
+ results = @servertype.select(pstmt)
683
724
  rescue StandardError => fetch_error # Handle driver fetch errors
684
725
  error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT )
685
726
  if error_msg && !error_msg.empty?
@@ -703,14 +744,13 @@ module ActiveRecord
703
744
  # and +name+ is an optional description for logging
704
745
  def prepared_select_values(sql_param_hash, name = nil)
705
746
  # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
706
-
707
747
  results = []
708
748
  # Invokes the method +prepare+ in order prepare the SQL
709
749
  # IBM_DB.Statement is returned from which the statement is executed and results fetched
710
750
  pstmt = prepare(sql_param_hash["sqlSegment"], name)
711
751
  if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
712
752
  begin
713
- @servertype.select_rows(sql_param_hash["sqlSegment"], name, pstmt, results)
753
+ results = @servertype.select_rows(sql_param_hash["sqlSegment"], name, pstmt, results)
714
754
  if results
715
755
  return results.map { |v| v[0] }
716
756
  else
@@ -734,20 +774,11 @@ module ActiveRecord
734
774
  results
735
775
  end
736
776
 
737
- # Returns an array of hashes with the column names as keys and
738
- # column values as values. +sql+ is the select query,
739
- # and +name+ is an optional description for logging
740
- def select(sql, name = nil)
741
- # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
742
- sql.gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
743
-
744
- results = []
745
- # Invokes the method +execute+ in order to log and execute the SQL
746
- # IBM_DB.Statement is returned from which results can be fetched
747
- stmt = execute(sql, name)
777
+ #Calls the servertype select method to fetch the data
778
+ def fetch_data(stmt)
748
779
  if(stmt)
749
780
  begin
750
- @servertype.select(sql, name, stmt, results)
781
+ return @servertype.select(stmt)
751
782
  rescue StandardError => fetch_error # Handle driver fetch errors
752
783
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
753
784
  if error_msg && !error_msg.empty?
@@ -758,13 +789,47 @@ module ActiveRecord
758
789
  raise error_msg
759
790
  end
760
791
  ensure
761
- # Ensures to free the resources associated with the statement
792
+ # Ensures to free the resources associated with the statement
762
793
  IBM_DB.free_stmt(stmt) if stmt
763
794
  end
764
795
  end
796
+ end
797
+ =begin
798
+ # Returns an array of hashes with the column names as keys and
799
+ # column values as values. +sql+ is the select query,
800
+ # and +name+ is an optional description for logging
801
+ def select(sql, name = nil)
802
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
803
+ sql.gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
804
+
805
+ results = []
806
+ # Invokes the method +execute+ in order to log and execute the SQL
807
+ # IBM_DB.Statement is returned from which results can be fetched
808
+ stmt = execute(sql, name)
809
+
810
+ results = fetch_data(stmt)
765
811
  # The array of record hashes is returned
766
812
  results
767
813
  end
814
+ =end
815
+ def select(sql, name = nil, binds = [])
816
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
817
+ sql.gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
818
+
819
+ results = []
820
+
821
+ if(binds.nil? || binds.empty?)
822
+ stmt = execute(sql, name)
823
+ else
824
+ stmt = exec_query(sql, name, binds)
825
+ end
826
+
827
+ if( stmt )
828
+ results = fetch_data(stmt)
829
+ end
830
+
831
+ return results
832
+ end
768
833
 
769
834
  #Returns an array of arrays containing the field values.
770
835
  #This is an implementation for the abstract method
@@ -779,7 +844,7 @@ module ActiveRecord
779
844
  stmt = execute(sql, name)
780
845
  if(stmt)
781
846
  begin
782
- @servertype.select_rows(sql, name, stmt, results)
847
+ results = @servertype.select_rows(sql, name, stmt, results)
783
848
  rescue StandardError => fetch_error # Handle driver fetch errors
784
849
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
785
850
  if error_msg && !error_msg.empty?
@@ -810,7 +875,12 @@ module ActiveRecord
810
875
  #overridden to handle LOB's fixture insertion, as, in normal inserts callbacks are triggered but during fixture insertion callbacks are not triggered
811
876
  #hence only markers like @@@IBMBINARY@@@ will be inserted and are not updated to actual data
812
877
  def insert_fixture(fixture, table_name)
813
- insert_query = "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.key_list})"
878
+ if(fixture.respond_to?(:keys))
879
+ insert_query = "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.keys.join(', ')})"
880
+ else
881
+ insert_query = "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.key_list})"
882
+ end
883
+
814
884
  insert_values = []
815
885
  params = []
816
886
  if @servertype.instance_of? IBM_IDS
@@ -870,7 +940,7 @@ module ActiveRecord
870
940
  # Perform an insert and returns the last ID generated.
871
941
  # This can be the ID passed to the method or the one auto-generated by the database,
872
942
  # and retrieved by the +last_generated_id+ method.
873
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
943
+ def insert_direct(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
874
944
  if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
875
945
  @sql = []
876
946
  @handle_lobs_triggered = false
@@ -889,11 +959,30 @@ module ActiveRecord
889
959
  end
890
960
  end
891
961
 
962
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [] )
963
+ sql, binds = [to_sql(arel),binds]
964
+
965
+ #unless IBM_DBAdapter.respond_to?(:exec_insert)
966
+ if binds.nil? || binds.empty?
967
+ return insert_direct(sql, name, pk, id_value, sequence_name)
968
+ end
969
+
970
+ clear_query_cache if defined? clear_query_cache
971
+ if stmt = exec_insert(sql, name, binds)
972
+ begin
973
+ @sql << sql
974
+ return id_value || @servertype.last_generated_id(stmt)
975
+ ensure
976
+ IBM_DB.free_stmt(stmt) if stmt
977
+ end
978
+ end
979
+ end
980
+
892
981
  # Praveen
893
982
  # Performs an insert using the prepared statement and returns the last ID generated.
894
983
  # This can be the ID passed to the method or the one auto-generated by the database,
895
984
  # and retrieved by the +last_generated_id+ method.
896
- def prepared_insert(pstmt, param_array = nil)
985
+ def prepared_insert(pstmt, param_array = nil, id_value = nil)
897
986
  if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
898
987
  @sql = []
899
988
  @sql_parameter_values = []
@@ -906,7 +995,7 @@ module ActiveRecord
906
995
  if execute_prepared_stmt(pstmt, param_array)
907
996
  @sql << @prepared_sql
908
997
  @sql_parameter_values << param_array
909
- return @servertype.last_generated_id(pstmt)
998
+ return id_value || @servertype.last_generated_id(pstmt)
910
999
  end
911
1000
  rescue StandardError => insert_err
912
1001
  raise insert_err
@@ -948,6 +1037,29 @@ module ActiveRecord
948
1037
  end
949
1038
  end
950
1039
 
1040
+ # Executes +sql+ statement in the context of this connection using
1041
+ # +binds+ as the bind substitutes. +name+ is logged along with
1042
+ # the executed +sql+ statement.
1043
+ def exec_query(sql, name = 'SQL', binds = [])
1044
+ begin
1045
+ param_array = binds.map do |column,value|
1046
+ quote_value_for_pstmt(value, column)
1047
+ end
1048
+
1049
+ stmt = prepare(sql, name)
1050
+
1051
+ if( stmt )
1052
+ if(execute_prepared_stmt(stmt, param_array))
1053
+ return stmt
1054
+ end
1055
+ else
1056
+ return false
1057
+ end
1058
+ ensure
1059
+ @offset = @limit = nil
1060
+ end
1061
+ end
1062
+
951
1063
  # Executes and logs +sql+ commands and
952
1064
  # returns a +IBM_DB.Statement+ object.
953
1065
  def execute(sql, name = nil)
@@ -959,29 +1071,12 @@ module ActiveRecord
959
1071
  end
960
1072
 
961
1073
  # Executes an "UPDATE" SQL statement
962
- def update(sql, name = nil)
1074
+ def update_direct(sql, name = nil)
963
1075
  if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
964
1076
  @sql = []
965
1077
  @handle_lobs_triggered = false
966
1078
  end
967
1079
 
968
- clear_query_cache if defined? clear_query_cache
969
-
970
- # Make sure the WHERE clause handles NULL's correctly
971
- sqlarray = sql.split(/\s*WHERE\s*/)
972
- size = sqlarray.size
973
- if size > 1
974
- sql = sqlarray[0] + " WHERE "
975
- if size > 2
976
- 1.upto size-2 do |index|
977
- sqlarray[index].gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" ) unless sqlarray[index].nil?
978
- sql = sql + sqlarray[index] + " WHERE "
979
- end
980
- end
981
- sqlarray[size-1].gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" ) unless sqlarray[size-1].nil?
982
- sql = sql + sqlarray[size-1]
983
- end
984
-
985
1080
  # Logs and execute the given sql query.
986
1081
  if stmt = execute(sql, name)
987
1082
  begin
@@ -1022,9 +1117,39 @@ module ActiveRecord
1022
1117
  # The delete method executes the delete
1023
1118
  # statement and returns the number of affected rows.
1024
1119
  # The method is an alias for +update+
1025
- alias_method :delete, :update
1026
1120
  alias_method :prepared_delete, :prepared_update
1027
1121
 
1122
+ def update(arel, name = nil, binds = [])
1123
+ sql = to_sql(arel)
1124
+
1125
+ # Make sure the WHERE clause handles NULL's correctly
1126
+ sqlarray = sql.split(/\s*WHERE\s*/)
1127
+ size = sqlarray.size
1128
+ if size > 1
1129
+ sql = sqlarray[0] + " WHERE "
1130
+ if size > 2
1131
+ 1.upto size-2 do |index|
1132
+ sqlarray[index].gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" ) unless sqlarray[index].nil?
1133
+ sql = sql + sqlarray[index] + " WHERE "
1134
+ end
1135
+ end
1136
+ sqlarray[size-1].gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" ) unless sqlarray[size-1].nil?
1137
+ sql = sql + sqlarray[size-1]
1138
+ end
1139
+
1140
+ clear_query_cache if defined? clear_query_cache
1141
+
1142
+ if binds.nil? || binds.empty?
1143
+ update_direct(sql, name)
1144
+ else
1145
+ if stmt = exec_query(sql,name,binds)
1146
+ IBM_DB.num_rows(stmt)
1147
+ end
1148
+ end
1149
+ end
1150
+
1151
+ alias_method :delete, :update
1152
+
1028
1153
  # Begins the transaction (and turns off auto-committing)
1029
1154
  def begin_db_transaction
1030
1155
  # Turns off the auto-commit
@@ -1065,27 +1190,26 @@ module ActiveRecord
1065
1190
  # generates "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_rownum
1066
1191
  # FROM (SELECT * FROM staff) AS I) AS O WHERE sys_row_num BETWEEN 31 AND 40"
1067
1192
  def add_limit_offset!(sql, options)
1068
- # If there is a limit
1069
- if limit = options[:limit]
1070
- # if the limit is zero
1071
- if limit == 0
1072
- # Returns a query that will always generate zero records
1073
- # (e.g. WHERE sys_row_num BETWEEN 1 and 0)
1074
- if( @pstmt_support_on )
1075
- sql = @servertype.query_offset_limit!(sql, 0, limit, options)
1076
- else
1077
- sql = @servertype.query_offset_limit(sql, 0, limit)
1078
- end
1079
- # If there is a non-zero limit
1193
+ limit = options[:limit]
1194
+ offset = options[:offset]
1195
+
1196
+ # if the limit is zero
1197
+ if limit && limit == 0
1198
+ # Returns a query that will always generate zero records
1199
+ # (e.g. WHERE sys_row_num BETWEEN 1 and 0)
1200
+ if( @pstmt_support_on )
1201
+ sql = @servertype.query_offset_limit!(sql, 0, limit, options)
1080
1202
  else
1081
- offset = options[:offset]
1082
- # If an offset is specified builds the query with offset and limit,
1083
- # otherwise retrieves only the first +limit+ rows
1084
- if( @pstmt_support_on )
1085
- sql = @servertype.query_offset_limit!(sql, offset, limit, options)
1086
- else
1087
- sql = @servertype.query_offset_limit(sql, offset, limit)
1088
- end
1203
+ sql = @servertype.query_offset_limit(sql, 0, limit)
1204
+ end
1205
+ # If there is a non-zero limit
1206
+ else
1207
+ # If an offset is specified builds the query with offset and limit,
1208
+ # otherwise retrieves only the first +limit+ rows
1209
+ if( @pstmt_support_on )
1210
+ sql = @servertype.query_offset_limit!(sql, offset, limit, options)
1211
+ else
1212
+ sql = @servertype.query_offset_limit(sql, offset, limit)
1089
1213
  end
1090
1214
  end
1091
1215
  # Returns the sql query in any case
@@ -1121,6 +1245,7 @@ module ActiveRecord
1121
1245
  when Float, Fixnum, Bignum then value
1122
1246
  # BigDecimals need to be output in a non-normalized form and quoted.
1123
1247
  when BigDecimal then value.to_s('F')
1248
+ when Numeric, Symbol then value.to_s
1124
1249
  else
1125
1250
  if value.acts_like?(:date) || value.acts_like?(:time)
1126
1251
  quoted_date(value)
@@ -1133,6 +1258,8 @@ module ActiveRecord
1133
1258
  # Properly quotes the various data types.
1134
1259
  # +value+ contains the data, +column+ is optional and contains info on the field
1135
1260
  def quote(value, column = nil)
1261
+ return value.quoted_id if value.respond_to?(:quoted_id)
1262
+
1136
1263
  case value
1137
1264
  # If it's a numeric value and the column type is not a string, it shouldn't be quoted
1138
1265
  # (IBM_DB doesn't accept quotes on numeric types)
@@ -1173,14 +1300,22 @@ module ActiveRecord
1173
1300
  end
1174
1301
  else
1175
1302
  unless caller[0] =~ /insert_fixture/i
1176
- "'#{quote_string(value)}'"
1303
+ super
1177
1304
  else
1178
1305
  "#{value}"
1179
1306
  end
1180
1307
  end
1181
1308
  when TrueClass then quoted_true # return '1' for true
1182
1309
  when FalseClass then quoted_false # return '0' for false
1183
- else super # rely on superclass handling
1310
+ when nil then "NULL"
1311
+ when Date, Time then "'#{quoted_date(value)}'"
1312
+ when Symbol then "'#{quote_string(value.to_s)}'"
1313
+ else
1314
+ unless caller[0] =~ /insert_fixture/i
1315
+ "'#{quote_string(YAML.dump(value))}'"
1316
+ else
1317
+ "#{quote_string(YAML.dump(value))}"
1318
+ end
1184
1319
  end
1185
1320
  end
1186
1321
 
@@ -1240,6 +1375,63 @@ module ActiveRecord
1240
1375
  }
1241
1376
  end
1242
1377
 
1378
+ def build_conn_str_for_dbops()
1379
+ connect_str = "DRIVER={IBM DB2 ODBC DRIVER};ATTACH=true;"
1380
+ if(!@host.nil?)
1381
+ connect_str << "HOSTNAME=#{@host};"
1382
+ connect_str << "PORT=#{@port};"
1383
+ connect_str << "PROTOCOL=TCPIP;"
1384
+ end
1385
+ connect_str << "UID=#{@username};PWD=#{@password};"
1386
+ return connect_str
1387
+ end
1388
+
1389
+ def drop_database(dbName)
1390
+ connect_str = build_conn_str_for_dbops()
1391
+
1392
+ #Ensure connection is closed before trying to drop a database.
1393
+ #As a connect call would have been made by call seeing connection in active
1394
+ disconnect!
1395
+
1396
+ begin
1397
+ dropConn = IBM_DB.connect(connect_str, '', '')
1398
+ rescue StandardError => connect_err
1399
+ raise "Failed to connect to server due to: #{connect_err}"
1400
+ end
1401
+
1402
+ if(IBM_DB.dropDB(dropConn,dbName))
1403
+ IBM_DB.close(dropConn)
1404
+ return true
1405
+ else
1406
+ error = IBM_DB.getErrormsg(dropConn, IBM_DB::DB_CONN)
1407
+ IBM_DB.close(dropConn)
1408
+ raise "Could not drop Database due to: #{error}"
1409
+ end
1410
+ end
1411
+
1412
+ def create_database(dbName, codeSet=nil, mode=nil)
1413
+ connect_str = build_conn_str_for_dbops()
1414
+
1415
+ #Ensure connection is closed before trying to drop a database.
1416
+ #As a connect call would have been made by call seeing connection in active
1417
+ disconnect!
1418
+
1419
+ begin
1420
+ createConn = IBM_DB.connect(connect_str, '', '')
1421
+ rescue StandardError => connect_err
1422
+ raise "Failed to connect to server due to: #{connect_err}"
1423
+ end
1424
+
1425
+ if(IBM_DB.createDB(createConn,dbName,codeSet,mode))
1426
+ IBM_DB.close(createConn)
1427
+ return true
1428
+ else
1429
+ error = IBM_DB.getErrormsg(createConn, IBM_DB::DB_CONN)
1430
+ IBM_DB.close(createConn)
1431
+ raise "Could not create Database due to: #{error}"
1432
+ end
1433
+ end
1434
+
1243
1435
  # IBM data servers do not support limits on certain data types (unlike MySQL)
1244
1436
  # Limit is supported for the {float, decimal, numeric, varchar, clob, blob, graphic, vargraphic} data types.
1245
1437
  def type_to_sql(type, limit = nil, precision = nil, scale = nil)
@@ -1272,13 +1464,16 @@ module ActiveRecord
1272
1464
  # Initializes the tables array
1273
1465
  tables = []
1274
1466
  # Retrieve table's metadata through IBM_DB driver
1275
- stmt = IBM_DB.tables(@connection, nil, @servertype.set_case(@schema))
1467
+ stmt = IBM_DB.tables(@connection, nil,
1468
+ @servertype.set_case(@schema))
1276
1469
  if(stmt)
1277
1470
  begin
1278
1471
  # Fetches all the records available
1279
1472
  while tab = IBM_DB.fetch_assoc(stmt)
1280
1473
  # Adds the lowercase table name to the array
1281
- tables << tab["table_name"].downcase
1474
+ if(tab["table_type"]== 'TABLE') #check, so that only tables are dumped,IBM_DB.tables also returns views,alias etc in the schema
1475
+ tables << tab["table_name"].downcase
1476
+ end
1282
1477
  end
1283
1478
  rescue StandardError => fetch_error # Handle driver fetch errors
1284
1479
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
@@ -1482,6 +1677,8 @@ module ActiveRecord
1482
1677
  column_default_value = col["column_def"]
1483
1678
  # If there is no default value, it assigns NIL
1484
1679
  column_default_value = nil if (column_default_value && column_default_value.upcase == 'NULL')
1680
+ # If default value is IDENTITY GENERATED BY DEFAULT (this value is retrieved in case of id columns)
1681
+ column_default_value = nil if (column_default_value && column_default_value.upcase =~ /IDENTITY GENERATED BY DEFAULT/i)
1485
1682
  # Removes single quotes from the default value
1486
1683
  column_default_value.gsub!(/^'(.*)'$/, '\1') unless column_default_value.nil?
1487
1684
  # Assigns the column type
@@ -1600,6 +1797,15 @@ module ActiveRecord
1600
1797
  end
1601
1798
  =end
1602
1799
 
1800
+ #Add distinct clause to the sql if there is no order by specified
1801
+ def distinct(columns, order_by)
1802
+ if order_by.nil?
1803
+ "DISTINCT #{columns}"
1804
+ else
1805
+ "#{columns}"
1806
+ end
1807
+ end
1808
+
1603
1809
  # Sets a new default value for a column. This does not set the default
1604
1810
  # value to +NULL+, instead, it needs DatabaseStatements#execute which
1605
1811
  # can execute the appropriate SQL statement for setting the value.
@@ -1653,7 +1859,7 @@ module ActiveRecord
1653
1859
  end
1654
1860
 
1655
1861
  def check_reserved_words(col_name)
1656
- col_name
1862
+ col_name.to_s
1657
1863
  end
1658
1864
 
1659
1865
  # This is supported by the DB2 for Linux, UNIX, Windows data servers
@@ -1674,7 +1880,8 @@ To remove the column, the table must be dropped and recreated without the #{colu
1674
1880
  end
1675
1881
  end
1676
1882
 
1677
- def select(sql, name, stmt, results)
1883
+ def select(stmt)
1884
+ results = []
1678
1885
  # Fetches all the results available. IBM_DB.fetch_assoc(stmt) returns
1679
1886
  # an hash for each single record.
1680
1887
  # The loop stops when there aren't any more valid records to fetch
@@ -1693,6 +1900,7 @@ To remove the column, the table must be dropped and recreated without the #{colu
1693
1900
  raise error_msg
1694
1901
  end
1695
1902
  end
1903
+ return results
1696
1904
  end
1697
1905
 
1698
1906
  def select_rows(sql, name, stmt, results)
@@ -1714,6 +1922,7 @@ To remove the column, the table must be dropped and recreated without the #{colu
1714
1922
  raise error_msg
1715
1923
  end
1716
1924
  end
1925
+ return results
1717
1926
  end
1718
1927
 
1719
1928
  # Praveen
@@ -1919,11 +2128,14 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1919
2128
  def get_double_mapping
1920
2129
  return "double"
1921
2130
  end
1922
-
2131
+ =begin
2132
+ # Commenting this code, as offset handling is now part of sql and we need to handle it in select and also
2133
+ # need not set cursor type during prepare or execute
1923
2134
  # Fetches all the results available. IBM_DB.fetch_assoc(stmt) returns
1924
2135
  # an hash for each single record.
1925
2136
  # The loop stops when there aren't any more valid records to fetch
1926
- def select(sql, name, stmt, results)
2137
+ def select(stmt)
2138
+ results = []
1927
2139
  begin
1928
2140
  if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
1929
2141
  # We know at this point that there is an offset and/or a limit
@@ -1973,6 +2185,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1973
2185
  # Add the record to the +results+ array
1974
2186
  results << single_hash
1975
2187
  end
2188
+ return results
1976
2189
  end
1977
2190
  rescue StandardError => fetch_error # Handle driver fetch errors
1978
2191
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
@@ -2058,6 +2271,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2058
2271
  @offset = nil
2059
2272
  @limit = nil
2060
2273
  end
2274
+ return results
2061
2275
  end
2062
2276
 
2063
2277
  # Praveen
@@ -2113,22 +2327,62 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2113
2327
  raise error_msg
2114
2328
  end
2115
2329
  end
2116
-
2330
+ =end
2117
2331
  def query_offset_limit(sql, offset, limit)
2118
- @limit = limit
2119
- @offset = offset
2332
+ if(offset.nil? && limit.nil?)
2333
+ return sql
2334
+ end
2335
+
2120
2336
  if (offset.nil?)
2121
- sql << " FETCH FIRST #{limit} ROWS ONLY"
2337
+ return sql << " FETCH FIRST #{limit} ROWS ONLY"
2338
+ end
2339
+
2340
+ if(limit.nil?)
2341
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2342
+ return sql << ") AS I) AS O WHERE sys_row_num > #{offset}"
2122
2343
  end
2344
+
2345
+ # Defines what will be the last record
2346
+ last_record = offset + limit
2347
+ # Transforms the SELECT query in order to retrieve/fetch only
2348
+ # a number of records after the specified offset.
2349
+ # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2350
+ # to select with the condition of this column being between offset+1 and the offset+limit
2351
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2352
+ # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2353
+ # and retrieve only a LIMIT number of records starting from the OFFSET+1
2354
+ sql << ") AS I) AS O WHERE sys_row_num BETWEEN #{offset+1} AND #{last_record}"
2123
2355
  end
2124
2356
 
2125
2357
  def query_offset_limit!(sql, offset, limit, options)
2126
- @limit = limit
2127
- @offset = offset
2358
+ if(offset.nil? && limit.nil?)
2359
+ options[:paramArray] = []
2360
+ return sql
2361
+ end
2362
+
2128
2363
  if (offset.nil?)
2129
- sql << " FETCH FIRST #{limit} ROWS ONLY"
2364
+ options[:paramArray] = []
2365
+ return sql << " FETCH FIRST #{limit} ROWS ONLY"
2130
2366
  end
2131
- options[:paramArray] = []
2367
+
2368
+ if(limit.nil?)
2369
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2370
+ sql << ") AS I) AS O WHERE sys_row_num > ?"
2371
+ options[:paramArray] = [offset]
2372
+ return
2373
+ end
2374
+
2375
+ # Defines what will be the last record
2376
+ last_record = offset + limit
2377
+ # Transforms the SELECT query in order to retrieve/fetch only
2378
+ # a number of records after the specified offset.
2379
+ # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2380
+ # to select with the condition of this column being between offset+1 and the offset+limit
2381
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2382
+ # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2383
+ # and retrieve only a LIMIT number of records starting from the OFFSET+1
2384
+ sql << ") AS I) AS O WHERE sys_row_num BETWEEN ? AND ?"
2385
+ options[:paramArray] = [offset+1, last_record]
2132
2386
  end
2133
2387
 
2134
2388
  # This method generates the default blob value specified for
@@ -2160,40 +2414,6 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2160
2414
  def reorg_table(table_name)
2161
2415
  execute("CALL ADMIN_CMD('REORG TABLE #{table_name}')")
2162
2416
  end
2163
-
2164
- def query_offset_limit(sql, offset, limit)
2165
- if (offset.nil?)
2166
- return sql << " FETCH FIRST #{limit} ROWS ONLY"
2167
- end
2168
- # Defines what will be the last record
2169
- last_record = offset + limit
2170
- # Transforms the SELECT query in order to retrieve/fetch only
2171
- # a number of records after the specified offset.
2172
- # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2173
- # to select with the condition of this column being between offset+1 and the offset+limit
2174
- sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2175
- # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2176
- # and retrieve only a LIMIT number of records starting from the OFFSET+1
2177
- sql << ") AS I) AS O WHERE sys_row_num BETWEEN #{offset+1} AND #{last_record}"
2178
- end
2179
-
2180
- def query_offset_limit!(sql, offset, limit, options)
2181
- if (offset.nil?)
2182
- options[:paramArray] = []
2183
- return sql << " FETCH FIRST #{limit} ROWS ONLY"
2184
- end
2185
- # Defines what will be the last record
2186
- last_record = offset + limit
2187
- # Transforms the SELECT query in order to retrieve/fetch only
2188
- # a number of records after the specified offset.
2189
- # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2190
- # to select with the condition of this column being between offset+1 and the offset+limit
2191
- sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2192
- # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2193
- # and retrieve only a LIMIT number of records starting from the OFFSET+1
2194
- sql << ") AS I) AS O WHERE sys_row_num BETWEEN ? AND ?"
2195
- options[:paramArray] = [offset+1, last_record]
2196
- end
2197
2417
  end # class IBM_DB2_LUW
2198
2418
 
2199
2419
  class IBM_DB2_LUW_COBRA < IBM_DB2_LUW
@@ -2250,7 +2470,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2250
2470
  if RESERVED_WORDS[col_name]
2251
2471
  '"' + RESERVED_WORDS[col_name] + '"'
2252
2472
  else
2253
- col_name
2473
+ col_name.to_s
2254
2474
  end
2255
2475
  end
2256
2476
  else
@@ -2328,6 +2548,20 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2328
2548
  class IBM_DB2_ZOS_8 < IBM_DB2_ZOS
2329
2549
  include HostedDataServer
2330
2550
 
2551
+ def query_offset_limit(sql, offset, limit)
2552
+ if (!limit.nil?)
2553
+ sql << " FETCH FIRST #{limit} ROWS ONLY"
2554
+ end
2555
+ return sql
2556
+ end
2557
+
2558
+ def query_offset_limit!(sql, offset, limit, options)
2559
+ if (!limit.nil?)
2560
+ sql << " FETCH FIRST #{limit} ROWS ONLY"
2561
+ end
2562
+ options[:paramArray] = []
2563
+ end
2564
+
2331
2565
  # This call is needed on DB2 z/OS v8 for the creation of tables
2332
2566
  # with LOBs. When issued, this call does the following:
2333
2567
  # DB2 creates LOB table spaces, auxiliary tables, and indexes on auxiliary
@@ -2553,3 +2787,68 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2553
2787
  end # class IBM_IDS
2554
2788
  end # module ConnectionAdapters
2555
2789
  end # module ActiveRecord
2790
+
2791
+ module Arel
2792
+ module Visitors
2793
+ class Visitor #opening and closing the class to ensure backward compatibility
2794
+ end
2795
+
2796
+ class ToSql < Arel::Visitors::Visitor #opening and closing the class to ensure backward compatibility
2797
+ # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
2798
+ # to ensure the same code works on any version of Rails
2799
+
2800
+ #Check Arel version
2801
+ begin
2802
+ @arelVersion = Arel::VERSION.to_i
2803
+ rescue
2804
+ @arelVersion = 0
2805
+ end
2806
+
2807
+ if(@arelVersion >= 3)
2808
+ def initialize connection
2809
+ @connection = connection
2810
+ @schema_cache = connection.schema_cache if(connection.respond_to?(:schema_cache))
2811
+ @quoted_tables = {}
2812
+ @quoted_columns = {}
2813
+ @last_column = nil
2814
+ end
2815
+ end
2816
+ end
2817
+
2818
+ class IBM_DB < Arel::Visitors::ToSql
2819
+ private
2820
+
2821
+ def visit_Arel_Nodes_Limit o
2822
+ visit o.expr
2823
+ end
2824
+
2825
+ def visit_Arel_Nodes_Offset o
2826
+ visit o.expr
2827
+ end
2828
+
2829
+ def visit_Arel_Nodes_SelectStatement o
2830
+ sql = [
2831
+ (visit(o.with) if o.with),
2832
+ o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
2833
+ ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
2834
+ ].compact.join ' '
2835
+
2836
+ if o.limit
2837
+ limit = visit(o.limit)
2838
+ else
2839
+ limit = nil
2840
+ end
2841
+
2842
+ if o.offset
2843
+ offset = visit(o.offset)
2844
+ else
2845
+ offset = nil
2846
+ end
2847
+ @connection.add_limit_offset!(sql, {:limit => limit, :offset => offset})
2848
+ sql << " #{(visit(o.lock) if o.lock)}"
2849
+ return sql
2850
+ end
2851
+
2852
+ end
2853
+ end
2854
+ end