activerecord-rdb-adapter 0.9.4 → 0.9.5
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.
- checksums.yaml +4 -4
- data/README.md +8 -8
- data/extconf.rb +93 -93
- data/fb.c +3072 -3072
- data/fb_extensions.rb +21 -21
- data/lib/active_model/type/integer.rb +67 -67
- data/lib/active_record/connection_adapters/rdb/database_limits.rb +35 -35
- data/lib/active_record/connection_adapters/rdb/database_statements.rb +186 -183
- data/lib/active_record/connection_adapters/rdb/quoting.rb +152 -152
- data/lib/active_record/connection_adapters/rdb/schema_creation.rb +52 -52
- data/lib/active_record/connection_adapters/rdb/schema_dumper.rb +23 -23
- data/lib/active_record/connection_adapters/rdb/schema_statements.rb +431 -431
- data/lib/active_record/connection_adapters/rdb/table_definition.rb +28 -28
- data/lib/active_record/connection_adapters/rdb_adapter.rb +163 -163
- data/lib/active_record/connection_adapters/rdb_column.rb +69 -69
- data/lib/active_record/rdb_base.rb +34 -34
- data/lib/active_record/tasks/rdb_database_tasks.rb +78 -78
- data/lib/activerecord-rdb-adapter.rb +10 -10
- data/lib/arel/visitors/rdb_visitor.rb +135 -135
- metadata +3 -4
data/fb_extensions.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
module Fb
|
2
|
-
class Connection
|
3
|
-
def execute_script(sql)
|
4
|
-
stmts = []
|
5
|
-
delim = ';'
|
6
|
-
while sql =~ /\S/
|
7
|
-
stmt, sql = sql.split(delim, 2)
|
8
|
-
if stmt =~ /^\s*set\s+term\s+(\S+)/i
|
9
|
-
delim = $1
|
10
|
-
elsif stmt =~ /\S/
|
11
|
-
stmts << stmt
|
12
|
-
end
|
13
|
-
end
|
14
|
-
self.transaction do
|
15
|
-
stmts.each do |stmt|
|
16
|
-
self.execute(stmt)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
1
|
+
module Fb
|
2
|
+
class Connection
|
3
|
+
def execute_script(sql)
|
4
|
+
stmts = []
|
5
|
+
delim = ';'
|
6
|
+
while sql =~ /\S/
|
7
|
+
stmt, sql = sql.split(delim, 2)
|
8
|
+
if stmt =~ /^\s*set\s+term\s+(\S+)/i
|
9
|
+
delim = $1
|
10
|
+
elsif stmt =~ /\S/
|
11
|
+
stmts << stmt
|
12
|
+
end
|
13
|
+
end
|
14
|
+
self.transaction do
|
15
|
+
stmts.each do |stmt|
|
16
|
+
self.execute(stmt)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,68 +1,68 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveModel
|
4
|
-
module Type
|
5
|
-
class Integer < Value # :nodoc:
|
6
|
-
include Helpers::Numeric
|
7
|
-
|
8
|
-
DEFAULT_LIMIT = 8
|
9
|
-
|
10
|
-
def initialize(*)
|
11
|
-
super
|
12
|
-
@range = min_value...max_value
|
13
|
-
end
|
14
|
-
|
15
|
-
def type
|
16
|
-
:integer
|
17
|
-
end
|
18
|
-
|
19
|
-
def deserialize(value)
|
20
|
-
return if value.nil?
|
21
|
-
value.to_i
|
22
|
-
end
|
23
|
-
|
24
|
-
def serialize(value)
|
25
|
-
result = cast(value)
|
26
|
-
if result
|
27
|
-
ensure_in_range(result)
|
28
|
-
end
|
29
|
-
result
|
30
|
-
end
|
31
|
-
|
32
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
33
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
34
|
-
protected
|
35
|
-
|
36
|
-
attr_reader :range
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def cast_value(value)
|
41
|
-
case value
|
42
|
-
when true then 1
|
43
|
-
when false then 0
|
44
|
-
else
|
45
|
-
value.to_i rescue nil
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def ensure_in_range(value)
|
50
|
-
unless range.cover?(value)
|
51
|
-
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def max_value
|
56
|
-
1 << (_limit * 8 - 1) # 8 bits per byte with one bit for sign
|
57
|
-
end
|
58
|
-
|
59
|
-
def min_value
|
60
|
-
-max_value
|
61
|
-
end
|
62
|
-
|
63
|
-
def _limit
|
64
|
-
limit || DEFAULT_LIMIT
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Type
|
5
|
+
class Integer < Value # :nodoc:
|
6
|
+
include Helpers::Numeric
|
7
|
+
|
8
|
+
DEFAULT_LIMIT = 8
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@range = min_value...max_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def type
|
16
|
+
:integer
|
17
|
+
end
|
18
|
+
|
19
|
+
def deserialize(value)
|
20
|
+
return if value.nil?
|
21
|
+
value.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def serialize(value)
|
25
|
+
result = cast(value)
|
26
|
+
if result
|
27
|
+
ensure_in_range(result)
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
33
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
34
|
+
protected
|
35
|
+
|
36
|
+
attr_reader :range
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def cast_value(value)
|
41
|
+
case value
|
42
|
+
when true then 1
|
43
|
+
when false then 0
|
44
|
+
else
|
45
|
+
value.to_i rescue nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def ensure_in_range(value)
|
50
|
+
unless range.cover?(value)
|
51
|
+
raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def max_value
|
56
|
+
1 << (_limit * 8 - 1) # 8 bits per byte with one bit for sign
|
57
|
+
end
|
58
|
+
|
59
|
+
def min_value
|
60
|
+
-max_value
|
61
|
+
end
|
62
|
+
|
63
|
+
def _limit
|
64
|
+
limit || DEFAULT_LIMIT
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
68
|
end
|
@@ -1,35 +1,35 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module ConnectionAdapters
|
3
|
-
module Rdb
|
4
|
-
module DatabaseLimits # :nodoc:
|
5
|
-
def table_alias_length
|
6
|
-
31
|
7
|
-
end
|
8
|
-
|
9
|
-
def column_name_length
|
10
|
-
31
|
11
|
-
end
|
12
|
-
|
13
|
-
def table_name_length
|
14
|
-
31
|
15
|
-
end
|
16
|
-
|
17
|
-
def index_name_length
|
18
|
-
31
|
19
|
-
end
|
20
|
-
|
21
|
-
def indexes_per_table
|
22
|
-
65_535
|
23
|
-
end
|
24
|
-
|
25
|
-
def in_clause_length
|
26
|
-
1_499
|
27
|
-
end
|
28
|
-
|
29
|
-
def sql_query_length
|
30
|
-
32_767
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Rdb
|
4
|
+
module DatabaseLimits # :nodoc:
|
5
|
+
def table_alias_length
|
6
|
+
31
|
7
|
+
end
|
8
|
+
|
9
|
+
def column_name_length
|
10
|
+
31
|
11
|
+
end
|
12
|
+
|
13
|
+
def table_name_length
|
14
|
+
31
|
15
|
+
end
|
16
|
+
|
17
|
+
def index_name_length
|
18
|
+
31
|
19
|
+
end
|
20
|
+
|
21
|
+
def indexes_per_table
|
22
|
+
65_535
|
23
|
+
end
|
24
|
+
|
25
|
+
def in_clause_length
|
26
|
+
1_499
|
27
|
+
end
|
28
|
+
|
29
|
+
def sql_query_length
|
30
|
+
32_767
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,183 +1,186 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module ConnectionAdapters
|
3
|
-
module Rdb
|
4
|
-
module DatabaseStatements # :nodoc:
|
5
|
-
def execute(sql, name = nil)
|
6
|
-
log(sql, name) do
|
7
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
8
|
-
@connection.query(sql)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
|
14
|
-
type_casted_binds = type_casted_binds(binds)
|
15
|
-
|
16
|
-
log(sql, name, binds, type_casted_binds) do
|
17
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
18
|
-
result = @connection.execute(sql, *type_casted_binds)
|
19
|
-
if result.is_a?(Fb::Cursor)
|
20
|
-
fields = result.fields.map(&:name)
|
21
|
-
rows = result.fetchall.map do |row|
|
22
|
-
row.map do |col|
|
23
|
-
col.encode('UTF-8', @connection.encoding)
|
24
|
-
rescue StandardError
|
25
|
-
col
|
26
|
-
end
|
27
|
-
end
|
28
|
-
result.close
|
29
|
-
ActiveRecord::Result.new(fields, rows)
|
30
|
-
else
|
31
|
-
result
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Rdb
|
4
|
+
module DatabaseStatements # :nodoc:
|
5
|
+
def execute(sql, name = nil)
|
6
|
+
log(sql, name) do
|
7
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
8
|
+
@connection.query(sql)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
|
14
|
+
type_casted_binds = type_casted_binds(binds)
|
15
|
+
|
16
|
+
log(sql, name, binds, type_casted_binds) do
|
17
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
18
|
+
result = @connection.execute(sql, *type_casted_binds)
|
19
|
+
if result.is_a?(Fb::Cursor)
|
20
|
+
fields = result.fields.map(&:name)
|
21
|
+
rows = result.fetchall.map do |row|
|
22
|
+
row.map do |col|
|
23
|
+
col.encode('UTF-8', @connection.encoding)
|
24
|
+
rescue StandardError
|
25
|
+
col
|
26
|
+
end
|
27
|
+
end
|
28
|
+
result.close
|
29
|
+
ActiveRecord::Result.new(fields, rows)
|
30
|
+
else
|
31
|
+
result
|
32
|
+
end
|
33
|
+
rescue => e
|
34
|
+
result.close if result && result.is_a?(Fb::Cursor)
|
35
|
+
raise e
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue => e
|
39
|
+
raise e
|
40
|
+
end
|
41
|
+
|
42
|
+
def explain(arel, binds = [])
|
43
|
+
to_sql(arel, binds)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Begins the transaction (and turns off auto-committing).
|
47
|
+
def begin_db_transaction
|
48
|
+
log('begin transaction', nil) do
|
49
|
+
begin_isolated_db_transaction(default_transaction_isolation)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Default isolation levels for transactions. This method exists
|
54
|
+
# in 4.0.2+, so it's here for backward compatibility with AR 3
|
55
|
+
def transaction_isolation_levels
|
56
|
+
{
|
57
|
+
read_committed: 'READ COMMITTED',
|
58
|
+
repeatable_read: 'REPEATABLE READ',
|
59
|
+
serializable: 'SERIALIZABLE'
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
# Allows providing the :transaction option to ActiveRecord::Base.transaction
|
64
|
+
# in 4.0.2+. Can accept verbatim isolation options like 'WAIT READ COMMITTED'
|
65
|
+
def begin_isolated_db_transaction(isolation)
|
66
|
+
@connection.transaction transaction_isolation_levels.fetch(isolation, isolation)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Commits the transaction (and turns on auto-committing).
|
70
|
+
def commit_db_transaction
|
71
|
+
log('commit transaction', nil) { @connection.commit }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Rolls back the transaction (and turns on auto-committing). Must be
|
75
|
+
# done if the transaction block raises an exception or returns false.
|
76
|
+
def rollback_db_transaction
|
77
|
+
log('rollback transaction', nil) { @connection.rollback }
|
78
|
+
end
|
79
|
+
|
80
|
+
def default_sequence_name(table_name, _column = nil)
|
81
|
+
"#{table_name.to_s.tr('-', '_')[0, table_name_length - 4]}_seq"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Set the sequence to the max value of the table's column.
|
85
|
+
def reset_sequence!(table, column, sequence = nil)
|
86
|
+
sequence ||= default_sequence_name(table, column)
|
87
|
+
max_id = select_value("select max(#{column}) from #{table}")
|
88
|
+
execute("alter sequence #{sequence} restart with #{max_id}")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Uses the raw connection to get the next sequence value.
|
92
|
+
def next_sequence_value(sequence_name)
|
93
|
+
@connection.query("SELECT NEXT VALUE FOR #{sequence_name} FROM RDB$DATABASE")[0][0]
|
94
|
+
end
|
95
|
+
|
96
|
+
def last_inserted_id(_result)
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
101
|
+
fixture_inserts = fixture_set.map do |table_name, fixtures|
|
102
|
+
next if fixtures.empty?
|
103
|
+
|
104
|
+
build_fixture_sql(fixtures, table_name).collect {|f| f }
|
105
|
+
end.compact
|
106
|
+
|
107
|
+
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
|
108
|
+
sql = fixture_inserts.flatten(1)
|
109
|
+
sql.unshift(*table_deletes)
|
110
|
+
|
111
|
+
transaction(requires_new: true) do
|
112
|
+
sql.each do |s|
|
113
|
+
execute s, 'Fixture load'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def default_insert_value(column)
|
121
|
+
if column.default.nil?
|
122
|
+
Arel.sql('NULL')
|
123
|
+
else
|
124
|
+
Arel.sql(quote(column.default))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_fixture_sql(fixtures, table_name)
|
129
|
+
columns = schema_cache.columns_hash(table_name)
|
130
|
+
|
131
|
+
values = fixtures.map do |fixture|
|
132
|
+
fixture = fixture.stringify_keys
|
133
|
+
|
134
|
+
unknown_columns = fixture.keys - columns.keys
|
135
|
+
if unknown_columns.any?
|
136
|
+
raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
|
137
|
+
end
|
138
|
+
|
139
|
+
columns.map do |name, column|
|
140
|
+
if fixture.key?(name)
|
141
|
+
type = lookup_cast_type_from_column(column)
|
142
|
+
bind = Relation::QueryAttribute.new(name, fixture[name], type)
|
143
|
+
with_yaml_fallback(bind.value_for_database)
|
144
|
+
else
|
145
|
+
default_insert_value(column)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
sql ||= []
|
151
|
+
|
152
|
+
values.each_with_index do |row, i|
|
153
|
+
s = ''
|
154
|
+
s << "INSERT INTO #{quote_table_name(table_name)}"
|
155
|
+
|
156
|
+
unless columns.empty?
|
157
|
+
s << ' ('
|
158
|
+
columns.each_with_index do |x, y|
|
159
|
+
s << ', ' unless y == 0
|
160
|
+
s << quote_column_name(x[1].name)
|
161
|
+
end
|
162
|
+
s << ')'
|
163
|
+
end
|
164
|
+
|
165
|
+
s << ' VALUES ('
|
166
|
+
row.each_with_index do |value, k|
|
167
|
+
s << ', ' unless k == 0
|
168
|
+
case value
|
169
|
+
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
170
|
+
s << value.to_s
|
171
|
+
when Time
|
172
|
+
s << quote(value.strftime("%F %T"))
|
173
|
+
else
|
174
|
+
s << quote(value).to_s
|
175
|
+
end
|
176
|
+
end
|
177
|
+
s << ');'
|
178
|
+
sql << s
|
179
|
+
end
|
180
|
+
|
181
|
+
sql
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|