ar_firebird_adapter 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/active_record/connection_adapters/ar_firebird/connection.rb +18 -0
- data/lib/active_record/connection_adapters/ar_firebird/database_limits.rb +33 -0
- data/lib/active_record/connection_adapters/ar_firebird/database_statements.rb +96 -0
- data/lib/active_record/connection_adapters/ar_firebird/fb_column.rb +32 -0
- data/lib/active_record/connection_adapters/ar_firebird/quoting.rb +35 -0
- data/lib/active_record/connection_adapters/ar_firebird/schema_statements.rb +147 -0
- data/lib/active_record/connection_adapters/ar_firebird/sql_type_metadata.rb +24 -0
- data/lib/active_record/connection_adapters/ar_firebird_adapter.rb +126 -0
- data/lib/active_record/extensions.rb +15 -0
- data/lib/active_record/internal_metadata_extensions.rb +31 -0
- data/lib/arel/visitors/ar_firebird.rb +68 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8ca6d29acdccc19c734c581fd88d10789e7a7e9aa49baba016f68668479d28cb
|
4
|
+
data.tar.gz: 0b12220547231a7bed9a27494309f1dba73f9ae27fde6cb38123906971cbf5f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '051176505698bd4da3565b447b0b13c1f6d228c060095b00e78f0bf73f1593244685230906bb75cdb29f359df273e573eb8889b96ba271974d670d5ed9548e30'
|
7
|
+
data.tar.gz: 07a6000f3fcb3d4cf00f87c1352676ce00b3c5f512ff0ea66b63ac39dc8f6ec004c9d796c7b9836c215f0a6d25d7e79adc578017626548f281dfe510893ab2f5
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveRecord::ConnectionHandling
|
2
|
+
def ar_firebird_connection(config)
|
3
|
+
require 'active_record/extensions'
|
4
|
+
require 'active_record/internal_metadata_extensions'
|
5
|
+
|
6
|
+
config = config.symbolize_keys.dup.reverse_merge(downcase_names: true, port: 3050, encoding: ActiveRecord::ConnectionAdapters::ArFirebirdAdapter::DEFAULT_ENCODING)
|
7
|
+
|
8
|
+
if config[:host]
|
9
|
+
config[:database] = "#{config[:host]}/#{config[:port]}:#{config[:database]}"
|
10
|
+
else
|
11
|
+
config[:database] = File.expand_path(config[:database], Rails.root)
|
12
|
+
end
|
13
|
+
|
14
|
+
connection = ::Fb::Database.new(config).connect
|
15
|
+
|
16
|
+
ActiveRecord::ConnectionAdapters::ArFirebirdAdapter.new(connection, logger, config)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActiveRecord::ConnectionAdapters::ArFirebird
|
2
|
+
module DatabaseLimits
|
3
|
+
|
4
|
+
def table_alias_length
|
5
|
+
31
|
6
|
+
end
|
7
|
+
|
8
|
+
def column_name_length
|
9
|
+
31
|
10
|
+
end
|
11
|
+
|
12
|
+
def table_name_length
|
13
|
+
31
|
14
|
+
end
|
15
|
+
|
16
|
+
def index_name_length
|
17
|
+
31
|
18
|
+
end
|
19
|
+
|
20
|
+
def indexes_per_table
|
21
|
+
65_535
|
22
|
+
end
|
23
|
+
|
24
|
+
def in_clause_length
|
25
|
+
1_499
|
26
|
+
end
|
27
|
+
|
28
|
+
def sql_query_length
|
29
|
+
32_767
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ActiveRecord::ConnectionAdapters::ArFirebird::DatabaseStatements
|
2
|
+
|
3
|
+
delegate :boolean_domain, to: 'ActiveRecord::ConnectionAdapters::ArFirebirdAdapter'
|
4
|
+
|
5
|
+
def execute(sql, name = nil)
|
6
|
+
sql = sql.encode(encoding, 'UTF-8')
|
7
|
+
|
8
|
+
log(sql, name) do
|
9
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
10
|
+
@connection.query(sql)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
|
16
|
+
sql = sql.encode(encoding, 'UTF-8')
|
17
|
+
|
18
|
+
type_casted_binds = type_casted_binds(binds).map do |value|
|
19
|
+
value.encode(encoding) rescue value
|
20
|
+
end
|
21
|
+
|
22
|
+
log(sql, name, binds, type_casted_binds) do
|
23
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
24
|
+
begin
|
25
|
+
result = @connection.execute(sql, *type_casted_binds)
|
26
|
+
if result.is_a?(Fb::Cursor)
|
27
|
+
fields = result.fields.map(&:name)
|
28
|
+
rows = result.fetchall.map do |row|
|
29
|
+
row.map do |col|
|
30
|
+
col.encode('UTF-8', @connection.encoding) rescue col
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
result.close
|
35
|
+
ActiveRecord::Result.new(fields, rows)
|
36
|
+
else
|
37
|
+
result
|
38
|
+
end
|
39
|
+
rescue Exception => e
|
40
|
+
raise e.message.encode('UTF-8', @connection.encoding)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def begin_db_transaction
|
47
|
+
log("begin transaction", nil) { @connection.transaction('READ COMMITTED') }
|
48
|
+
end
|
49
|
+
|
50
|
+
def commit_db_transaction
|
51
|
+
log("commit transaction", nil) { @connection.commit }
|
52
|
+
end
|
53
|
+
|
54
|
+
def exec_rollback_db_transaction
|
55
|
+
log("rollback transaction", nil) { @connection.rollback }
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_table(table_name, **options)
|
59
|
+
super
|
60
|
+
|
61
|
+
if options[:sequence] != false && options[:id] != false
|
62
|
+
sequence_name = options[:sequence] || default_sequence_name(table_name)
|
63
|
+
create_sequence(sequence_name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def drop_table(table_name, options = {})
|
68
|
+
if options[:sequence] != false
|
69
|
+
sequence_name = options[:sequence] || default_sequence_name(table_name)
|
70
|
+
drop_sequence(sequence_name) if sequence_exists?(sequence_name)
|
71
|
+
end
|
72
|
+
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_sequence(sequence_name)
|
77
|
+
execute("CREATE SEQUENCE #{sequence_name}") rescue nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def drop_sequence(sequence_name)
|
81
|
+
execute("DROP SEQUENCE #{sequence_name}") rescue nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def sequence_exists?(sequence_name)
|
85
|
+
@connection.generator_names.include?(sequence_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def default_sequence_name(table_name, _column = nil)
|
89
|
+
"#{table_name}_g01"
|
90
|
+
end
|
91
|
+
|
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
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module ArFirebird
|
4
|
+
class FbColumn < ActiveRecord::ConnectionAdapters::Column # :nodoc:
|
5
|
+
|
6
|
+
attr_reader :domain
|
7
|
+
def initialize(
|
8
|
+
name,
|
9
|
+
default,
|
10
|
+
sql_type_metadata = nil,
|
11
|
+
null = true,
|
12
|
+
table_name = nil,
|
13
|
+
default_function = nil,
|
14
|
+
collation = nil,
|
15
|
+
comment = nil,
|
16
|
+
firebird_options = {}
|
17
|
+
)
|
18
|
+
@domain = firebird_options.domain
|
19
|
+
super(
|
20
|
+
name,
|
21
|
+
default,
|
22
|
+
sql_type_metadata,
|
23
|
+
null,
|
24
|
+
default_function,
|
25
|
+
collation: nil,
|
26
|
+
comment: comment
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module ArFirebird
|
6
|
+
module Quoting
|
7
|
+
|
8
|
+
def unquoted_true
|
9
|
+
boolean_domain[:true]
|
10
|
+
end
|
11
|
+
|
12
|
+
def quoted_true # :nodoc:
|
13
|
+
quote unquoted_true
|
14
|
+
end
|
15
|
+
|
16
|
+
def unquoted_false
|
17
|
+
boolean_domain[:false]
|
18
|
+
end
|
19
|
+
|
20
|
+
def quoted_false # :nodoc:
|
21
|
+
quote unquoted_false
|
22
|
+
end
|
23
|
+
|
24
|
+
def lookup_cast_type_from_column(column) # :nodoc:
|
25
|
+
sql_type = (column.domain == boolean_domain[:name]) ? 'BOOLEAN' : column.sql_type
|
26
|
+
type_map.lookup(sql_type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def quoted_date(value)
|
30
|
+
super.sub(/(\.\d{6})\z/, $1.to_s.first(5))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module ActiveRecord::ConnectionAdapters::ArFirebird::SchemaStatements
|
2
|
+
|
3
|
+
def tables(_name = nil)
|
4
|
+
@connection.table_names
|
5
|
+
end
|
6
|
+
|
7
|
+
def views
|
8
|
+
@connection.view_names
|
9
|
+
end
|
10
|
+
|
11
|
+
def indexes(table_name)
|
12
|
+
result = query(<<~SQL, "SCHEMA")
|
13
|
+
SELECT
|
14
|
+
rdb$index_name,
|
15
|
+
rdb$unique_flag
|
16
|
+
FROM
|
17
|
+
rdb$indices
|
18
|
+
WHERE
|
19
|
+
rdb$relation_name = '#{table_name.upcase}';
|
20
|
+
SQL
|
21
|
+
|
22
|
+
result.map do |row|
|
23
|
+
index_name = row[0].strip
|
24
|
+
unique = row[1] == 1
|
25
|
+
columns = query_values(<<~SQL, "SCHEMA")
|
26
|
+
SELECT
|
27
|
+
rdb$field_name
|
28
|
+
FROM
|
29
|
+
rdb$index_segments
|
30
|
+
WHERE
|
31
|
+
rdb$index_name = '#{index_name}'
|
32
|
+
ORDER BY
|
33
|
+
rdb$field_position
|
34
|
+
SQL
|
35
|
+
|
36
|
+
ActiveRecord::ConnectionAdapters::IndexDefinition.new(
|
37
|
+
table_name.downcase,
|
38
|
+
index_name.downcase,
|
39
|
+
unique,
|
40
|
+
columns.map(&:strip).map(&:downcase),
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove_index(table_name, options = {})
|
46
|
+
index_name = index_name_for_remove(table_name, options)
|
47
|
+
execute "DROP INDEX #{quote_column_name(index_name)}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def foreign_keys(table_name)
|
51
|
+
result = query(<<~SQL, "SCHEMA")
|
52
|
+
WITH FK_FIELDS AS (
|
53
|
+
SELECT
|
54
|
+
AA2.RDB$RELATION_NAME,
|
55
|
+
AA2.RDB$CONSTRAINT_NAME,
|
56
|
+
BB2.RDB$CONST_NAME_UQ AS LINK_UK_OR_PK,
|
57
|
+
EE2.RDB$RELATION_NAME AS REFERENCE_TABLE,
|
58
|
+
CC2.RDB$FIELD_NAME,
|
59
|
+
AA2.RDB$CONSTRAINT_TYPE
|
60
|
+
FROM
|
61
|
+
RDB$RELATION_CONSTRAINTS AA2
|
62
|
+
LEFT JOIN RDB$REF_CONSTRAINTS BB2 ON BB2.RDB$CONSTRAINT_NAME = AA2.RDB$CONSTRAINT_NAME
|
63
|
+
LEFT JOIN RDB$INDEX_SEGMENTS CC2 ON CC2.RDB$INDEX_NAME = AA2.RDB$INDEX_NAME
|
64
|
+
LEFT JOIN RDB$RELATION_FIELDS DD2 ON DD2.RDB$FIELD_NAME = CC2.RDB$FIELD_NAME AND DD2.RDB$RELATION_NAME = AA2.RDB$RELATION_NAME
|
65
|
+
LEFT JOIN RDB$RELATION_CONSTRAINTS EE2 ON EE2.RDB$CONSTRAINT_NAME = BB2.RDB$CONST_NAME_UQ
|
66
|
+
)
|
67
|
+
SELECT
|
68
|
+
AA.RDB$CONSTRAINT_NAME,
|
69
|
+
BB.REFERENCE_TABLE,
|
70
|
+
BB.FIELDS,
|
71
|
+
BB.REFERENCE_FIELDS
|
72
|
+
FROM
|
73
|
+
RDB$RELATION_CONSTRAINTS AA
|
74
|
+
LEFT JOIN (
|
75
|
+
SELECT
|
76
|
+
AA1.RDB$RELATION_NAME,
|
77
|
+
AA1.RDB$CONSTRAINT_NAME,
|
78
|
+
AA1.LINK_UK_OR_PK,
|
79
|
+
AA1.REFERENCE_TABLE,
|
80
|
+
(
|
81
|
+
SELECT LIST(TRIM(AA3.RDB$FIELD_NAME), ', ') FROM FK_FIELDS AA3 WHERE AA3.RDB$CONSTRAINT_NAME = AA1.RDB$CONSTRAINT_NAME ROWS 1
|
82
|
+
) AS FIELDS,
|
83
|
+
(
|
84
|
+
SELECT LIST(TRIM(AA4.RDB$FIELD_NAME), ', ') FROM FK_FIELDS AA4 WHERE AA4.RDB$CONSTRAINT_NAME = AA1.LINK_UK_OR_PK ROWS 1
|
85
|
+
) AS REFERENCE_FIELDS
|
86
|
+
FROM
|
87
|
+
FK_FIELDS AA1
|
88
|
+
GROUP BY
|
89
|
+
AA1.RDB$RELATION_NAME,
|
90
|
+
AA1.RDB$CONSTRAINT_NAME,
|
91
|
+
AA1.REFERENCE_TABLE,
|
92
|
+
AA1.LINK_UK_OR_PK
|
93
|
+
) BB ON BB.RDB$RELATION_NAME = AA.RDB$RELATION_NAME AND BB.RDB$CONSTRAINT_NAME = AA.RDB$CONSTRAINT_NAME
|
94
|
+
WHERE
|
95
|
+
AA.RDB$CONSTRAINT_TYPE = 'FOREIGN KEY'
|
96
|
+
AND AA.RDB$RELATION_NAME = '#{table_name.upcase}';
|
97
|
+
SQL
|
98
|
+
|
99
|
+
result.map do |row|
|
100
|
+
options = {
|
101
|
+
column: row[2].downcase,
|
102
|
+
name: row[0].strip.downcase,
|
103
|
+
primary_key: row[3].downcase
|
104
|
+
}
|
105
|
+
|
106
|
+
ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row[1].strip.downcase, options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def column_definitions(table_name)
|
113
|
+
@connection.columns(table_name)
|
114
|
+
end
|
115
|
+
|
116
|
+
def new_column_from_field(table_name, field)
|
117
|
+
type_metadata = fetch_type_metadata(field["sql_type"], field)
|
118
|
+
ActiveRecord::ConnectionAdapters::ArFirebird::FbColumn.new(
|
119
|
+
field["name"],
|
120
|
+
field["default"],
|
121
|
+
type_metadata,
|
122
|
+
field["nullable"],
|
123
|
+
table_name,
|
124
|
+
nil,
|
125
|
+
nil,
|
126
|
+
nil,
|
127
|
+
field
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
def fetch_type_metadata(sql_type, field = "")
|
132
|
+
if field['domain'] == ActiveRecord::ConnectionAdapters::ArFirebirdAdapter.boolean_domain[:name]
|
133
|
+
cast_type = lookup_cast_type("boolean")
|
134
|
+
else
|
135
|
+
cast_type = lookup_cast_type(sql_type)
|
136
|
+
end
|
137
|
+
ActiveRecord::ConnectionAdapters::ArFirebird::SqlTypeMetadata.new(
|
138
|
+
sql_type: sql_type,
|
139
|
+
type: cast_type.type,
|
140
|
+
precision: cast_type.precision,
|
141
|
+
scale: cast_type.scale,
|
142
|
+
limit: cast_type.limit,
|
143
|
+
field: field
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module ArFirebird
|
4
|
+
class SqlTypeMetadata < ActiveRecord::ConnectionAdapters::SqlTypeMetadata
|
5
|
+
|
6
|
+
def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil, **firebird_options)
|
7
|
+
@sql_type = sql_type
|
8
|
+
@type = (firebird_options[:field].domain) ? :boolean : sql_type
|
9
|
+
@limit = limit
|
10
|
+
@precision = precision
|
11
|
+
@scale = scale
|
12
|
+
@firebird_options = firebird_options
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def attributes_for_hash
|
18
|
+
super + [@firebird_options]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'fb'
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/ar_firebird/connection'
|
4
|
+
require 'active_record/connection_adapters/ar_firebird/database_limits'
|
5
|
+
require 'active_record/connection_adapters/ar_firebird/database_statements'
|
6
|
+
require 'active_record/connection_adapters/ar_firebird/schema_statements'
|
7
|
+
require 'active_record/connection_adapters/ar_firebird/sql_type_metadata'
|
8
|
+
require 'active_record/connection_adapters/ar_firebird/fb_column'
|
9
|
+
require 'active_record/connection_adapters/ar_firebird/quoting'
|
10
|
+
|
11
|
+
require 'arel/visitors/ar_firebird'
|
12
|
+
|
13
|
+
class ActiveRecord::ConnectionAdapters::ArFirebirdAdapter < ActiveRecord::ConnectionAdapters::AbstractAdapter
|
14
|
+
|
15
|
+
ADAPTER_NAME = "ArFirebird".freeze
|
16
|
+
DEFAULT_ENCODING = "Windows-1252".freeze
|
17
|
+
|
18
|
+
include ActiveRecord::ConnectionAdapters::ArFirebird::DatabaseLimits
|
19
|
+
include ActiveRecord::ConnectionAdapters::ArFirebird::DatabaseStatements
|
20
|
+
include ActiveRecord::ConnectionAdapters::ArFirebird::SchemaStatements
|
21
|
+
include ActiveRecord::ConnectionAdapters::ArFirebird::Quoting
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
@boolean_domain = { name: "smallint", limit: 1, type: "smallint", true: 1, false: 0}
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_accessor :boolean_domain
|
29
|
+
end
|
30
|
+
|
31
|
+
NATIVE_DATABASE_TYPES = {
|
32
|
+
primary_key: 'integer not null primary key',
|
33
|
+
string: { name: 'varchar', limit: 255 },
|
34
|
+
text: { name: 'blob sub_type text' },
|
35
|
+
integer: { name: 'integer' },
|
36
|
+
float: { name: 'float' },
|
37
|
+
decimal: { name: 'decimal' },
|
38
|
+
datetime: { name: 'timestamp' },
|
39
|
+
timestamp: { name: 'timestamp' },
|
40
|
+
date: { name: 'date' },
|
41
|
+
binary: { name: 'blob' },
|
42
|
+
boolean: { name: ActiveRecord::ConnectionAdapters::ArFirebirdAdapter.boolean_domain[:name] }
|
43
|
+
}
|
44
|
+
|
45
|
+
def native_database_types
|
46
|
+
NATIVE_DATABASE_TYPES
|
47
|
+
end
|
48
|
+
|
49
|
+
def arel_visitor
|
50
|
+
@arel_visitor ||= Arel::Visitors::ArFirebird.new(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
def prefetch_primary_key?(table_name = nil)
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def active?
|
58
|
+
return false unless @connection.open?
|
59
|
+
|
60
|
+
@connection.query("SELECT 1 FROM RDB$DATABASE")
|
61
|
+
true
|
62
|
+
rescue
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def reconnect!
|
67
|
+
disconnect!
|
68
|
+
@connection = ::Fb::Database.connect(@config)
|
69
|
+
end
|
70
|
+
|
71
|
+
def disconnect!
|
72
|
+
super
|
73
|
+
@connection.close rescue nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def reset!
|
77
|
+
reconnect!
|
78
|
+
end
|
79
|
+
|
80
|
+
def primary_keys(table_name)
|
81
|
+
raise ArgumentError unless table_name.present?
|
82
|
+
|
83
|
+
names = query_values(<<~SQL, "SCHEMA")
|
84
|
+
SELECT
|
85
|
+
s.rdb$field_name
|
86
|
+
FROM
|
87
|
+
rdb$indices i
|
88
|
+
JOIN rdb$index_segments s ON i.rdb$index_name = s.rdb$index_name
|
89
|
+
LEFT JOIN rdb$relation_constraints c ON i.rdb$index_name = c.rdb$index_name
|
90
|
+
WHERE
|
91
|
+
i.rdb$relation_name = '#{table_name.upcase}'
|
92
|
+
AND c.rdb$constraint_type = 'PRIMARY KEY';
|
93
|
+
SQL
|
94
|
+
|
95
|
+
names.map(&:strip).map(&:downcase)
|
96
|
+
end
|
97
|
+
|
98
|
+
def encoding
|
99
|
+
@connection.encoding
|
100
|
+
end
|
101
|
+
|
102
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
|
103
|
+
sql = sql.encode('UTF-8', encoding) if sql.encoding.to_s == encoding
|
104
|
+
super
|
105
|
+
end
|
106
|
+
|
107
|
+
def supports_foreign_keys?
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def translate_exception(e, message)
|
114
|
+
case e.message
|
115
|
+
when /violation of FOREIGN KEY constraint/
|
116
|
+
ActiveRecord::InvalidForeignKey.new(message)
|
117
|
+
when /violation of PRIMARY or UNIQUE KEY constraint/, /attempt to store duplicate value/
|
118
|
+
ActiveRecord::RecordNotUnique.new(message)
|
119
|
+
when /This operation is not defined for system tables/
|
120
|
+
ActiveRecord::ActiveRecordError.new(message)
|
121
|
+
else
|
122
|
+
super
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
ActiveRecord::Calculations.module_eval do
|
2
|
+
def count(column_name = nil)
|
3
|
+
return super() if block_given?
|
4
|
+
calculate(:count, column_name || 1)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class ActiveRecord::ConnectionAdapters::AbstractAdapter
|
9
|
+
def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil)
|
10
|
+
result = from_clause + join_clause + where_clause + having_clause
|
11
|
+
result.unshift(offset) if offset
|
12
|
+
result.unshift(limit) if limit
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end if ActiveRecord::VERSION::STRING < '5.2.0'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ActiveRecord::InternalMetadata
|
2
|
+
class << self
|
3
|
+
def adapter_name
|
4
|
+
connection.adapter_name.downcase.to_sym
|
5
|
+
end
|
6
|
+
|
7
|
+
def value_name
|
8
|
+
adapter_name == :ar_firebird ? :value_ : :value
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
find_or_initialize_by(key: key).update!(value_name => value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
where(key: key).pluck(value_name).first
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_table
|
20
|
+
unless table_exists?
|
21
|
+
key_options = connection.internal_string_options_for_primary_key
|
22
|
+
|
23
|
+
connection.create_table(table_name, id: false) do |t|
|
24
|
+
t.string :key, key_options
|
25
|
+
t.string value_name
|
26
|
+
t.timestamps
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Arel::Visitors::ArFirebird < Arel::Visitors::ToSql
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def visit_Arel_Nodes_SelectStatement o, collector
|
8
|
+
if o.with
|
9
|
+
collector = visit o.with, collector
|
10
|
+
collector << ' '
|
11
|
+
end
|
12
|
+
|
13
|
+
collector = o.cores.inject(collector) do |c, x|
|
14
|
+
visit_Arel_Nodes_SelectCore(x, c, o)
|
15
|
+
end
|
16
|
+
|
17
|
+
unless o.orders.empty?
|
18
|
+
collector << ' ORDER BY '
|
19
|
+
o.orders.each_with_index do |x, i|
|
20
|
+
collector << ', ' unless i == 0
|
21
|
+
collector = visit(x, collector)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
collector
|
26
|
+
end
|
27
|
+
|
28
|
+
def visit_Arel_Nodes_SelectCore(core, collector, o)
|
29
|
+
# We need to use the Arel::Nodes::SelectCore `core`
|
30
|
+
# as well as Arel::Nodes::SelectStatement `o` in
|
31
|
+
# contradiction to the super class because we access
|
32
|
+
# the `visit_Arel_Nodes_SelectOptions` method because
|
33
|
+
# we need to set our limit and offset in the select
|
34
|
+
# clause (Firebird specific SQL)
|
35
|
+
collector << 'SELECT'
|
36
|
+
|
37
|
+
visit_Arel_Nodes_SelectOptions(o, collector)
|
38
|
+
|
39
|
+
collector = maybe_visit core.set_quantifier, collector
|
40
|
+
|
41
|
+
collect_nodes_for core.projections, collector, ' '
|
42
|
+
|
43
|
+
if core.source && !core.source.empty?
|
44
|
+
collector << ' FROM '
|
45
|
+
collector = visit core.source, collector
|
46
|
+
end
|
47
|
+
|
48
|
+
collect_nodes_for core.wheres, collector, ' WHERE ', ' AND '
|
49
|
+
collect_nodes_for core.groups, collector, ' GROUP BY '
|
50
|
+
unless core.havings.empty?
|
51
|
+
collector << ' HAVING '
|
52
|
+
inject_join core.havings, collector, ' AND '
|
53
|
+
end
|
54
|
+
collect_nodes_for core.windows, collector, ' WINDOW '
|
55
|
+
|
56
|
+
collector
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
60
|
+
collector << 'FIRST '
|
61
|
+
visit o.expr, collector
|
62
|
+
end
|
63
|
+
|
64
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
65
|
+
collector << 'SKIP '
|
66
|
+
visit o.expr, collector
|
67
|
+
end
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ar_firebird_adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fábio Rodrigues
|
8
|
+
- Gernot Gradwohl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2019-10-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fb
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.9.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.9.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rails
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 5.2.0
|
35
|
+
- - "<"
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 6.1.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 5.2.0
|
45
|
+
- - "<"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 6.1.0
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: bundler
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.16'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.16'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: database_cleaner
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
type: :development
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.6'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: pry-meta
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.0.10
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.0.10
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: rake
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '10.0'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: rspec
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.7'
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.7'
|
118
|
+
description:
|
119
|
+
email:
|
120
|
+
- grnt.grdwhl@gmail.com
|
121
|
+
executables: []
|
122
|
+
extensions: []
|
123
|
+
extra_rdoc_files: []
|
124
|
+
files:
|
125
|
+
- lib/active_record/connection_adapters/ar_firebird/connection.rb
|
126
|
+
- lib/active_record/connection_adapters/ar_firebird/database_limits.rb
|
127
|
+
- lib/active_record/connection_adapters/ar_firebird/database_statements.rb
|
128
|
+
- lib/active_record/connection_adapters/ar_firebird/fb_column.rb
|
129
|
+
- lib/active_record/connection_adapters/ar_firebird/quoting.rb
|
130
|
+
- lib/active_record/connection_adapters/ar_firebird/schema_statements.rb
|
131
|
+
- lib/active_record/connection_adapters/ar_firebird/sql_type_metadata.rb
|
132
|
+
- lib/active_record/connection_adapters/ar_firebird_adapter.rb
|
133
|
+
- lib/active_record/extensions.rb
|
134
|
+
- lib/active_record/internal_metadata_extensions.rb
|
135
|
+
- lib/arel/visitors/ar_firebird.rb
|
136
|
+
homepage: https://github.com/rails-firebird/ar_firebird_adapter/
|
137
|
+
licenses:
|
138
|
+
- MIT
|
139
|
+
metadata: {}
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - "~>"
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '2.5'
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubygems_version: 3.0.3
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: ActiveRecord Firebird Adapter for Rails 5 and 6.
|
159
|
+
test_files: []
|