ibm_db 1.5.0-mswin32 → 2.0.0-mswin32

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.
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