ibm_db 0.7.5 → 0.8.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.
data/ext/ibm_db.c CHANGED
@@ -10,7 +10,7 @@
10
10
  +----------------------------------------------------------------------+
11
11
  */
12
12
 
13
- #define MODULE_RELEASE "0.6.0"
13
+ #define MODULE_RELEASE "0.7.0"
14
14
 
15
15
  #ifdef HAVE_CONFIG_H
16
16
  #include "config.h"
@@ -30,7 +30,7 @@ static void _ruby_ibm_db_assign_options( void* handle, int type, long opt_key, V
30
30
  static void _ruby_ibm_db_clear_conn_err_cache();
31
31
  static void _ruby_ibm_db_clear_stmt_err_cache();
32
32
  static char * _ruby_ibm_db_instance_name;
33
- static int is_systemi; /* 1 == TRUE; 0 == FALSE; */
33
+ static int is_systemi, is_informix; /* 1 == TRUE; 0 == FALSE; */
34
34
 
35
35
  /* Defines a linked list structure for caching param data */
36
36
  typedef struct _param_cache_node {
@@ -187,6 +187,7 @@ void Init_ibm_db(void) {
187
187
  rb_define_module_function(mDB, "setoption", ibm_db_set_option, -1);
188
188
  rb_define_module_function(mDB, "get_option", ibm_db_get_option, -1);
189
189
  rb_define_module_function(mDB, "getoption", ibm_db_get_option, -1);
190
+ rb_define_module_function(mDB, "get_last_serial_value", ibm_db_get_last_serial_value, -1);
190
191
  rb_define_module_function(mDB, "fetch_object", ibm_db_fetch_object, -1);
191
192
  rb_define_module_function(mDB, "server_info", ibm_db_server_info, -1);
192
193
  rb_define_module_function(mDB, "client_info", ibm_db_client_info, -1);
@@ -244,6 +245,7 @@ void Init_ibm_db(void) {
244
245
  RUBY_FALIAS(setoption, set_option)
245
246
  RUBY_FE(get_option)
246
247
  RUBY_FALIAS(getoption, get_option)
248
+ RUBY_FE(get_last_serial_value)
247
249
  RUBY_FE(fetch_object)
248
250
  RUBY_FE(server_info)
249
251
  RUBY_FE(client_info)
@@ -1081,7 +1083,7 @@ static VALUE _ruby_ibm_db_connect_helper( int argc, VALUE *argv, int isPersisten
1081
1083
  do {
1082
1084
  /* Check if we already have a connection for this userID & database combination */
1083
1085
  if (isPersistent) {
1084
- hKeyLen = strlen(database) + strlen(uid) + strlen(password) + 9;
1086
+ hKeyLen = strlen(database) + strlen(uid) + strlen(password) + 12;
1085
1087
  hKey = ALLOC_N(char, hKeyLen);
1086
1088
 
1087
1089
  sprintf(hKey, "__ibm_db_%s.%s.%s", uid, database, password);
@@ -1154,19 +1156,6 @@ static VALUE _ruby_ibm_db_connect_helper( int argc, VALUE *argv, int isPersisten
1154
1156
  }
1155
1157
  #endif
1156
1158
 
1157
- /* Set SQL_ATTR_REPLACE_QUOTED_LITERALS connection attribute to
1158
- * enable CLI numeric literal feature. This is equivalent to
1159
- * PATCH2=71 in the db2cli.ini file
1160
- */
1161
- #ifndef PASE
1162
- rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS, (SQLPOINTER)(enable_numeric_literals), SQL_IS_INTEGER);
1163
- #else
1164
- rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS, (SQLPOINTER)(&enable_numeric_literals), SQL_IS_INTEGER);
1165
- #endif
1166
- if (rc != SQL_SUCCESS) {
1167
- _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1);
1168
- }
1169
-
1170
1159
  conn_res->c_bin_mode = IBM_DB_G(bin_mode);
1171
1160
  conn_res->c_case_mode = CASE_NATURAL;
1172
1161
  conn_res->c_cursor_type = SQL_SCROLL_FORWARD_ONLY;
@@ -1208,6 +1197,26 @@ static VALUE _ruby_ibm_db_connect_helper( int argc, VALUE *argv, int isPersisten
1208
1197
  memset(server, 0, sizeof(server));
1209
1198
  rc = SQLGetInfo(conn_res->hdbc, SQL_DBMS_NAME, (SQLPOINTER)server, 2048, NULL);
1210
1199
  if (!strcmp(server, "AS")) is_systemi = 1;
1200
+ if (!strncmp(server, "IDS", 3)) is_informix = 1;
1201
+
1202
+ /* Set SQL_ATTR_REPLACE_QUOTED_LITERALS connection attribute to
1203
+ * enable CLI numeric literal feature. This is equivalent to
1204
+ * PATCH2=71 in the db2cli.ini file
1205
+ */
1206
+ #ifndef PASE
1207
+ /* Only enable this feature if we are not connected to an Informix data server */
1208
+ if (!is_informix) {
1209
+ rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS, (SQLPOINTER)(enable_numeric_literals), SQL_IS_INTEGER);
1210
+ }
1211
+ #else
1212
+ /* Only enable this feature if we are not connected to an Informix data server */
1213
+ if (!is_informix) {
1214
+ rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS, (SQLPOINTER)(&enable_numeric_literals), SQL_IS_INTEGER);
1215
+ }
1216
+ #endif
1217
+ if (rc != SQL_SUCCESS) {
1218
+ _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1);
1219
+ }
1211
1220
  }
1212
1221
  conn_res->handle_active = 1;
1213
1222
  } while (0);
@@ -3257,7 +3266,7 @@ static int _ruby_ibm_db_bind_data( stmt_handle *stmt_res, param_node *curr, VALU
3257
3266
  if (curr->param_type == SQL_PARAM_OUTPUT || curr->param_type == SQL_PARAM_INPUT_OUTPUT) {
3258
3267
  curr->bind_indicator = curr->ivalue;
3259
3268
  valueType = SQL_C_CHAR;
3260
- paramValuePtr = (SQLPOINTER)curr;
3269
+ paramValuePtr = (SQLPOINTER)curr->svalue;
3261
3270
  } else {
3262
3271
  curr->bind_indicator = SQL_DATA_AT_EXEC;
3263
3272
  valueType = SQL_C_CHAR;
@@ -6292,6 +6301,55 @@ VALUE ibm_db_get_option(int argc, VALUE *argv, VALUE self)
6292
6301
  }
6293
6302
  }
6294
6303
 
6304
+
6305
+ /*
6306
+ * IBM_DB::get_last_serial_value -- Gets the last inserted serial value from IDS
6307
+ *
6308
+ * ===Description
6309
+ * string IBM_DB::get_last_serial_value ( resource stmt )
6310
+ *
6311
+ * Returns a string, that is the last inserted value for a serial column for IDS.
6312
+ * The last inserted value could be auto-generated or entered explicitly by the user
6313
+ * This function is valid for IDS (Informix Dynamic Server only)
6314
+ *
6315
+ * ===Parameters
6316
+ *
6317
+ * stmt
6318
+ * A valid statement resource.
6319
+ *
6320
+ * ===Return Values
6321
+ *
6322
+ * Returns a string representation of last inserted serial value on a successful call.
6323
+ * Returns FALSE on failure.
6324
+ */
6325
+ VALUE ibm_db_get_last_serial_value(int argc, VALUE *argv, VALUE self)
6326
+ {
6327
+ VALUE stmt = Qnil;
6328
+ SQLCHAR *value = NULL;
6329
+ stmt_handle *stmt_res;
6330
+ int rc = 0;
6331
+
6332
+ rb_scan_args(argc, argv, "1", &stmt);
6333
+
6334
+ if (!NIL_P(stmt)) {
6335
+ Data_Get_Struct(stmt, stmt_handle, stmt_res);
6336
+
6337
+ /* We allocate a buffer of size 31 as per recommendations from the CLI IDS team */
6338
+ value = ALLOC_N(char, 31);
6339
+ rc = SQLGetStmtAttr((SQLHSTMT)stmt_res->hstmt, SQL_ATTR_GET_GENERATED_VALUE, (SQLPOINTER)value, 31, NULL);
6340
+ if ( rc == SQL_ERROR ) {
6341
+ _ruby_ibm_db_check_sql_errors( (SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1);
6342
+ return Qfalse;
6343
+ }
6344
+ return INT2NUM(atoi(value));
6345
+ }
6346
+ else {
6347
+ rb_throw("Supplied statement handle is invalid", Qnil);
6348
+ return Qfalse;
6349
+ }
6350
+ }
6351
+
6352
+
6295
6353
  /*
6296
6354
  * Local variables:
6297
6355
  * tab-width: 4
data/ext/ruby_ibm_db.h CHANGED
@@ -25,6 +25,14 @@
25
25
  #define SQL_ATTR_REPLACE_QUOTED_LITERALS 116
26
26
  #endif
27
27
 
28
+ /* If using a DB2 CLI version which doesn't support this functionality, explicitly
29
+ * define this. We will rely on DB2 CLI to throw an error when SQLGetStmtAttr is
30
+ * called.
31
+ */
32
+ #ifndef SQL_ATTR_GET_GENERATED_VALUE
33
+ #define SQL_ATTR_GET_GENERATED_VALUE 2578
34
+ #endif
35
+
28
36
  #ifdef _WIN32
29
37
  #define RUBY_IBM_DB_API __declspec(dllexport)
30
38
  #else
@@ -125,6 +133,7 @@ VALUE ibm_db_free_result(int argc, VALUE *argv, VALUE self);
125
133
  VALUE ibm_db_set_option(int argc, VALUE *argv, VALUE self);
126
134
  VALUE ibm_db_setoption(int argc, VALUE *argv, VALUE self);
127
135
  VALUE ibm_db_get_option(int argc, VALUE *argv, VALUE self);
136
+ VALUE ibm_db_get_last_serial_value(int argc, VALUE *argv, VALUE self);
128
137
  VALUE ibm_db_getoption(int argc, VALUE *argv, VALUE self);
129
138
  VALUE ibm_db_fetch_object(int argc, VALUE *argv, VALUE self);
130
139
  VALUE ibm_db_server_info(int argc, VALUE *argv, VALUE self);
@@ -20,7 +20,8 @@ module ActiveRecord
20
20
  # Checks that the insert or update had at least a BLOB, CLOB or XML field
21
21
  if connection.sql =~ /BLOB\('(.*)'\)/i ||
22
22
  connection.sql =~ /@@@IBMTEXT@@@/i ||
23
- connection.sql =~ /@@@IBMXML@@@/i
23
+ connection.sql =~ /@@@IBMXML@@@/i ||
24
+ connection.sql =~ /@@@IBMBINARY@@@/i
24
25
  update_query = "UPDATE #{self.class.table_name} SET ("
25
26
  counter = 0
26
27
  values = []
@@ -208,6 +209,8 @@ requires credentials: username and password"
208
209
  :string
209
210
  when /boolean/i
210
211
  :boolean
212
+ when /serial/i
213
+ :serial
211
214
  when /rowid/i # rowid is a supported datatype on z/OS and i/5
212
215
  :rowid
213
216
  end
@@ -277,7 +280,7 @@ requires credentials: username and password"
277
280
  # When schema is not specified, the username value is used instead.
278
281
  #
279
282
  class IBM_DBAdapter < AbstractAdapter
280
- attr_reader :connection
283
+ attr_reader :connection, :servertype
281
284
  attr_accessor :sql
282
285
  attr_reader :schema, :app_user, :account, :application, :workstation
283
286
 
@@ -325,8 +328,11 @@ requires credentials: username and password"
325
328
  end
326
329
  when /AS/i # DB2 for i5 (iSeries)
327
330
  @servertype = IBM_DB2_I5.new(self)
331
+ when /IDS/i # Informix Dynamic Server
332
+ @servertype = IBM_IDS.new(self)
328
333
  end
329
334
  end
335
+
330
336
  # Executes the +set schema+ statement using the schema identifier provided
331
337
  @servertype.set_schema(@schema) if @schema && @schema != @username
332
338
  end
@@ -495,7 +501,7 @@ requires credentials: username and password"
495
501
  if stmt = execute(sql, name)
496
502
  begin
497
503
  @sql = sql
498
- return id_value || @servertype.last_generated_id
504
+ return id_value || @servertype.last_generated_id(stmt)
499
505
  # Ensures to free the resources associated with the statement
500
506
  ensure
501
507
  IBM_DB::free_result(stmt)
@@ -586,27 +592,19 @@ requires credentials: username and password"
586
592
  if limit == 0
587
593
  # Returns a query that will always generate zero records
588
594
  # (e.g. WHERE sys_row_num BETWEEN 1 and 0)
589
- sql = query_offset_limit(sql, 0, limit)
595
+ sql = @servertype.query_offset_limit(sql, 0, limit)
590
596
  # If there is a non-zero limit
591
597
  else
592
598
  offset = options[:offset]
593
599
  # If an offset is specified builds the query with offset and limit,
594
600
  # otherwise retrieves only the first +limit+ rows
595
- offset ? sql = query_offset_limit(sql, offset, limit) \
596
- : sql << " FETCH FIRST #{limit} ROWS ONLY"
601
+ sql = @servertype.query_offset_limit(sql, offset, limit)
597
602
  end
598
603
  end
599
604
  # Returns the sql query in any case
600
605
  sql
601
606
  end # method add_limit_offset!
602
607
 
603
- # Private method used by +add_limit_offset!+ to create a
604
- # sql query given an offset and a limit
605
- def query_offset_limit(sql, offset, limit)
606
- @servertype.query_offset_limit(sql, offset, limit)
607
- end
608
- private :query_offset_limit
609
-
610
608
  def default_sequence_name(table, column) # :nodoc:
611
609
  "#{table}_#{column}_seq"
612
610
  end
@@ -632,30 +630,30 @@ requires credentials: username and password"
632
630
  value.to_s
633
631
  end
634
632
  when String, ActiveSupport::Multibyte::Chars
635
- if column && column.type == :binary
636
- # If quoting is required for the insert/update of a BLOB
633
+ if column && column.type == :binary
634
+ # If quoting is required for the insert/update of a BLOB
635
+ unless caller[0] =~ /add_column_options/i
636
+ # Invokes a convertion from string to binary
637
+ @servertype.set_binary_value
638
+ else
639
+ # Quoting required for the default value of a column
640
+ @servertype.set_binary_default(value)
641
+ end
642
+ elsif column && column.type == :text
637
643
  unless caller[0] =~ /add_column_options/i
638
- # Invokes a convertion from string to binary
639
- "BLOB('?')"
644
+ "'@@@IBMTEXT@@@'"
640
645
  else
641
- # Quoting required for the default value of a column
642
- @servertype.set_blob_default(value)
646
+ @servertype.set_text_default(value)
643
647
  end
644
- elsif column && column.type == :text
645
- unless caller[0] =~ /add_column_options/i
646
- "'@@@IBMTEXT@@@'"
647
- else
648
- "#{value}"
649
- end
650
- elsif column && column.type == :xml
651
- unless caller[0] =~ /add_column_options/i
652
- "'<ibm>@@@IBMXML@@@</ibm>'"
653
- else
654
- "#{value}"
655
- end
656
- else
657
- "'#{quote_string(value)}'"
658
- end
648
+ elsif column && column.type == :xml
649
+ unless caller[0] =~ /add_column_options/i
650
+ "'<ibm>@@@IBMXML@@@</ibm>'"
651
+ else
652
+ "#{value}"
653
+ end
654
+ else
655
+ "'#{quote_string(value)}'"
656
+ end
659
657
  when TrueClass then quoted_true # return '1' for true
660
658
  when FalseClass then quoted_false # return '0' for false
661
659
  when NilClass
@@ -701,9 +699,9 @@ requires credentials: username and password"
701
699
  :text => { :name => "clob" },
702
700
  :integer => { :name => "integer" },
703
701
  :float => { :name => "float" },
704
- :datetime => { :name => "timestamp" },
705
- :timestamp => { :name => "timestamp" },
706
- :time => { :name => "time" },
702
+ :datetime => { :name => @servertype.get_datetime_mapping },
703
+ :timestamp => { :name => @servertype.get_datetime_mapping },
704
+ :time => { :name => @servertype.get_time_mapping },
707
705
  :date => { :name => "date" },
708
706
  :binary => { :name => "blob" },
709
707
 
@@ -713,7 +711,8 @@ requires credentials: username and password"
713
711
  :boolean => { :name => "smallint"},
714
712
  :xml => { :name => "xml"},
715
713
  :decimal => { :name => "decimal" },
716
- :rowid => { :name => "rowid" } # rowid is a supported datatype on z/OS and i/5
714
+ :rowid => { :name => "rowid" }, # rowid is a supported datatype on z/OS and i/5
715
+ :serial => { :name => "serial" } # rowid is a supported datatype on Informix Dynamic Server
717
716
  }
718
717
  end
719
718
 
@@ -743,7 +742,9 @@ requires credentials: username and password"
743
742
  # Initializes the tables array
744
743
  tables = []
745
744
  # Returns a IBM_DB::Statement used to retrieve the tables
746
- if stmt = IBM_DB::tables(@connection, nil, @schema.upcase)
745
+ if stmt = IBM_DB::tables(@connection,
746
+ nil,
747
+ @servertype.set_case(@schema))
747
748
  # Fetches all the records available
748
749
  while tab = IBM_DB::fetch_assoc(stmt)
749
750
  # Adds the lowercase table name to the array
@@ -769,7 +770,11 @@ requires credentials: username and password"
769
770
  # "NON_UNIQUE: #{index_stats[3]}"
770
771
  # "INDEX_NAME: #{index_stats[5]}"
771
772
  # "COLUMN_NAME: #{index_stats[8]}"
772
- stmt = IBM_DB::statistics( @connection, nil, @schema.upcase, table_name.upcase, 1)
773
+ stmt = IBM_DB::statistics( @connection,
774
+ nil,
775
+ @servertype.set_case(@schema),
776
+ @servertype.set_case(table_name),
777
+ 1)
773
778
  while ( index_stats = IBM_DB::fetch_array(stmt) )
774
779
  if index_stats[5] # INDEX_NAME
775
780
  index_name = index_stats[5].downcase
@@ -792,14 +797,17 @@ requires credentials: username and password"
792
797
  # Returns an array of Column objects for the table specified by +table_name+
793
798
  def columns(table_name, name = nil)
794
799
  # to_s required because it may be a symbol.
795
- table_name = table_name.to_s.upcase
800
+ table_name = @servertype.set_case(table_name.to_s)
796
801
  # Checks if a blank table name has been given.
797
802
  # If so it returns an empty array
798
803
  return [] if table_name.strip.empty?
799
804
  # +columns+ will contain the resulting array
800
805
  columns = []
801
806
  # Statement required to access all the columns information
802
- if stmt = IBM_DB::columns(@connection, nil, @schema.upcase, table_name.upcase)
807
+ if stmt = IBM_DB::columns(@connection,
808
+ nil,
809
+ @servertype.set_case(@schema),
810
+ @servertype.set_case(table_name))
803
811
  begin
804
812
  # Fetches all the columns and assigns them to col.
805
813
  # +col+ is an hash with keys/value pairs for a column
@@ -974,13 +982,13 @@ requires credentials: username and password"
974
982
 
975
983
  end # class IBM_DBAdapter
976
984
 
977
- # This class contains common code across DB's (DB2 LUW, zOS and i5)
985
+ # This class contains common code across DB's (DB2 LUW, zOS, i5 and IDS)
978
986
  class IBM_DataServer
979
987
  def initialize(adapter)
980
988
  @adapter = adapter
981
989
  end
982
990
 
983
- def last_generated_id
991
+ def last_generated_id(stmt)
984
992
  end
985
993
 
986
994
  def create_index_after_table (table_name)
@@ -1014,18 +1022,59 @@ To remove the column, the table must be dropped and recreated without the #{colu
1014
1022
  end
1015
1023
  end
1016
1024
 
1017
- def set_schema(schema)
1018
- @adapter.execute("SET SCHEMA #{schema}")
1025
+ def select_all(sql, name, stmt, results)
1026
+ # Fetches all the results available. IBM_DB::fetch_assoc(stmt) returns
1027
+ # an hash for each single record.
1028
+ # The loop stops when there aren't any more valid records to fetch
1029
+ while single_hash = IBM_DB::fetch_assoc(stmt)
1030
+ # Add the record to the +results+ array
1031
+ results << single_hash
1032
+ end
1019
1033
  end
1020
1034
 
1021
- def select_all(sql, name, stmt, results)
1035
+ def execute(sql, name = nil)
1036
+ begin
1037
+ if stmt = IBM_DB::exec(@adapter.connection, sql)
1038
+ stmt # Return the statement object
1039
+ else
1040
+ raise StatementInvalid, IBM_DB::stmt_errormsg
1041
+ end
1042
+ rescue StandardError
1043
+ error_msg = IBM_DB::conn_errormsg ? IBM_DB::conn_errormsg : IBM_DB::stmt_errormsg
1044
+ if error_msg && !error_msg.empty?
1045
+ raise "Failed to execute statement due to error: #{error_msg}"
1046
+ else
1047
+ raise
1048
+ end
1049
+ end
1050
+ end
1051
+
1052
+ def set_schema(schema)
1053
+ @adapter.execute("SET SCHEMA #{schema}")
1022
1054
  end
1023
1055
 
1024
1056
  def query_offset_limit(sql, offset, limit)
1025
1057
  end
1026
1058
 
1027
- def set_blob_default(value)
1028
- "BLOB('#{value}')"
1059
+ def get_datetime_mapping
1060
+ end
1061
+
1062
+ def get_time_mapping
1063
+ end
1064
+
1065
+ def change_column_default(table_name, column_name, default)
1066
+ end
1067
+
1068
+ def set_binary_default(value)
1069
+ end
1070
+
1071
+ def set_binary_value
1072
+ end
1073
+
1074
+ def set_text_default
1075
+ end
1076
+
1077
+ def set_case(value)
1029
1078
  end
1030
1079
  end # class IBM_DataServer
1031
1080
 
@@ -1040,7 +1089,8 @@ To remove the column, the table must be dropped and recreated without the #{colu
1040
1089
 
1041
1090
  # Returns the last automatically generated ID.
1042
1091
  # This method is required by the +insert+ method
1043
- def last_generated_id
1092
+ # The "stmt" parameter is ignored for DB2 but used for IDS
1093
+ def last_generated_id(stmt)
1044
1094
  # Queries the db to obtain the last ID that was automatically generated
1045
1095
  sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
1046
1096
  stmt = IBM_DB::exec(@adapter.connection, sql)
@@ -1069,6 +1119,7 @@ The column datatype change to [#{data_type}] is not supported by this data serve
1069
1119
  end
1070
1120
  end
1071
1121
 
1122
+ # DB2 specific ALTER TABLE statement to add a default clause
1072
1123
  def change_column_default(table_name, column_name, default)
1073
1124
  # SQL statement which alters column's default value
1074
1125
  change_column_sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} \
@@ -1078,6 +1129,18 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1078
1129
  IBM_DB::free_result stmt if stmt
1079
1130
  end
1080
1131
 
1132
+ # This method returns the DB2 SQL type corresponding to the Rails
1133
+ # datetime/timestamp type
1134
+ def get_datetime_mapping
1135
+ return "timestamp"
1136
+ end
1137
+
1138
+ # This method returns the DB2 SQL type corresponding to the Rails
1139
+ # time type
1140
+ def get_time_mapping
1141
+ return "time"
1142
+ end
1143
+
1081
1144
  # Fetches all the results available. IBM_DB::fetch_assoc(stmt) returns
1082
1145
  # an hash for each single record.
1083
1146
  # The loop stops when there aren't any more valid records to fetch
@@ -1086,6 +1149,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1086
1149
  # We know at this point that there is an offset and/or a limit
1087
1150
  # Check if the cursor type is set correctly
1088
1151
  cursor_type = IBM_DB::get_option stmt, IBM_DB::SQL_ATTR_CURSOR_TYPE, 0
1152
+ @offset = 0 if @offset.nil?
1089
1153
  if (cursor_type == IBM_DB::SQL_CURSOR_STATIC)
1090
1154
  index = 0
1091
1155
  # Get @limit rows starting at @offset
@@ -1176,6 +1240,32 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1176
1240
  def query_offset_limit(sql, offset, limit)
1177
1241
  @limit = limit
1178
1242
  @offset = offset
1243
+ if (offset.nil?)
1244
+ sql << " FETCH FIRST #{limit} ROWS ONLY"
1245
+ end
1246
+ end
1247
+
1248
+ # This method generates the default blob value specified for
1249
+ # DB2 Dataservers
1250
+ def set_binary_default(value)
1251
+ "BLOB('#{value}')"
1252
+ end
1253
+
1254
+ # This method generates the blob value specified for DB2 Dataservers
1255
+ def set_binary_value
1256
+ "BLOB('?')"
1257
+ end
1258
+
1259
+ # This method generates the default clob value specified for
1260
+ # DB2 Dataservers
1261
+ def set_text_default(value)
1262
+ "#{value}"
1263
+ end
1264
+
1265
+ # For DB2 Dataservers , the arguments to the meta-data functions
1266
+ # need to be in upper-case
1267
+ def set_case(value)
1268
+ value.upcase
1179
1269
  end
1180
1270
  end # class IBM_DB2
1181
1271
 
@@ -1185,34 +1275,10 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1185
1275
  execute("CALL ADMIN_CMD('REORG TABLE #{table_name}')")
1186
1276
  end
1187
1277
 
1188
- def select_all(sql, name, stmt, results)
1189
- # Fetches all the results available. IBM_DB::fetch_assoc(stmt) returns
1190
- # an hash for each single record.
1191
- # The loop stops when there aren't any more valid records to fetch
1192
- while single_hash = IBM_DB::fetch_assoc(stmt)
1193
- # Add the record to the +results+ array
1194
- results << single_hash
1195
- end
1196
- end
1197
-
1198
- def execute(sql, name = nil)
1199
- begin
1200
- if stmt = IBM_DB::exec(@adapter.connection, sql)
1201
- stmt # Return the statement object
1202
- else
1203
- raise StatementInvalid, IBM_DB::stmt_errormsg
1204
- end
1205
- rescue StandardError
1206
- error_msg = IBM_DB::conn_errormsg ? IBM_DB::conn_errormsg : IBM_DB::stmt_errormsg
1207
- if error_msg && !error_msg.empty?
1208
- raise "Failed to execute statement due to error: #{error_msg}"
1209
- else
1210
- raise
1211
- end
1212
- end
1213
- end
1214
-
1215
1278
  def query_offset_limit(sql, offset, limit)
1279
+ if (offset.nil?)
1280
+ return sql << " FETCH FIRST #{limit} ROWS ONLY"
1281
+ end
1216
1282
  # Defines what will be the last record
1217
1283
  last_record = offset + limit
1218
1284
  # Transforms the SELECT query in order to retrieve/fetch only
@@ -1257,7 +1323,7 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1257
1323
 
1258
1324
  # DB2 z/OS only allows NULL or "" (empty) string as DEFAULT value for a BLOB column.
1259
1325
  # For non-empty string and non-NULL values, the server returns error
1260
- def set_blob_default(value)
1326
+ def set_binary_default(value)
1261
1327
  "#{value}"
1262
1328
  end
1263
1329
 
@@ -1297,5 +1363,119 @@ SET WITH DEFAULT #{@adapter.quote(default)}"
1297
1363
  include HostedDataServer
1298
1364
  end # class IBM_DB2_I5
1299
1365
 
1366
+ class IBM_IDS < IBM_DataServer
1367
+ # IDS does not support the SET SCHEMA syntax
1368
+ def set_schema(schema)
1369
+ end
1370
+
1371
+ def rename_column(table_name, column_name, new_column_name)
1372
+ execute "RENAME COLUMN #{table_name}.#{column_name} TO #{new_column_name}"
1373
+ end
1374
+
1375
+ def primary_key
1376
+ return "SERIAL(100) PRIMARY KEY"
1377
+ end
1378
+
1379
+ def change_column(table_name, column_name, type, options)
1380
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{@adapter.type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
1381
+ if !options[:default].nil?
1382
+ change_column_default(table_name, column_name, options[:default])
1383
+ end
1384
+ reorg_table(table_name)
1385
+ end
1386
+
1387
+ # IDS specific ALTER TABLE statement to add a default clause
1388
+ # IDS requires the data type to be explicitly specified when adding the
1389
+ # DEFAULT clause
1390
+ def change_column_default(table_name, column_name, default)
1391
+ sql_type = nil
1392
+ @adapter.columns(table_name).select do |col|
1393
+ if (col.name == column_name)
1394
+ sql_type = @adapter.type_to_sql(col.type, col.limit, col.precision, col.scale)
1395
+ end
1396
+ end
1397
+ # SQL statement which alters column's default value
1398
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{sql_type} DEFAULT #{@adapter.quote(default)}"
1399
+ stmt = execute(change_column_sql)
1400
+ # Ensures to free the resources associated with the statement
1401
+ ensure
1402
+ IBM_DB::free_result stmt if stmt
1403
+ end
1404
+
1405
+ # Reorganizes the table for column changes
1406
+ def reorg_table(table_name)
1407
+ execute("UPDATE STATISTICS FOR TABLE #{table_name}")
1408
+ end
1409
+
1410
+ # This method returns the IDS SQL type corresponding to the Rails
1411
+ # datetime/timestamp type
1412
+ def get_datetime_mapping
1413
+ return "datetime year to fraction(5)"
1414
+ end
1415
+
1416
+ # This method returns the IDS SQL type corresponding to the Rails
1417
+ # time type
1418
+ def get_time_mapping
1419
+ return "datetime hour to second"
1420
+ end
1421
+
1422
+ # Handling offset/limit as per Informix requirements
1423
+ def query_offset_limit(sql, offset, limit)
1424
+ if limit != 0
1425
+ if !offset.nil?
1426
+ # Modifying the SQL to utilize the skip and limit amounts
1427
+ sql.gsub!(/SELECT/i,"SELECT SKIP #{offset} LIMIT #{limit}")
1428
+ else
1429
+ # Modifying the SQL to retrieve only the first #{limit} rows
1430
+ sql = sql.gsub!("SELECT","SELECT FIRST #{limit}")
1431
+ end
1432
+ else
1433
+ # Modifying the SQL to ensure that no rows will be returned
1434
+ sql.gsub!(/SELECT/i,"SELECT * FROM (SELECT")
1435
+ sql << ") WHERE 0 = 1"
1436
+ end
1437
+ end
1438
+
1439
+ # Method that returns the last automatically generated ID
1440
+ # on the given +@connection+. This method is required by the +insert+
1441
+ # method. IDS returns the last generated serial value in the SQLCA unlike
1442
+ # DB2 where the generated value has to be retrieved using the
1443
+ # IDENTITY_VAL_LOCAL function. We used the "stmt" parameter to identify
1444
+ # the statement resource from which to get the last generated value
1445
+ def last_generated_id(stmt)
1446
+ IBM_DB::get_last_serial_value(stmt)
1447
+ end
1448
+
1449
+ # This method throws an error when trying to create a default value on a
1450
+ # BLOB/CLOB column for IDS. The documentation states: "if the column is a
1451
+ # BLOB or CLOB datatype, NULL is the only valid default value."
1452
+ def set_binary_default(value)
1453
+ unless (value == 'NULL')
1454
+ raise "Informix Dynamic Server only allows NULL as a valid default value for a BLOB data type"
1455
+ end
1456
+ end
1457
+
1458
+ # For Informix Dynamic Server, we treat binary value same as we treat a
1459
+ # text value. We support literals by converting the insert into a dummy
1460
+ # insert and an update (See handle_lobs method above)
1461
+ def set_binary_value
1462
+ "'@@@IBMBINARY@@@'"
1463
+ end
1464
+
1465
+ # This method throws an error when trying to create a default value on a
1466
+ # BLOB/CLOB column for IDS. The documentation states: "if the column is
1467
+ # a BLOB or CLOB datatype, NULL is the only valid default value."
1468
+ def set_text_default(value)
1469
+ unless (value == 'NULL')
1470
+ raise "Informix Dynamic Server only allows NULL as a valid default value for a CLOB data type"
1471
+ end
1472
+ end
1473
+
1474
+ # For Informix Dynamic Server, the arguments to the meta-data functions
1475
+ # need to be in lower-case
1476
+ def set_case(value)
1477
+ value.downcase
1478
+ end
1479
+ end # class IBM_IDS
1300
1480
  end # module ConnectionAdapters
1301
1481
  end # module ActiveRecord