activerecord 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +34 -0
- data/README +0 -0
- data/Rakefile +6 -5
- data/lib/active_record.rb +8 -10
- data/lib/active_record/association_preload.rb +17 -12
- data/lib/active_record/associations.rb +45 -27
- data/lib/active_record/associations/association_collection.rb +8 -5
- data/lib/active_record/associations/association_proxy.rb +2 -6
- data/lib/active_record/associations/belongs_to_association.rb +0 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -3
- data/lib/active_record/associations/has_many_association.rb +16 -4
- data/lib/active_record/associations/has_many_through_association.rb +1 -1
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/has_one_through_association.rb +4 -0
- data/lib/active_record/base.rb +33 -15
- data/lib/active_record/calculations.rb +20 -7
- data/lib/active_record/callbacks.rb +0 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +17 -10
- data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +53 -24
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -20
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +12 -0
- data/lib/active_record/dirty.rb +10 -3
- data/lib/active_record/fixtures.rb +0 -0
- data/lib/active_record/locking/optimistic.rb +1 -0
- data/lib/active_record/migration.rb +35 -8
- data/lib/active_record/named_scope.rb +6 -1
- data/lib/active_record/observer.rb +7 -5
- data/lib/active_record/test_case.rb +13 -2
- data/lib/active_record/validations.rb +19 -9
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_postgresql.rb +2 -2
- data/test/cases/adapter_test.rb +1 -1
- data/test/cases/associations/belongs_to_associations_test.rb +19 -0
- data/test/cases/associations/cascaded_eager_loading_test.rb +13 -1
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
- data/test/cases/associations/eager_test.rb +25 -1
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +27 -5
- data/test/cases/associations/has_many_associations_test.rb +106 -4
- data/test/cases/associations/has_many_through_associations_test.rb +10 -0
- data/test/cases/associations/has_one_associations_test.rb +22 -0
- data/test/cases/associations/has_one_through_associations_test.rb +44 -5
- data/test/cases/associations/join_model_test.rb +7 -0
- data/test/cases/associations_test.rb +2 -2
- data/test/cases/attribute_methods_test.rb +10 -10
- data/test/cases/base_test.rb +39 -16
- data/test/cases/calculations_test.rb +53 -1
- data/test/cases/column_definition_test.rb +36 -0
- data/test/cases/database_statements_test.rb +12 -0
- data/test/cases/defaults_test.rb +1 -1
- data/test/cases/deprecated_finder_test.rb +0 -0
- data/test/cases/dirty_test.rb +94 -0
- data/test/cases/finder_test.rb +7 -0
- data/test/cases/fixtures_test.rb +0 -0
- data/test/cases/helper.rb +5 -5
- data/test/cases/inheritance_test.rb +9 -2
- data/test/cases/lifecycle_test.rb +54 -1
- data/test/cases/locking_test.rb +20 -0
- data/test/cases/method_scoping_test.rb +11 -1
- data/test/cases/migration_test.rb +147 -22
- data/test/cases/multiple_db_test.rb +1 -1
- data/test/cases/named_scope_test.rb +50 -1
- data/test/cases/query_cache_test.rb +4 -3
- data/test/cases/readonly_test.rb +0 -0
- data/test/cases/reflection_test.rb +3 -3
- data/test/cases/schema_dumper_test.rb +46 -0
- data/test/cases/unconnected_test.rb +0 -0
- data/test/cases/validations_test.rb +30 -5
- data/test/debug.log +358 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/models/author.rb +4 -0
- data/test/models/category.rb +1 -0
- data/test/models/company.rb +10 -1
- data/test/models/developer.rb +4 -1
- data/test/models/person.rb +1 -1
- data/test/models/post.rb +6 -1
- data/test/models/project.rb +1 -1
- data/test/models/reply.rb +0 -0
- data/test/models/topic.rb +1 -0
- data/test/schema/mysql_specific_schema.rb +2 -2
- data/test/schema/schema.rb +8 -0
- metadata +11 -5
- data/lib/active_record/vendor/db2.rb +0 -362
Binary file
|
Binary file
|
data/test/models/author.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
class Author < ActiveRecord::Base
|
2
2
|
has_many :posts
|
3
3
|
has_many :posts_with_comments, :include => :comments, :class_name => "Post"
|
4
|
+
has_many :posts_with_comments_sorted_by_comment_id, :include => :comments, :class_name => "Post", :order => 'comments.id'
|
4
5
|
has_many :posts_with_categories, :include => :categories, :class_name => "Post"
|
5
6
|
has_many :posts_with_comments_and_categories, :include => [ :comments, :categories ], :order => "posts.id", :class_name => "Post"
|
6
7
|
has_many :posts_containing_the_letter_a, :class_name => "Post"
|
@@ -31,6 +32,9 @@ class Author < ActiveRecord::Base
|
|
31
32
|
has_many :special_posts
|
32
33
|
has_many :special_post_comments, :through => :special_posts, :source => :comments
|
33
34
|
|
35
|
+
has_many :sti_posts, :class_name => 'StiPost'
|
36
|
+
has_many :sti_post_comments, :through => :sti_posts, :source => :comments
|
37
|
+
|
34
38
|
has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
|
35
39
|
has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0"
|
36
40
|
has_many :nonexistant_comments, :through => :posts
|
data/test/models/category.rb
CHANGED
@@ -2,6 +2,7 @@ class Category < ActiveRecord::Base
|
|
2
2
|
has_and_belongs_to_many :posts
|
3
3
|
has_and_belongs_to_many :special_posts, :class_name => "Post"
|
4
4
|
has_and_belongs_to_many :other_posts, :class_name => "Post"
|
5
|
+
has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, :class_name => "Post", :include => :authors, :order => "authors.id"
|
5
6
|
|
6
7
|
has_and_belongs_to_many(:select_testing_posts,
|
7
8
|
:class_name => 'Post',
|
data/test/models/company.rb
CHANGED
@@ -18,6 +18,13 @@ end
|
|
18
18
|
module Namespaced
|
19
19
|
class Company < ::Company
|
20
20
|
end
|
21
|
+
|
22
|
+
class Firm < ::Company
|
23
|
+
has_many :clients, :class_name => 'Namespaced::Client'
|
24
|
+
end
|
25
|
+
|
26
|
+
class Client < ::Company
|
27
|
+
end
|
21
28
|
end
|
22
29
|
|
23
30
|
class Firm < Company
|
@@ -26,6 +33,7 @@ class Firm < Company
|
|
26
33
|
"AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )"
|
27
34
|
has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
|
28
35
|
has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
|
36
|
+
has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
|
29
37
|
has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy
|
30
38
|
has_many :exclusively_dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all
|
31
39
|
has_many :limited_clients, :class_name => "Client", :order => "id", :limit => 1
|
@@ -46,7 +54,8 @@ class Firm < Company
|
|
46
54
|
has_many :plain_clients, :class_name => 'Client'
|
47
55
|
has_many :readonly_clients, :class_name => 'Client', :readonly => true
|
48
56
|
|
49
|
-
has_one :account, :foreign_key => "firm_id", :dependent => :destroy
|
57
|
+
has_one :account, :foreign_key => "firm_id", :dependent => :destroy, :validate => true
|
58
|
+
has_one :unvalidated_account, :foreign_key => "firm_id", :class_name => 'Account', :validate => false
|
50
59
|
has_one :account_with_select, :foreign_key => "firm_id", :select => "id, firm_id", :class_name=>'Account'
|
51
60
|
has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true
|
52
61
|
end
|
data/test/models/developer.rb
CHANGED
@@ -43,6 +43,8 @@ class Developer < ActiveRecord::Base
|
|
43
43
|
|
44
44
|
has_many :audit_logs
|
45
45
|
|
46
|
+
named_scope :jamises, :conditions => {:name => 'Jamis'}
|
47
|
+
|
46
48
|
validates_inclusion_of :salary, :in => 50000..200000
|
47
49
|
validates_length_of :name, :within => 3..20
|
48
50
|
|
@@ -56,7 +58,8 @@ class Developer < ActiveRecord::Base
|
|
56
58
|
end
|
57
59
|
|
58
60
|
class AuditLog < ActiveRecord::Base
|
59
|
-
belongs_to :developer
|
61
|
+
belongs_to :developer, :validate => true
|
62
|
+
belongs_to :unvalidated_developer, :class_name => 'Developer'
|
60
63
|
end
|
61
64
|
|
62
65
|
DeveloperSalary = Struct.new(:amount)
|
data/test/models/person.rb
CHANGED
@@ -6,5 +6,5 @@ class Person < ActiveRecord::Base
|
|
6
6
|
has_many :references
|
7
7
|
has_many :jobs, :through => :references
|
8
8
|
has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
|
9
|
-
|
9
|
+
has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id'
|
10
10
|
end
|
data/test/models/post.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
class Post < ActiveRecord::Base
|
2
2
|
named_scope :containing_the_letter_a, :conditions => "body LIKE '%a%'"
|
3
|
-
|
3
|
+
named_scope :with_authors_at_address, lambda { |address| {
|
4
|
+
:conditions => [ 'authors.author_address_id = ?', address.id ],
|
5
|
+
:joins => 'JOIN authors ON authors.id = posts.author_id'
|
6
|
+
}
|
7
|
+
}
|
8
|
+
|
4
9
|
belongs_to :author do
|
5
10
|
def greeting
|
6
11
|
"hello"
|
data/test/models/project.rb
CHANGED
@@ -7,7 +7,7 @@ class Project < ActiveRecord::Base
|
|
7
7
|
has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true
|
8
8
|
has_and_belongs_to_many :developers_named_david_with_hash_conditions, :class_name => "Developer", :conditions => { :name => 'David' }, :uniq => true
|
9
9
|
has_and_belongs_to_many :salaried_developers, :class_name => "Developer", :conditions => "salary > 0"
|
10
|
-
has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id}'
|
10
|
+
has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id'
|
11
11
|
has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => "DELETE FROM developers_projects WHERE project_id = \#{id} AND developer_id = \#{record.id}"
|
12
12
|
has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"},
|
13
13
|
:after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"},
|
data/test/models/reply.rb
CHANGED
File without changes
|
data/test/models/topic.rb
CHANGED
@@ -4,6 +4,7 @@ class Topic < ActiveRecord::Base
|
|
4
4
|
{ :conditions => ['written_on < ?', time] }
|
5
5
|
}
|
6
6
|
named_scope :approved, :conditions => {:approved => true}
|
7
|
+
named_scope 'approved_as_string', :conditions => {:approved => true}
|
7
8
|
named_scope :replied, :conditions => ['replies_count > 0']
|
8
9
|
named_scope :anonymous_extension do
|
9
10
|
def one
|
@@ -1,5 +1,5 @@
|
|
1
1
|
ActiveRecord::Schema.define do
|
2
|
-
create_table :binary_fields, :force => true do |t|
|
2
|
+
create_table :binary_fields, :force => true, :options => 'CHARACTER SET latin1' do |t|
|
3
3
|
t.binary :tiny_blob, :limit => 255
|
4
4
|
t.binary :normal_blob, :limit => 65535
|
5
5
|
t.binary :medium_blob, :limit => 16777215
|
@@ -9,4 +9,4 @@ ActiveRecord::Schema.define do
|
|
9
9
|
t.text :medium_text, :limit => 16777215
|
10
10
|
t.text :long_text, :limit => 2147483647
|
11
11
|
end
|
12
|
-
end
|
12
|
+
end
|
data/test/schema/schema.rb
CHANGED
@@ -66,6 +66,7 @@ ActiveRecord::Schema.define do
|
|
66
66
|
create_table :categories, :force => true do |t|
|
67
67
|
t.string :name, :null => false
|
68
68
|
t.string :type
|
69
|
+
t.integer :categorizations_count
|
69
70
|
end
|
70
71
|
|
71
72
|
create_table :categories_posts, :force => true, :id => false do |t|
|
@@ -407,6 +408,13 @@ ActiveRecord::Schema.define do
|
|
407
408
|
t.column :key, :string
|
408
409
|
end
|
409
410
|
|
411
|
+
create_table :integer_limits, :force => true do |t|
|
412
|
+
t.integer :"c_int_without_limit"
|
413
|
+
(1..8).each do |i|
|
414
|
+
t.integer :"c_int_#{i}", :limit => i
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
410
418
|
except 'SQLite' do
|
411
419
|
# fk_test_has_fk should be before fk_test_has_pk
|
412
420
|
create_table :fk_test_has_fk, :force => true do |t|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
@@ -9,17 +9,18 @@ autorequire: active_record
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-09-04 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
17
|
+
type: :runtime
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
20
21
|
- - "="
|
21
22
|
- !ruby/object:Gem::Version
|
22
|
-
version: 2.1.
|
23
|
+
version: 2.1.1
|
23
24
|
version:
|
24
25
|
description: Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.
|
25
26
|
email: david@loudthinking.com
|
@@ -87,7 +88,6 @@ files:
|
|
87
88
|
- lib/active_record/transactions.rb
|
88
89
|
- lib/active_record/validations.rb
|
89
90
|
- lib/active_record/vendor
|
90
|
-
- lib/active_record/vendor/db2.rb
|
91
91
|
- lib/active_record/vendor/mysql.rb
|
92
92
|
- lib/active_record/version.rb
|
93
93
|
- lib/active_record.rb
|
@@ -107,6 +107,7 @@ files:
|
|
107
107
|
- test/cases/associations/belongs_to_associations_test.rb
|
108
108
|
- test/cases/associations/callbacks_test.rb
|
109
109
|
- test/cases/associations/cascaded_eager_loading_test.rb
|
110
|
+
- test/cases/associations/eager_load_includes_full_sti_class_test.rb
|
110
111
|
- test/cases/associations/eager_load_nested_include_test.rb
|
111
112
|
- test/cases/associations/eager_singularization_test.rb
|
112
113
|
- test/cases/associations/eager_test.rb
|
@@ -126,9 +127,11 @@ files:
|
|
126
127
|
- test/cases/callbacks_test.rb
|
127
128
|
- test/cases/class_inheritable_attributes_test.rb
|
128
129
|
- test/cases/column_alias_test.rb
|
130
|
+
- test/cases/column_definition_test.rb
|
129
131
|
- test/cases/connection_test_firebird.rb
|
130
132
|
- test/cases/connection_test_mysql.rb
|
131
133
|
- test/cases/copy_table_test_sqlite.rb
|
134
|
+
- test/cases/database_statements_test.rb
|
132
135
|
- test/cases/datatype_test_postgresql.rb
|
133
136
|
- test/cases/date_time_test.rb
|
134
137
|
- test/cases/default_test_firebird.rb
|
@@ -190,6 +193,7 @@ files:
|
|
190
193
|
- test/connections/native_sqlite3/in_memory_connection.rb
|
191
194
|
- test/connections/native_sybase
|
192
195
|
- test/connections/native_sybase/connection.rb
|
196
|
+
- test/debug.log
|
193
197
|
- test/fixtures
|
194
198
|
- test/fixtures/accounts.yml
|
195
199
|
- test/fixtures/all
|
@@ -219,6 +223,8 @@ files:
|
|
219
223
|
- test/fixtures/developers_projects.yml
|
220
224
|
- test/fixtures/edges.yml
|
221
225
|
- test/fixtures/entrants.yml
|
226
|
+
- test/fixtures/fixture_database.sqlite3
|
227
|
+
- test/fixtures/fixture_database_2.sqlite3
|
222
228
|
- test/fixtures/fk_test_has_fk.yml
|
223
229
|
- test/fixtures/fk_test_has_pk.yml
|
224
230
|
- test/fixtures/funny_jokes.yml
|
@@ -385,7 +391,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
385
391
|
requirements: []
|
386
392
|
|
387
393
|
rubyforge_project: activerecord
|
388
|
-
rubygems_version: 1.0
|
394
|
+
rubygems_version: 1.2.0
|
389
395
|
signing_key:
|
390
396
|
specification_version: 2
|
391
397
|
summary: Implements the ActiveRecord pattern for ORM.
|
@@ -1,362 +0,0 @@
|
|
1
|
-
require 'db2/db2cli.rb'
|
2
|
-
|
3
|
-
module DB2
|
4
|
-
module DB2Util
|
5
|
-
include DB2CLI
|
6
|
-
|
7
|
-
def free() SQLFreeHandle(@handle_type, @handle); end
|
8
|
-
def handle() @handle; end
|
9
|
-
|
10
|
-
def check_rc(rc)
|
11
|
-
if ![SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA_FOUND].include?(rc)
|
12
|
-
rec = 1
|
13
|
-
msg = ''
|
14
|
-
loop do
|
15
|
-
a = SQLGetDiagRec(@handle_type, @handle, rec, 500)
|
16
|
-
break if a[0] != SQL_SUCCESS
|
17
|
-
msg << a[3] if !a[3].nil? and a[3] != '' # Create message.
|
18
|
-
rec += 1
|
19
|
-
end
|
20
|
-
raise "DB2 error: #{msg}"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Environment
|
26
|
-
include DB2Util
|
27
|
-
|
28
|
-
def initialize
|
29
|
-
@handle_type = SQL_HANDLE_ENV
|
30
|
-
rc, @handle = SQLAllocHandle(@handle_type, SQL_NULL_HANDLE)
|
31
|
-
check_rc(rc)
|
32
|
-
end
|
33
|
-
|
34
|
-
def data_sources(buffer_length = 1024)
|
35
|
-
retval = []
|
36
|
-
max_buffer_length = buffer_length
|
37
|
-
|
38
|
-
a = SQLDataSources(@handle, SQL_FETCH_FIRST, SQL_MAX_DSN_LENGTH + 1, buffer_length)
|
39
|
-
retval << [a[1], a[3]]
|
40
|
-
max_buffer_length = [max_buffer_length, a[4]].max
|
41
|
-
|
42
|
-
loop do
|
43
|
-
a = SQLDataSources(@handle, SQL_FETCH_NEXT, SQL_MAX_DSN_LENGTH + 1, buffer_length)
|
44
|
-
break if a[0] == SQL_NO_DATA_FOUND
|
45
|
-
|
46
|
-
retval << [a[1], a[3]]
|
47
|
-
max_buffer_length = [max_buffer_length, a[4]].max
|
48
|
-
end
|
49
|
-
|
50
|
-
if max_buffer_length > buffer_length
|
51
|
-
get_data_sources(max_buffer_length)
|
52
|
-
else
|
53
|
-
retval
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
class Connection
|
59
|
-
include DB2Util
|
60
|
-
|
61
|
-
def initialize(environment)
|
62
|
-
@env = environment
|
63
|
-
@handle_type = SQL_HANDLE_DBC
|
64
|
-
rc, @handle = SQLAllocHandle(@handle_type, @env.handle)
|
65
|
-
check_rc(rc)
|
66
|
-
end
|
67
|
-
|
68
|
-
def connect(server_name, user_name = '', auth = '')
|
69
|
-
check_rc(SQLConnect(@handle, server_name, user_name.to_s, auth.to_s))
|
70
|
-
end
|
71
|
-
|
72
|
-
def set_connect_attr(attr, value)
|
73
|
-
value += "\0" if value.class == String
|
74
|
-
check_rc(SQLSetConnectAttr(@handle, attr, value))
|
75
|
-
end
|
76
|
-
|
77
|
-
def set_auto_commit_on
|
78
|
-
set_connect_attr(SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON)
|
79
|
-
end
|
80
|
-
|
81
|
-
def set_auto_commit_off
|
82
|
-
set_connect_attr(SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF)
|
83
|
-
end
|
84
|
-
|
85
|
-
def disconnect
|
86
|
-
check_rc(SQLDisconnect(@handle))
|
87
|
-
end
|
88
|
-
|
89
|
-
def rollback
|
90
|
-
check_rc(SQLEndTran(@handle_type, @handle, SQL_ROLLBACK))
|
91
|
-
end
|
92
|
-
|
93
|
-
def commit
|
94
|
-
check_rc(SQLEndTran(@handle_type, @handle, SQL_COMMIT))
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
class Statement
|
99
|
-
include DB2Util
|
100
|
-
|
101
|
-
def initialize(connection)
|
102
|
-
@conn = connection
|
103
|
-
@handle_type = SQL_HANDLE_STMT
|
104
|
-
@parms = [] #yun
|
105
|
-
@sql = '' #yun
|
106
|
-
@numParms = 0 #yun
|
107
|
-
@prepared = false #yun
|
108
|
-
@parmArray = [] #yun. attributes of the parameter markers
|
109
|
-
rc, @handle = SQLAllocHandle(@handle_type, @conn.handle)
|
110
|
-
check_rc(rc)
|
111
|
-
end
|
112
|
-
|
113
|
-
def columns(table_name, schema_name = '%')
|
114
|
-
check_rc(SQLColumns(@handle, '', schema_name.upcase, table_name.upcase, '%'))
|
115
|
-
fetch_all
|
116
|
-
end
|
117
|
-
|
118
|
-
def tables(schema_name = '%')
|
119
|
-
check_rc(SQLTables(@handle, '', schema_name.upcase, '%', 'TABLE'))
|
120
|
-
fetch_all
|
121
|
-
end
|
122
|
-
|
123
|
-
def indexes(table_name, schema_name = '')
|
124
|
-
check_rc(SQLStatistics(@handle, '', schema_name.upcase, table_name.upcase, SQL_INDEX_ALL, SQL_ENSURE))
|
125
|
-
fetch_all
|
126
|
-
end
|
127
|
-
|
128
|
-
def prepare(sql)
|
129
|
-
@sql = sql
|
130
|
-
check_rc(SQLPrepare(@handle, sql))
|
131
|
-
rc, @numParms = SQLNumParams(@handle) #number of question marks
|
132
|
-
check_rc(rc)
|
133
|
-
#--------------------------------------------------------------------------
|
134
|
-
# parameter attributes are stored in instance variable @parmArray so that
|
135
|
-
# they are available when execute method is called.
|
136
|
-
#--------------------------------------------------------------------------
|
137
|
-
if @numParms > 0 # get parameter marker attributes
|
138
|
-
1.upto(@numParms) do |i| # parameter number starts from 1
|
139
|
-
rc, type, size, decimalDigits = SQLDescribeParam(@handle, i)
|
140
|
-
check_rc(rc)
|
141
|
-
@parmArray << Parameter.new(type, size, decimalDigits)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
@prepared = true
|
145
|
-
self
|
146
|
-
end
|
147
|
-
|
148
|
-
def execute(*parms)
|
149
|
-
raise "The statement was not prepared" if @prepared == false
|
150
|
-
|
151
|
-
if parms.size == 1 and parms[0].class == Array
|
152
|
-
parms = parms[0]
|
153
|
-
end
|
154
|
-
|
155
|
-
if @numParms != parms.size
|
156
|
-
raise "Number of parameters supplied does not match with the SQL statement"
|
157
|
-
end
|
158
|
-
|
159
|
-
if @numParms > 0 #need to bind parameters
|
160
|
-
#--------------------------------------------------------------------
|
161
|
-
#calling bindParms may not be safe. Look comment below.
|
162
|
-
#--------------------------------------------------------------------
|
163
|
-
#bindParms(parms)
|
164
|
-
|
165
|
-
valueArray = []
|
166
|
-
1.upto(@numParms) do |i| # parameter number starts from 1
|
167
|
-
type = @parmArray[i - 1].class
|
168
|
-
size = @parmArray[i - 1].size
|
169
|
-
decimalDigits = @parmArray[i - 1].decimalDigits
|
170
|
-
|
171
|
-
if parms[i - 1].class == String
|
172
|
-
valueArray << parms[i - 1]
|
173
|
-
else
|
174
|
-
valueArray << parms[i - 1].to_s
|
175
|
-
end
|
176
|
-
|
177
|
-
rc = SQLBindParameter(@handle, i, type, size, decimalDigits, valueArray[i - 1])
|
178
|
-
check_rc(rc)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
check_rc(SQLExecute(@handle))
|
183
|
-
|
184
|
-
if @numParms != 0
|
185
|
-
check_rc(SQLFreeStmt(@handle, SQL_RESET_PARAMS)) # Reset parameters
|
186
|
-
end
|
187
|
-
|
188
|
-
self
|
189
|
-
end
|
190
|
-
|
191
|
-
#-------------------------------------------------------------------------------
|
192
|
-
# The last argument(value) to SQLBindParameter is a deferred argument, that is,
|
193
|
-
# it should be available when SQLExecute is called. Even though "value" is
|
194
|
-
# local to bindParms method, it seems that it is available when SQLExecute
|
195
|
-
# is called. I am not sure whether it would still work if garbage collection
|
196
|
-
# is done between bindParms call and SQLExecute call inside the execute method
|
197
|
-
# above.
|
198
|
-
#-------------------------------------------------------------------------------
|
199
|
-
def bindParms(parms) # This is the real thing. It uses SQLBindParms
|
200
|
-
1.upto(@numParms) do |i| # parameter number starts from 1
|
201
|
-
rc, dataType, parmSize, decimalDigits = SQLDescribeParam(@handle, i)
|
202
|
-
check_rc(rc)
|
203
|
-
if parms[i - 1].class == String
|
204
|
-
value = parms[i - 1]
|
205
|
-
else
|
206
|
-
value = parms[i - 1].to_s
|
207
|
-
end
|
208
|
-
rc = SQLBindParameter(@handle, i, dataType, parmSize, decimalDigits, value)
|
209
|
-
check_rc(rc)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
#------------------------------------------------------------------------------
|
214
|
-
# bind method does not use DB2's SQLBindParams, but replaces "?" in the
|
215
|
-
# SQL statement with the value before passing the SQL statement to DB2.
|
216
|
-
# It is not efficient and can handle only strings since it puts everything in
|
217
|
-
# quotes.
|
218
|
-
#------------------------------------------------------------------------------
|
219
|
-
def bind(sql, args) #does not use SQLBindParams
|
220
|
-
arg_index = 0
|
221
|
-
result = ""
|
222
|
-
tokens(sql).each do |part|
|
223
|
-
case part
|
224
|
-
when '?'
|
225
|
-
result << "'" + (args[arg_index]) + "'" #put it into quotes
|
226
|
-
arg_index += 1
|
227
|
-
when '??'
|
228
|
-
result << "?"
|
229
|
-
else
|
230
|
-
result << part
|
231
|
-
end
|
232
|
-
end
|
233
|
-
if arg_index < args.size
|
234
|
-
raise "Too many SQL parameters"
|
235
|
-
elsif arg_index > args.size
|
236
|
-
raise "Not enough SQL parameters"
|
237
|
-
end
|
238
|
-
result
|
239
|
-
end
|
240
|
-
|
241
|
-
## Break the sql string into parts.
|
242
|
-
#
|
243
|
-
# This is NOT a full lexer for SQL. It just breaks up the SQL
|
244
|
-
# string enough so that question marks, double question marks and
|
245
|
-
# quoted strings are separated. This is used when binding
|
246
|
-
# arguments to "?" in the SQL string. Note: comments are not
|
247
|
-
# handled.
|
248
|
-
#
|
249
|
-
def tokens(sql)
|
250
|
-
toks = sql.scan(/('([^'\\]|''|\\.)*'|"([^"\\]|""|\\.)*"|\?\??|[^'"?]+)/)
|
251
|
-
toks.collect { |t| t[0] }
|
252
|
-
end
|
253
|
-
|
254
|
-
def exec_direct(sql)
|
255
|
-
check_rc(SQLExecDirect(@handle, sql))
|
256
|
-
self
|
257
|
-
end
|
258
|
-
|
259
|
-
def set_cursor_name(name)
|
260
|
-
check_rc(SQLSetCursorName(@handle, name))
|
261
|
-
self
|
262
|
-
end
|
263
|
-
|
264
|
-
def get_cursor_name
|
265
|
-
rc, name = SQLGetCursorName(@handle)
|
266
|
-
check_rc(rc)
|
267
|
-
name
|
268
|
-
end
|
269
|
-
|
270
|
-
def row_count
|
271
|
-
rc, rowcount = SQLRowCount(@handle)
|
272
|
-
check_rc(rc)
|
273
|
-
rowcount
|
274
|
-
end
|
275
|
-
|
276
|
-
def num_result_cols
|
277
|
-
rc, cols = SQLNumResultCols(@handle)
|
278
|
-
check_rc(rc)
|
279
|
-
cols
|
280
|
-
end
|
281
|
-
|
282
|
-
def fetch_all
|
283
|
-
if block_given?
|
284
|
-
while row = fetch do
|
285
|
-
yield row
|
286
|
-
end
|
287
|
-
else
|
288
|
-
res = []
|
289
|
-
while row = fetch do
|
290
|
-
res << row
|
291
|
-
end
|
292
|
-
res
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
def fetch
|
297
|
-
cols = get_col_desc
|
298
|
-
rc = SQLFetch(@handle)
|
299
|
-
if rc == SQL_NO_DATA_FOUND
|
300
|
-
SQLFreeStmt(@handle, SQL_CLOSE) # Close cursor
|
301
|
-
SQLFreeStmt(@handle, SQL_RESET_PARAMS) # Reset parameters
|
302
|
-
return nil
|
303
|
-
end
|
304
|
-
raise "ERROR" unless rc == SQL_SUCCESS
|
305
|
-
|
306
|
-
retval = []
|
307
|
-
cols.each_with_index do |c, i|
|
308
|
-
rc, content = SQLGetData(@handle, i + 1, c[1], c[2] + 1) #yun added 1 to c[2]
|
309
|
-
retval << adjust_content(content)
|
310
|
-
end
|
311
|
-
retval
|
312
|
-
end
|
313
|
-
|
314
|
-
def fetch_as_hash
|
315
|
-
cols = get_col_desc
|
316
|
-
rc = SQLFetch(@handle)
|
317
|
-
if rc == SQL_NO_DATA_FOUND
|
318
|
-
SQLFreeStmt(@handle, SQL_CLOSE) # Close cursor
|
319
|
-
SQLFreeStmt(@handle, SQL_RESET_PARAMS) # Reset parameters
|
320
|
-
return nil
|
321
|
-
end
|
322
|
-
raise "ERROR" unless rc == SQL_SUCCESS
|
323
|
-
|
324
|
-
retval = {}
|
325
|
-
cols.each_with_index do |c, i|
|
326
|
-
rc, content = SQLGetData(@handle, i + 1, c[1], c[2] + 1) #yun added 1 to c[2]
|
327
|
-
retval[c[0]] = adjust_content(content)
|
328
|
-
end
|
329
|
-
retval
|
330
|
-
end
|
331
|
-
|
332
|
-
def get_col_desc
|
333
|
-
rc, nr_cols = SQLNumResultCols(@handle)
|
334
|
-
cols = (1..nr_cols).collect do |c|
|
335
|
-
rc, name, bl, type, col_sz = SQLDescribeCol(@handle, c, 1024)
|
336
|
-
[name.downcase, type, col_sz]
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def adjust_content(c)
|
341
|
-
case c.class.to_s
|
342
|
-
when 'DB2CLI::NullClass'
|
343
|
-
return nil
|
344
|
-
when 'DB2CLI::Time'
|
345
|
-
"%02d:%02d:%02d" % [c.hour, c.minute, c.second]
|
346
|
-
when 'DB2CLI::Date'
|
347
|
-
"%04d-%02d-%02d" % [c.year, c.month, c.day]
|
348
|
-
when 'DB2CLI::Timestamp'
|
349
|
-
"%04d-%02d-%02d %02d:%02d:%02d" % [c.year, c.month, c.day, c.hour, c.minute, c.second]
|
350
|
-
else
|
351
|
-
return c
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
class Parameter
|
357
|
-
attr_reader :type, :size, :decimalDigits
|
358
|
-
def initialize(type, size, decimalDigits)
|
359
|
-
@type, @size, @decimalDigits = type, size, decimalDigits
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|