ibm_db 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/ruby_ibm_db.h CHANGED
@@ -2,11 +2,11 @@
2
2
  +----------------------------------------------------------------------+
3
3
  | Licensed Materials - Property of IBM |
4
4
  | |
5
- | (C) Copyright IBM Corporation 2006, 2007, 2008, 2009 |
5
+ | (C) Copyright IBM Corporation 2006, 2007, 2008, 2009, 2010 |
6
6
  +----------------------------------------------------------------------+
7
7
  | Authors: Sushant Koduru, Lynh Nguyen, Kanchana Padmanabhan, |
8
8
  | Dan Scott, Helmut Tessarek, Kellen Bombardier, Sam Ruby |
9
- | Ambrish Bhargava, Tarun Pasrija |
9
+ | Ambrish Bhargava, Tarun Pasrija, Praveen Devarao |
10
10
  +----------------------------------------------------------------------+
11
11
  */
12
12
 
@@ -81,6 +81,10 @@
81
81
  #define CONN_ERROR 1
82
82
  #define STMT_ERROR 2
83
83
 
84
+ /*Used to decide if LITERAL REPLACEMENT should be turned on or not*/
85
+ #define SET_QUOTED_LITERAL_REPLACEMENT_ON 1
86
+ #define SET_QUOTED_LITERAL_REPLACEMENT_OFF 0
87
+
84
88
  /* DB2 instance environment variable */
85
89
  #define DB2_VAR_INSTANCE "DB2INSTANCE="
86
90
 
@@ -2,7 +2,7 @@
2
2
  +----------------------------------------------------------------------+
3
3
  | Licensed Materials - Property of IBM |
4
4
  | |
5
- | (C) Copyright IBM Corporation 2009 |
5
+ | (C) Copyright IBM Corporation 2009, 2010 |
6
6
  +----------------------------------------------------------------------+
7
7
  | Authors: Praveen Devarao |
8
8
  +----------------------------------------------------------------------+
@@ -517,7 +517,7 @@ int _ruby_ibm_db_SQLNumParams_helper(row_col_count_args *data) {
517
517
  /*
518
518
  This function calls SQLRowCount cli call to fetch the number of rows affected by the SQL statement
519
519
  */
520
- int _ruby_ibm_db_SQLRowCount_helper(row_col_count_args *data) {
520
+ int _ruby_ibm_db_SQLRowCount_helper(sql_row_count_args *data) {
521
521
  int rc = 0;
522
522
 
523
523
  data->stmt_res->is_executing = 1;
@@ -626,4 +626,4 @@ void _ruby_ibm_db_Statement_level_UBF(stmt_handle *stmt_res) {
626
626
  rc = SQLCancel( (SQLHSTMT) stmt_res->hstmt );
627
627
  }
628
628
  return;
629
- }
629
+ }
@@ -2,7 +2,7 @@
2
2
  +----------------------------------------------------------------------+
3
3
  | Licensed Materials - Property of IBM |
4
4
  | |
5
- | (C) Copyright IBM Corporation 2009 |
5
+ | (C) Copyright IBM Corporation 2009, 2010 |
6
6
  +----------------------------------------------------------------------+
7
7
  | Authors: Praveen Devarao |
8
8
  +----------------------------------------------------------------------+
@@ -29,9 +29,9 @@ typedef struct _param_cache_node {
29
29
  int param_type; /* Type of param - INP/OUT/INP-OUT/FILE */
30
30
  int size; /* Size of param */
31
31
  char *varname; /* bound variable name */
32
- long ivalue; /* Temp storage value */
33
- double fvalue; /* Temp storage value */
34
- char *svalue; /* Temp storage value */
32
+ SQLBIGINT ivalue; /* Temp storage value */
33
+ SQLDOUBLE fvalue; /* Temp storage value */
34
+ SQLCHAR *svalue; /* Temp storage value */
35
35
  struct _param_cache_node *next; /* Pointer to next node */
36
36
  } param_node;
37
37
 
@@ -99,6 +99,7 @@ typedef struct _stmt_handle_struct {
99
99
  int file_param; /* if option passed in is FILE_PARAM */
100
100
  int num_columns;
101
101
  int is_executing;
102
+ int is_freed; /* Indicates if the SQLFreeHandle is been called on the handle or not.*/
102
103
 
103
104
  ibm_db_result_set_info *column_info;
104
105
  ibm_db_row_type *row_data;
@@ -195,13 +196,21 @@ typedef struct _ibm_db_next_result_args_struct {
195
196
  } next_result_args;
196
197
 
197
198
  /*
198
- Structure holding the necessary info to be passed to calls SQLNumResultCols/SQLRowcount/SQLNumParams
199
+ Structure holding the necessary info to be passed to calls SQLNumResultCols/SQLNumParams
199
200
  */
200
201
  typedef struct _ibm_db_row_col_count_struct {
201
202
  stmt_handle *stmt_res;
202
- SQLINTEGER count;
203
+ SQLSMALLINT count;
203
204
  } row_col_count_args;
204
205
 
206
+ /*
207
+ Structure holding the necessary info to be passed to call SQLRowcount
208
+ */
209
+ typedef struct _ibm_db_row_count_struct {
210
+ stmt_handle *stmt_res;
211
+ SQLINTEGER count;
212
+ } sql_row_count_args;
213
+
205
214
  /*
206
215
  Structure holding the necessary info to be passed to call SQLColAttributes
207
216
  */
@@ -374,7 +383,7 @@ int _ruby_ibm_db_SQLFetchScroll_helper(fetch_data_args *data);
374
383
  int _ruby_ibm_db_SQLFetch_helper(fetch_data_args *data);
375
384
  int _ruby_ibm_db_SQLNumResultCols_helper(row_col_count_args *data);
376
385
  int _ruby_ibm_db_SQLNumParams_helper(row_col_count_args *data);
377
- int _ruby_ibm_db_SQLRowCount_helper(row_col_count_args *data);
386
+ int _ruby_ibm_db_SQLRowCount_helper(sql_row_count_args *data);
378
387
  int _ruby_ibm_db_SQLGetInfo_helper(get_info_args *data);
379
388
  int _ruby_ibm_db_SQLGetDiagRec_helper(get_diagRec_args *data);
380
389
  int _ruby_ibm_db_SQLSetStmtAttr_helper(set_handle_attr_args *data);
@@ -386,4 +395,4 @@ int _ruby_ibm_db_SQLBindFileToParam_helper(stmt_handle *stmt_res, param_node *cu
386
395
  int _ruby_ibm_db_SQLBindParameter_helper(bind_parameter_args *data);
387
396
  void _ruby_ibm_db_Statement_level_UBF(stmt_handle *stmt_res);
388
397
 
389
- #endif /* RUBY_IBM_DB_CLI_H */
398
+ #endif /* RUBY_IBM_DB_CLI_H */
data/lib/IBM_DB.rb CHANGED
@@ -1,2 +1,2 @@
1
- require (RUBY_PLATFORM =~ /mswin32/) ? 'mswin32/ibm_db.so' : 'ibm_db.so'
1
+ require (RUBY_PLATFORM =~ /mswin32/ || RUBY_PLATFORM =~ /mingw32/ ) ? 'mswin32/ibm_db.so' : 'ibm_db.so'
2
2
  require 'active_record/connection_adapters/ibm_db_adapter'
@@ -1,7 +1,7 @@
1
1
  # +----------------------------------------------------------------------+
2
2
  # | Licensed Materials - Property of IBM |
3
3
  # | |
4
- # | (C) Copyright IBM Corporation 2006, 2007, 2008, 2009 |
4
+ # | (C) Copyright IBM Corporation 2006, 2007, 2008, 2009, 2010 |
5
5
  # +----------------------------------------------------------------------+
6
6
  # | Authors: Antonio Cangiano <cangiano@ca.ibm.com> |
7
7
  # | : Mario Ds Briggs <mario.briggs@in.ibm.com> |
@@ -66,24 +66,28 @@ module ActiveRecord
66
66
 
67
67
  update_query << " WHERE #{self.class.primary_key} = ?"
68
68
  values << self[self.class.primary_key.downcase]
69
-
70
- unless stmt = IBM_DB.prepare(connection.connection, update_query)
71
- error_msg = IBM_DB.getErrormsg( connection.connection, IBM_DB::DB_CONN )
72
- if error_msg && !error_msg.empty?
73
- raise "Statement prepare for updating LOB/XML column failed : #{error_msg}"
74
- else
75
- raise StandardError.new('An unexpected error occurred during update of LOB/XML column')
69
+
70
+ begin
71
+ unless stmt = IBM_DB.prepare(connection.connection, update_query)
72
+ error_msg = IBM_DB.getErrormsg( connection.connection, IBM_DB::DB_CONN )
73
+ if error_msg && !error_msg.empty?
74
+ raise "Statement prepare for updating LOB/XML column failed : #{error_msg}"
75
+ else
76
+ raise StandardError.new('An unexpected error occurred during update of LOB/XML column')
77
+ end
76
78
  end
77
- end
78
- connection.log_query(update_query,'update of LOB/XML field(s)in handle_lobs')
79
+ connection.log_query(update_query,'update of LOB/XML field(s)in handle_lobs')
79
80
 
80
- # rollback any failed LOB/XML field updates (and remove associated marker)
81
- unless IBM_DB.execute(stmt, values)
82
- error_msg = "Failed to insert/update LOB/XML field(s) due to: #{IBM_DB.getErrormsg( stmt, IBM_DB::DB_STMT )}"
83
- connection.execute("ROLLBACK")
84
- raise error_msg
85
- else
86
- IBM_DB.free_result stmt
81
+ # rollback any failed LOB/XML field updates (and remove associated marker)
82
+ unless IBM_DB.execute(stmt, values)
83
+ error_msg = "Failed to insert/update LOB/XML field(s) due to: #{IBM_DB.getErrormsg( stmt, IBM_DB::DB_STMT )}"
84
+ connection.execute("ROLLBACK")
85
+ raise error_msg
86
+ end
87
+ rescue StandardError => error
88
+ raise error
89
+ ensure
90
+ IBM_DB.free_stmt(stmt) if stmt
87
91
  end
88
92
  end # if clob_sql
89
93
  end #connection.sql.each
@@ -103,9 +107,16 @@ module ActiveRecord
103
107
  raise LoadError, "Failed to load IBM_DB Ruby driver."
104
108
  end
105
109
 
110
+ if( config.has_key?(:parameterized) && config[:parameterized] == true )
111
+ require 'active_record/connection_adapters/ibm_db_pstmt'
112
+ end
113
+
106
114
  # Converts all +config+ keys to symbols
107
115
  config = config.symbolize_keys
108
116
 
117
+ # Flag to decide if quoted literal replcement should take place. By default it is ON. Set it to OFF if using Pstmt
118
+ set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_ON
119
+
109
120
  # Retrieves the database alias (local catalog name) or remote name
110
121
  # (for remote TCP/IP connections) from the +config+ hash
111
122
  # or raises ArgumentError in case of failure.
@@ -127,6 +138,10 @@ module ActiveRecord
127
138
  # Providing default schema (username) when not specified
128
139
  config[:schema] = config.has_key?(:schema) ? config[:schema].to_s : config[:username].to_s
129
140
 
141
+ if(config.has_key?(:parameterized) && config[:parameterized] == true )
142
+ set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_OFF
143
+ end
144
+
130
145
  # Extract connection options from the database configuration
131
146
  # (in support to formatting, audit and billing purposes):
132
147
  # Retrieve database objects fields in lowercase
@@ -171,10 +186,10 @@ module ActiveRecord
171
186
  conn_string << "AUTHENTICATION=#{config[:authentication]};" if config.has_key?(:authentication)
172
187
  conn_string << "CONNECTTIMEOUT=#{config[:timeout]};" if config.has_key?(:timeout)
173
188
 
174
- connection = IBM_DB.connect conn_string, '', '', conn_options
189
+ connection = IBM_DB.connect( conn_string, '', '', conn_options, set_quoted_literal_replacement )
175
190
  else
176
191
  # No host implies a local catalog-based connection: +database+ represents catalog alias
177
- connection = IBM_DB.connect( database, username, password, conn_options )
192
+ connection = IBM_DB.connect( database, username, password, conn_options, set_quoted_literal_replacement )
178
193
  end
179
194
  rescue StandardError => connect_err
180
195
  raise "Failed to connect to [#{database}] due to: #{connect_err}"
@@ -399,13 +414,18 @@ module ActiveRecord
399
414
  # // - to connect to the database server. By default value is SERVER
400
415
  # timeout: 10 // Specifies the time in seconds (0 - 32767) to wait for a reply from server -
401
416
  # //- when trying to establish a connection before generating a timeout
417
+ # == Parameterized Queries Support
418
+ # parameterized: false // Specifies if the prepared statement support of
419
+ # //- the IBM_DB Adapter is to be turned on or off
402
420
  #
403
421
  # When schema is not specified, the username value is used instead.
422
+ # The default setting of parameterized is false.
404
423
  #
405
424
  class IBM_DBAdapter < AbstractAdapter
406
425
  attr_reader :connection, :servertype
407
- attr_accessor :sql,:handle_lobs_triggered
426
+ attr_accessor :sql,:handle_lobs_triggered, :sql_parameter_values
408
427
  attr_reader :schema, :app_user, :account, :application, :workstation
428
+ attr_reader :pstmt_support_on, :set_quoted_literal_replacement
409
429
 
410
430
  # Name of the adapter
411
431
  def adapter_name
@@ -427,6 +447,14 @@ module ActiveRecord
427
447
  @security = config[:security] || nil
428
448
  @authentication = config[:authentication] || nil
429
449
  @timeout = config[:timeout] || 0 # default timeout value is 0
450
+
451
+ if( config.has_key?(:parameterized) && config[:parameterized] == true )
452
+ @pstmt_support_on = true
453
+ @set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_OFF
454
+ else
455
+ @pstmt_support_on = false
456
+ @set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_ON
457
+ end
430
458
 
431
459
  # Caching database connection options (auditing and billing support)
432
460
  @app_user = conn_options[:app_user] if conn_options.has_key?(:app_user)
@@ -434,7 +462,9 @@ module ActiveRecord
434
462
  @application = conn_options[:application] if conn_options.has_key?(:application)
435
463
  @workstation = conn_options[:workstation] if conn_options.has_key?(:workstation)
436
464
 
437
- @sql = []
465
+ @sql = []
466
+ @sql_parameter_values = [] #Used only if pstmt support is turned on
467
+
438
468
  @handle_lobs_triggered = false
439
469
 
440
470
  # Calls the parent class +ConnectionAdapters+' initializer
@@ -585,11 +615,11 @@ module ActiveRecord
585
615
  @conn_string << "AUTHENTICATION=#{@authentication};" if @authentication
586
616
  @conn_string << "CONNECTTIMEOUT=#{@timeout};"
587
617
  # Connects and assigns the resulting IBM_DB.Connection to the +@connection+ instance variable
588
- @connection = IBM_DB.connect(@conn_string, '', '', @conn_options)
618
+ @connection = IBM_DB.connect(@conn_string, '', '', @conn_options, @set_quoted_literal_replacement)
589
619
  else
590
620
  # Connects to the database using the local alias (@database)
591
621
  # and assigns the connection object (IBM_DB.Connection) to @connection
592
- @connection = IBM_DB.connect(@database, @username, @password, @conn_options)
622
+ @connection = IBM_DB.connect(@database, @username, @password, @conn_options, @set_quoted_literal_replacement)
593
623
  end
594
624
  rescue StandardError => connect_err
595
625
  warn "Connection to database #{@database} failed: #{connect_err}"
@@ -640,6 +670,73 @@ module ActiveRecord
640
670
  end
641
671
  end
642
672
 
673
+ # Returns an array of hashes with the column names as keys and
674
+ # column values as values. +sql+ is the select query,
675
+ # and +name+ is an optional description for logging
676
+ def prepared_select(sql_param_hash, name = nil)
677
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
678
+
679
+ results = []
680
+ # Invokes the method +prepare+ in order prepare the SQL
681
+ # IBM_DB.Statement is returned from which the statement is executed and results fetched
682
+ pstmt = prepare(sql_param_hash["sqlSegment"], name)
683
+ if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
684
+ begin
685
+ @servertype.select(sql_param_hash["sqlSegment"], name, pstmt, results)
686
+ rescue StandardError => fetch_error # Handle driver fetch errors
687
+ error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT )
688
+ if error_msg && !error_msg.empty?
689
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
690
+ else
691
+ error_msg = "An unexpected error occurred during data retrieval"
692
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
693
+ raise error_msg
694
+ end
695
+ ensure
696
+ # Ensures to free the resources associated with the statement
697
+ IBM_DB.free_stmt(pstmt) if pstmt
698
+ end
699
+ end
700
+ # The array of record hashes is returned
701
+ results
702
+ end
703
+
704
+ # Returns an array of hashes with the column names as keys and
705
+ # column values as values. +sql+ is the select query,
706
+ # and +name+ is an optional description for logging
707
+ def prepared_select_values(sql_param_hash, name = nil)
708
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
709
+
710
+ results = []
711
+ # Invokes the method +prepare+ in order prepare the SQL
712
+ # IBM_DB.Statement is returned from which the statement is executed and results fetched
713
+ pstmt = prepare(sql_param_hash["sqlSegment"], name)
714
+ if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
715
+ begin
716
+ @servertype.select_rows(sql_param_hash["sqlSegment"], name, pstmt, results)
717
+ if results
718
+ return results.map { |v| v[0] }
719
+ else
720
+ nil
721
+ end
722
+ rescue StandardError => fetch_error # Handle driver fetch errors
723
+ error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT )
724
+ if error_msg && !error_msg.empty?
725
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
726
+ else
727
+ error_msg = "An unexpected error occurred during data retrieval"
728
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
729
+ raise error_msg
730
+ end
731
+ ensure
732
+ # Ensures to free the resources associated with the statement
733
+ IBM_DB.free_stmt(pstmt) if pstmt
734
+ end
735
+ end
736
+ # The array of record hashes is returned
737
+ results
738
+ end
739
+
643
740
  # Returns an array of hashes with the column names as keys and
644
741
  # column values as values. +sql+ is the select query,
645
742
  # and +name+ is an optional description for logging
@@ -665,7 +762,7 @@ module ActiveRecord
665
762
  end
666
763
  ensure
667
764
  # Ensures to free the resources associated with the statement
668
- IBM_DB.free_result stmt
765
+ IBM_DB.free_stmt(stmt) if stmt
669
766
  end
670
767
  end
671
768
  # The array of record hashes is returned
@@ -697,7 +794,7 @@ module ActiveRecord
697
794
  end
698
795
  ensure
699
796
  # Ensures to free the resources associated with the statement
700
- IBM_DB.free_result stmt
797
+ IBM_DB.free_stmt(stmt) if stmt
701
798
  end
702
799
  end
703
800
  # The array of record hashes is returned
@@ -765,9 +862,10 @@ module ActiveRecord
765
862
  log(insert_query,'fixture insert') do
766
863
  unless IBM_DB.execute(stmt, insert_values)
767
864
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
865
+ IBM_DB.free_stmt(stmt) if stmt
768
866
  raise "Failed to insert due to: #{error_msg}"
769
867
  else
770
- IBM_DB.free_result stmt
868
+ IBM_DB.free_stmt(stmt) if stmt
771
869
  end
772
870
  end
773
871
  end
@@ -789,8 +887,67 @@ module ActiveRecord
789
887
  return id_value || @servertype.last_generated_id(stmt)
790
888
  # Ensures to free the resources associated with the statement
791
889
  ensure
792
- IBM_DB.free_result(stmt)
890
+ IBM_DB.free_stmt(stmt) if stmt
891
+ end
892
+ end
893
+ end
894
+
895
+ # Praveen
896
+ # Performs an insert using the prepared statement and returns the last ID generated.
897
+ # This can be the ID passed to the method or the one auto-generated by the database,
898
+ # and retrieved by the +last_generated_id+ method.
899
+ def prepared_insert(pstmt, param_array = nil)
900
+ if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
901
+ @sql = []
902
+ @sql_parameter_values = []
903
+ @handle_lobs_triggered = false
904
+ end
905
+
906
+ clear_query_cache if defined? clear_query_cache
907
+
908
+ begin
909
+ if execute_prepared_stmt(pstmt, param_array)
910
+ @sql << @prepared_sql
911
+ @sql_parameter_values << param_array
912
+ return @servertype.last_generated_id(pstmt)
913
+ end
914
+ rescue StandardError => insert_err
915
+ raise insert_err
916
+ ensure
917
+ IBM_DB.free_stmt(pstmt) if pstmt
918
+ end
919
+ end
920
+
921
+ # Praveen
922
+ # Prepares and logs +sql+ commands and
923
+ # returns a +IBM_DB.Statement+ object.
924
+ def prepare(sql,name = nil)
925
+ # The +log+ method is defined in the parent class +AbstractAdapter+
926
+ @prepared_sql = sql
927
+ log(sql,name) do
928
+ @servertype.prepare(sql, name)
929
+ end
930
+ end
931
+
932
+ # Praveen
933
+ #Executes the prepared statement
934
+ #ReturnsTrue on success and False on Failure
935
+ def execute_prepared_stmt(pstmt, param_array = nil)
936
+ if !param_array.nil? && param_array.size < 1
937
+ param_array = nil
938
+ end
939
+
940
+ if( !IBM_DB.execute(pstmt, param_array) )
941
+ error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT)
942
+ if !error_msg.empty?
943
+ error_msg = "Statement execution failed: " + error_msg
944
+ else
945
+ error_msg = "Statement execution failed"
793
946
  end
947
+ IBM_DB.free_stmt(pstmt) if pstmt
948
+ raise StatementInvalid, error_msg
949
+ else
950
+ return true
794
951
  end
795
952
  end
796
953
 
@@ -836,15 +993,40 @@ module ActiveRecord
836
993
  IBM_DB.num_rows(stmt)
837
994
  # Ensures to free the resources associated with the statement
838
995
  ensure
839
- IBM_DB.free_result(stmt)
996
+ IBM_DB.free_stmt(stmt) if stmt
840
997
  end
841
998
  end
842
999
  end
843
1000
 
1001
+ #Praveen
1002
+ def prepared_update(pstmt, param_array = nil )
1003
+ if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1004
+ @sql = []
1005
+ @sql_parameter_values = []
1006
+ @handle_lobs_triggered = false
1007
+ end
1008
+
1009
+ clear_query_cache if defined? clear_query_cache
1010
+
1011
+ begin
1012
+ if execute_prepared_stmt(pstmt, param_array)
1013
+ @sql << @prepared_sql
1014
+ @sql_parameter_values << param_array
1015
+ # Retrieves the number of affected rows
1016
+ IBM_DB.num_rows(pstmt)
1017
+ # Ensures to free the resources associated with the statement
1018
+ end
1019
+ rescue StandardError => updt_err
1020
+ raise updt_err
1021
+ ensure
1022
+ IBM_DB.free_stmt(pstmt) if pstmt
1023
+ end
1024
+ end
844
1025
  # The delete method executes the delete
845
1026
  # statement and returns the number of affected rows.
846
1027
  # The method is an alias for +update+
847
1028
  alias_method :delete, :update
1029
+ alias_method :prepared_delete, :prepared_update
848
1030
 
849
1031
  # Begins the transaction (and turns off auto-committing)
850
1032
  def begin_db_transaction
@@ -892,13 +1074,21 @@ module ActiveRecord
892
1074
  if limit == 0
893
1075
  # Returns a query that will always generate zero records
894
1076
  # (e.g. WHERE sys_row_num BETWEEN 1 and 0)
895
- sql = @servertype.query_offset_limit(sql, 0, limit)
1077
+ if( @pstmt_support_on )
1078
+ sql = @servertype.query_offset_limit!(sql, 0, limit, options)
1079
+ else
1080
+ sql = @servertype.query_offset_limit(sql, 0, limit)
1081
+ end
896
1082
  # If there is a non-zero limit
897
1083
  else
898
1084
  offset = options[:offset]
899
1085
  # If an offset is specified builds the query with offset and limit,
900
1086
  # otherwise retrieves only the first +limit+ rows
901
- sql = @servertype.query_offset_limit(sql, offset, limit)
1087
+ if( @pstmt_support_on )
1088
+ sql = @servertype.query_offset_limit!(sql, offset, limit, options)
1089
+ else
1090
+ sql = @servertype.query_offset_limit(sql, offset, limit)
1091
+ end
902
1092
  end
903
1093
  end
904
1094
  # Returns the sql query in any case
@@ -914,6 +1104,35 @@ module ActiveRecord
914
1104
  # QUOTING
915
1105
  #==============================================
916
1106
 
1107
+ #Praveen
1108
+ def quote_value_for_pstmt(value, column=nil)
1109
+
1110
+ return value.quoted_id if value.respond_to?(:quoted_id)
1111
+
1112
+ case value
1113
+ when String, ActiveSupport::Multibyte::Chars then
1114
+ value = value.to_s
1115
+ if column && [:integer, :float].include?(column.type)
1116
+ value = column.type == :integer ? value.to_i : value.to_f
1117
+ value
1118
+ else
1119
+ value
1120
+ end
1121
+ when NilClass then nil
1122
+ when TrueClass then 1
1123
+ when FalseClass then 0
1124
+ when Float, Fixnum, Bignum then value
1125
+ # BigDecimals need to be output in a non-normalized form and quoted.
1126
+ when BigDecimal then value.to_s('F')
1127
+ else
1128
+ if value.acts_like?(:date) || value.acts_like?(:time)
1129
+ quoted_date(value)
1130
+ else
1131
+ value.to_yaml
1132
+ end
1133
+ end
1134
+ end
1135
+
917
1136
  # Properly quotes the various data types.
918
1137
  # +value+ contains the data, +column+ is optional and contains info on the field
919
1138
  def quote(value, column = nil)
@@ -1080,7 +1299,7 @@ module ActiveRecord
1080
1299
  raise error_msg
1081
1300
  end
1082
1301
  ensure
1083
- IBM_DB.free_result(stmt) # Free resources associated with the statement
1302
+ IBM_DB.free_stmt(stmt) if stmt # Free resources associated with the statement
1084
1303
  end
1085
1304
  else # Handle driver execution errors
1086
1305
  error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
@@ -1115,7 +1334,7 @@ module ActiveRecord
1115
1334
  raise error_msg
1116
1335
  end
1117
1336
  ensure # Free resources associated with the statement
1118
- IBM_DB.free_result(stmt) if stmt
1337
+ IBM_DB.free_stmt(stmt) if stmt
1119
1338
  end
1120
1339
  else
1121
1340
  error_msg = IBM_DB.getErrormsg( @connection, IBM_DB::DB_CONN )
@@ -1171,7 +1390,7 @@ module ActiveRecord
1171
1390
  raise error_msg
1172
1391
  end
1173
1392
  ensure # Free resources associated with the statement
1174
- IBM_DB.free_result(stmt) if stmt
1393
+ IBM_DB.free_stmt(stmt) if stmt
1175
1394
  end
1176
1395
  else # Handle driver execution errors
1177
1396
  error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
@@ -1225,7 +1444,7 @@ module ActiveRecord
1225
1444
  raise error_msg
1226
1445
  end
1227
1446
  ensure # Free resources associated with the statement
1228
- IBM_DB.free_result(stmt) if stmt
1447
+ IBM_DB.free_stmt(stmt) if stmt
1229
1448
  end
1230
1449
  else # Handle driver execution errors
1231
1450
  error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
@@ -1314,7 +1533,7 @@ module ActiveRecord
1314
1533
  raise error_msg
1315
1534
  end
1316
1535
  ensure # Free resources associated with the statement
1317
- IBM_DB.free_result(stmt)
1536
+ IBM_DB.free_stmt(stmt) if stmt
1318
1537
  end
1319
1538
  else # Handle driver execution errors
1320
1539
  error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
@@ -1338,7 +1557,7 @@ module ActiveRecord
1338
1557
  stmt = execute(rename_table_sql)
1339
1558
  # Ensures to free the resources associated with the statement
1340
1559
  ensure
1341
- IBM_DB.free_result stmt if stmt
1560
+ IBM_DB.free_stmt(stmt) if stmt
1342
1561
  end
1343
1562
 
1344
1563
  # Renames a column.
@@ -1482,8 +1701,6 @@ To remove the column, the table must be dropped and recreated without the #{colu
1482
1701
  error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1483
1702
  raise error_msg
1484
1703
  end
1485
- ensure # Free resources associated with the statement
1486
- IBM_DB.free_result(stmt)
1487
1704
  end
1488
1705
  end
1489
1706
 
@@ -1505,8 +1722,24 @@ To remove the column, the table must be dropped and recreated without the #{colu
1505
1722
  error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1506
1723
  raise error_msg
1507
1724
  end
1508
- ensure # Free resources associated with the statement
1509
- IBM_DB.free_result(stmt)
1725
+ end
1726
+ end
1727
+
1728
+ # Praveen
1729
+ def prepare(sql,name = nil)
1730
+ begin
1731
+ stmt = IBM_DB.prepare(@adapter.connection, sql)
1732
+ if( stmt )
1733
+ stmt
1734
+ else
1735
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
1736
+ end
1737
+ rescue StandardError => prep_err
1738
+ if prep_err && !prep_err.message.empty?
1739
+ raise "Failed to prepare sql #{sql} due to: #{prep_err}"
1740
+ else
1741
+ raise
1742
+ end
1510
1743
  end
1511
1744
  end
1512
1745
 
@@ -1532,6 +1765,9 @@ To remove the column, the table must be dropped and recreated without the #{colu
1532
1765
 
1533
1766
  def query_offset_limit(sql, offset, limit)
1534
1767
  end
1768
+
1769
+ def query_offset_limit!(sql, offset, limit, options)
1770
+ end
1535
1771
 
1536
1772
  def get_datetime_mapping
1537
1773
  end
@@ -1580,24 +1816,35 @@ To remove the column, the table must be dropped and recreated without the #{colu
1580
1816
  def last_generated_id(stmt)
1581
1817
  # Queries the db to obtain the last ID that was automatically generated
1582
1818
  sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
1583
- stmt = IBM_DB.exec(@adapter.connection, sql)
1819
+ stmt = IBM_DB.prepare(@adapter.connection, sql)
1584
1820
  if(stmt)
1585
- begin
1586
- # Fetches the only record available (containing the last id)
1587
- IBM_DB.fetch_row(stmt)
1588
- # Retrieves and returns the result of the query with the last id.
1589
- IBM_DB.result(stmt,0)
1590
- rescue StandardError => fetch_error # Handle driver fetch errors
1821
+ if(IBM_DB.execute(stmt, nil))
1822
+ begin
1823
+ # Fetches the only record available (containing the last id)
1824
+ IBM_DB.fetch_row(stmt)
1825
+ # Retrieves and returns the result of the query with the last id.
1826
+ IBM_DB.result(stmt,0)
1827
+ rescue StandardError => fetch_error # Handle driver fetch errors
1828
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1829
+ if error_msg && !error_msg.empty?
1830
+ raise "Failed to retrieve last generated id: #{error_msg}"
1831
+ else
1832
+ error_msg = "An unexpected error occurred during retrieval of last generated id"
1833
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1834
+ raise error_msg
1835
+ end
1836
+ ensure # Free resources associated with the statement
1837
+ IBM_DB.free_stmt(stmt) if stmt
1838
+ end
1839
+ else
1591
1840
  error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1841
+ IBM_DB.free_stmt(stmt) if stmt
1592
1842
  if error_msg && !error_msg.empty?
1593
1843
  raise "Failed to retrieve last generated id: #{error_msg}"
1594
1844
  else
1595
1845
  error_msg = "An unexpected error occurred during retrieval of last generated id"
1596
- error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1597
1846
  raise error_msg
1598
1847
  end
1599
- ensure # Free resources associated with the statement
1600
- IBM_DB.free_result(stmt) if stmt
1601
1848
  end
1602
1849
  else
1603
1850
  error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
@@ -1640,7 +1887,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1640
1887
  stmt = execute(change_column_sql)
1641
1888
  reorg_table(table_name)
1642
1889
  ensure
1643
- IBM_DB.free_result stmt if stmt
1890
+ IBM_DB.free_stmt(stmt) if stmt
1644
1891
  end
1645
1892
 
1646
1893
  #DB2 specific ALTER TABLE statement to change the nullability of a column
@@ -1659,7 +1906,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1659
1906
  stmt = execute(change_column_sql)
1660
1907
  reorg_table(table_name)
1661
1908
  ensure
1662
- IBM_DB.free_result stmt if stmt
1909
+ IBM_DB.free_stmt(stmt) if stmt
1663
1910
  end
1664
1911
 
1665
1912
  # This method returns the DB2 SQL type corresponding to the Rails
@@ -1819,11 +2066,39 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1819
2066
  end
1820
2067
  end
1821
2068
 
2069
+ # Praveen
2070
+ def prepare(sql,name = nil)
2071
+ # Check if there is a limit and/or an offset
2072
+ # If so then make sure and use a static cursor type
2073
+ begin
2074
+ if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
2075
+ # Set the cursor type to static so we can later utilize the offset and limit correctly
2076
+ if stmt = IBM_DB.prepare(@adapter.connection, sql,
2077
+ {IBM_DB::SQL_ATTR_CURSOR_TYPE => IBM_DB::SQL_CURSOR_STATIC})
2078
+ stmt # Return the statement object
2079
+ else
2080
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2081
+ end
2082
+ else
2083
+ if stmt = IBM_DB.prepare(@adapter.connection, sql)
2084
+ stmt # Return the statement object
2085
+ else
2086
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2087
+ end
2088
+ end
2089
+ rescue StandardError => prep_err
2090
+ error_msg = "Failed to prepare sql #{sql}"
2091
+ error_msg = error_msg + ": #{prep_err.message}" if !prep_err.message.empty?
2092
+ raise error_msg
2093
+ end
2094
+ end
2095
+
2096
+ # Praveen
1822
2097
  def execute(sql, name = nil)
1823
2098
  # Check if there is a limit and/or an offset
1824
2099
  # If so then make sure and use a static cursor type
1825
- if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
1826
- begin
2100
+ begin
2101
+ if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
1827
2102
  # Set the cursor type to static so we can later utilize the offset and limit correctly
1828
2103
  if stmt = IBM_DB.exec(@adapter.connection, sql,
1829
2104
  {IBM_DB::SQL_ATTR_CURSOR_TYPE => IBM_DB::SQL_CURSOR_STATIC})
@@ -1831,23 +2106,17 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1831
2106
  else
1832
2107
  raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
1833
2108
  end
1834
- rescue StandardError => exec_err
1835
- error_msg = "Failed to execute statement due to"
1836
- error_msg = error_msg + ": #{exec_err.message}" if !exec_err.message.empty?
1837
- raise error_msg
1838
- end
1839
- else
1840
- begin
2109
+ else
1841
2110
  if stmt = IBM_DB.exec(@adapter.connection, sql)
1842
2111
  stmt # Return the statement object
1843
2112
  else
1844
2113
  raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
1845
2114
  end
1846
- rescue StandardError => exec_err
1847
- error_msg = "Failed to execute statement due to"
1848
- error_msg = error_msg + ": #{exec_err.message}" if !exec_err.message.empty?
1849
- raise error_msg
1850
2115
  end
2116
+ rescue StandardError => exec_err
2117
+ error_msg = "Failed to execute statement"
2118
+ error_msg = error_msg + ": #{exec_err.message}" if !exec_err.message.empty?
2119
+ raise error_msg
1851
2120
  end
1852
2121
  end
1853
2122
 
@@ -1859,6 +2128,15 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1859
2128
  end
1860
2129
  end
1861
2130
 
2131
+ def query_offset_limit!(sql, offset, limit, options)
2132
+ @limit = limit
2133
+ @offset = offset
2134
+ if (offset.nil?)
2135
+ sql << " FETCH FIRST #{limit} ROWS ONLY"
2136
+ end
2137
+ options[:paramArray] = []
2138
+ end
2139
+
1862
2140
  # This method generates the default blob value specified for
1863
2141
  # DB2 Dataservers
1864
2142
  def set_binary_default(value)
@@ -1904,6 +2182,24 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1904
2182
  # and retrieve only a LIMIT number of records starting from the OFFSET+1
1905
2183
  sql << ") AS I) AS O WHERE sys_row_num BETWEEN #{offset+1} AND #{last_record}"
1906
2184
  end
2185
+
2186
+ def query_offset_limit!(sql, offset, limit, options)
2187
+ if (offset.nil?)
2188
+ options[:paramArray] = []
2189
+ return sql << " FETCH FIRST #{limit} ROWS ONLY"
2190
+ end
2191
+ # Defines what will be the last record
2192
+ last_record = offset + limit
2193
+ # Transforms the SELECT query in order to retrieve/fetch only
2194
+ # a number of records after the specified offset.
2195
+ # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2196
+ # to select with the condition of this column being between offset+1 and the offset+limit
2197
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2198
+ # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2199
+ # and retrieve only a LIMIT number of records starting from the OFFSET+1
2200
+ sql << ") AS I) AS O WHERE sys_row_num BETWEEN ? AND ?"
2201
+ options[:paramArray] = [offset+1, last_record]
2202
+ end
1907
2203
  end # class IBM_DB2_LUW
1908
2204
 
1909
2205
  class IBM_DB2_LUW_COBRA < IBM_DB2_LUW
@@ -1945,7 +2241,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1945
2241
  reorg_table(_table_name)
1946
2242
 
1947
2243
  ensure
1948
- IBM_DB.free_stmt stmt if stmt
2244
+ IBM_DB.free_stmt(stmt) if stmt
1949
2245
  end #End of begin
1950
2246
  end # End of rename_column
1951
2247
  end #IBM_DB2_LUW_COBRA
@@ -2010,7 +2306,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2010
2306
  reorg_table(_table_name)
2011
2307
 
2012
2308
  ensure
2013
- IBM_DB.free_stmt stmt if stmt
2309
+ IBM_DB.free_stmt(stmt) if stmt
2014
2310
  end #End of begin
2015
2311
  end # End of rename_column
2016
2312
 
@@ -2097,7 +2393,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2097
2393
  reorg_table(_table_name)
2098
2394
 
2099
2395
  ensure
2100
- IBM_DB.free_stmt stmt if stmt
2396
+ IBM_DB.free_stmt(stmt) if stmt
2101
2397
  end #End of begin
2102
2398
  end # End of rename_column
2103
2399
 
@@ -2136,7 +2432,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2136
2432
  reorg_table(table_name)
2137
2433
  # Ensures to free the resources associated with the statement
2138
2434
  ensure
2139
- IBM_DB.free_result stmt if stmt
2435
+ IBM_DB.free_stmt(stmt) if stmt
2140
2436
  end
2141
2437
 
2142
2438
  # IDS specific ALTER TABLE statement to change the nullability of a column
@@ -2160,7 +2456,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2160
2456
  stmt = execute(change_column_sql)
2161
2457
  reorg_table(table_name)
2162
2458
  ensure
2163
- IBM_DB.free_result stmt if stmt
2459
+ IBM_DB.free_stmt(stmt) if stmt
2164
2460
  end
2165
2461
 
2166
2462
  # Reorganizes the table for column changes
@@ -2202,6 +2498,23 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
2202
2498
  end
2203
2499
  end
2204
2500
 
2501
+ # Handling offset/limit as per Informix requirements
2502
+ def query_offset_limit!(sql, offset, limit, options)
2503
+ if limit != 0
2504
+ if !offset.nil?
2505
+ # Modifying the SQL to utilize the skip and limit amounts
2506
+ sql.gsub!(/SELECT/i,"SELECT SKIP #{offset} LIMIT #{limit}")
2507
+ else
2508
+ # Modifying the SQL to retrieve only the first #{limit} rows
2509
+ sql = sql.gsub!("SELECT","SELECT FIRST #{limit}")
2510
+ end
2511
+ else
2512
+ # Modifying the SQL to ensure that no rows will be returned
2513
+ sql.gsub!(/SELECT/i,"SELECT * FROM (SELECT")
2514
+ sql << ") WHERE 0 = 1"
2515
+ end
2516
+ end
2517
+
2205
2518
  # Method that returns the last automatically generated ID
2206
2519
  # on the given +@connection+. This method is required by the +insert+
2207
2520
  # method. IDS returns the last generated serial value in the SQLCA unlike