ibm_db 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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