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