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/CHANGES +6 -1
- data/README +367 -346
- data/ext/ibm_db.c +75 -17
- data/ext/ruby_ibm_db.h +9 -0
- data/lib/active_record/connection_adapters/ibm_db_adapter.rb +258 -78
- data/test/adapter_test.rb +3 -0
- data/test/fixtures/db_definitions/ids/ibm_db.drop.sql +32 -0
- data/test/fixtures/db_definitions/ids/ibm_db.sql +234 -0
- data/test/fixtures/db_definitions/ids/ibm_db2.drop.sql +2 -0
- data/test/fixtures/db_definitions/ids/ibm_db2.sql +5 -0
- metadata +14 -12
- data/lib/linux32/ibm_db.so +0 -0
data/ext/ibm_db.c
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
+----------------------------------------------------------------------+
|
11
11
|
*/
|
12
12
|
|
13
|
-
#define MODULE_RELEASE "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) +
|
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
|
-
|
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
|
-
|
636
|
-
|
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
|
-
|
639
|
-
"BLOB('?')"
|
644
|
+
"'@@@IBMTEXT@@@'"
|
640
645
|
else
|
641
|
-
|
642
|
-
@servertype.set_blob_default(value)
|
646
|
+
@servertype.set_text_default(value)
|
643
647
|
end
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
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 =>
|
705
|
-
:timestamp => { :name =>
|
706
|
-
:time => { :name =>
|
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,
|
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,
|
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
|
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,
|
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
|
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
|
1018
|
-
|
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
|
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
|
1028
|
-
|
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
|
-
|
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
|
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
|