odbc_adapter 4.2.3 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/lib/active_record/connection_adapters/odbc_adapter.rb +6 -6
- data/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb +15 -1
- data/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +4 -4
- data/lib/odbc_adapter/column.rb +2 -17
- data/lib/odbc_adapter/database_statements.rb +17 -36
- data/lib/odbc_adapter/schema_statements.rb +45 -35
- data/lib/odbc_adapter/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a82f638f836342da6b1ef70597ad205ad869e796
|
4
|
+
data.tar.gz: e2f9e57124fa42ebd816ba4e3446d68c2fc06d74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1766ac63ef69f279469a25c47c18495e8e2aac3c9dce4a73c0b2ca73987446fef18fb91492231a8c60dd5b1ca30613744576c6f93ccd6dcb4dc41944bf04fe2
|
7
|
+
data.tar.gz: dc416dc3d4d359f7a6e5e5bac66fec0f08b567ffa576bbf56446d51d20dfa85836236a58cfb05811114a1d56f6fcf63ae43528d4eac4f5f697308fffea881e4a
|
data/Gemfile
CHANGED
@@ -83,7 +83,6 @@ module ActiveRecord
|
|
83
83
|
super(connection, logger)
|
84
84
|
@connection = connection
|
85
85
|
@dbms = dbms
|
86
|
-
@visitor = self.class::BindSubstitution.new(self)
|
87
86
|
end
|
88
87
|
|
89
88
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -127,10 +126,14 @@ module ActiveRecord
|
|
127
126
|
@connection.disconnect if @connection.connected?
|
128
127
|
end
|
129
128
|
|
129
|
+
def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil, native_type = nil)
|
130
|
+
::ODBCAdapter::Column.new(name, default, sql_type_metadata, null, table_name, default_function, collation, native_type)
|
131
|
+
end
|
132
|
+
|
130
133
|
protected
|
131
134
|
|
132
135
|
def initialize_type_map(map)
|
133
|
-
map.register_type
|
136
|
+
map.register_type 'boolean', Type::Boolean.new
|
134
137
|
map.register_type ODBC::SQL_CHAR, Type::String.new
|
135
138
|
map.register_type ODBC::SQL_LONGVARCHAR, Type::Text.new
|
136
139
|
map.register_type ODBC::SQL_TINYINT, Type::Integer.new(limit: 4)
|
@@ -149,6 +152,7 @@ module ActiveRecord
|
|
149
152
|
map.register_type ODBC::SQL_TIMESTAMP, Type::DateTime.new
|
150
153
|
map.register_type ODBC::SQL_GUID, Type::String.new
|
151
154
|
|
155
|
+
alias_type map, ODBC::SQL_BIT, 'boolean'
|
152
156
|
alias_type map, ODBC::SQL_VARCHAR, ODBC::SQL_CHAR
|
153
157
|
alias_type map, ODBC::SQL_WCHAR, ODBC::SQL_CHAR
|
154
158
|
alias_type map, ODBC::SQL_WVARCHAR, ODBC::SQL_CHAR
|
@@ -169,10 +173,6 @@ module ActiveRecord
|
|
169
173
|
end
|
170
174
|
end
|
171
175
|
|
172
|
-
def new_column(name, default, cast_type, sql_type = nil, null = true, native_type = nil, scale = nil, limit = nil)
|
173
|
-
::ODBCAdapter::Column.new(name, default, cast_type, sql_type, null, native_type, scale, limit)
|
174
|
-
end
|
175
|
-
|
176
176
|
private
|
177
177
|
|
178
178
|
def alias_type(map, new_type, old_type)
|
@@ -3,11 +3,25 @@ module ODBCAdapter
|
|
3
3
|
# Overrides specific to MySQL. Mostly taken from
|
4
4
|
# ActiveRecord::ConnectionAdapters::MySQLAdapter
|
5
5
|
class MySQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter
|
6
|
+
PRIMARY_KEY = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY'.freeze
|
7
|
+
|
6
8
|
class BindSubstitution < Arel::Visitors::MySQL
|
7
9
|
include Arel::Visitors::BindVisitor
|
8
10
|
end
|
9
11
|
|
10
|
-
|
12
|
+
def arel_visitor
|
13
|
+
BindSubstitution.new(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Explicitly turning off prepared statements in the MySQL adapter because
|
17
|
+
# of a weird bug with SQLDescribeParam returning a string type for LIMIT
|
18
|
+
# parameters. This is blocking them from running with an error:
|
19
|
+
#
|
20
|
+
# You have an error in your SQL syntax; ...
|
21
|
+
# ... right syntax to use near ''1'' at line 1: ...
|
22
|
+
def prepared_statements
|
23
|
+
false
|
24
|
+
end
|
11
25
|
|
12
26
|
def truncate(table_name, name = nil)
|
13
27
|
execute("TRUNCATE TABLE #{quote_table_name(table_name)}", name)
|
@@ -3,10 +3,6 @@ module ODBCAdapter
|
|
3
3
|
# Overrides specific to PostgreSQL. Mostly taken from
|
4
4
|
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
5
5
|
class PostgreSQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter
|
6
|
-
class BindSubstitution < Arel::Visitors::PostgreSQL
|
7
|
-
include Arel::Visitors::BindVisitor
|
8
|
-
end
|
9
|
-
|
10
6
|
BOOLEAN_TYPE = 'bool'.freeze
|
11
7
|
PRIMARY_KEY = 'SERIAL PRIMARY KEY'.freeze
|
12
8
|
|
@@ -15,6 +11,10 @@ module ODBCAdapter
|
|
15
11
|
@native_database_types ||= super.merge(boolean: { name: 'bool' })
|
16
12
|
end
|
17
13
|
|
14
|
+
def arel_visitor
|
15
|
+
Arel::Visitors::PostgreSQL.new(self)
|
16
|
+
end
|
17
|
+
|
18
18
|
# Filter for ODBCAdapter#tables
|
19
19
|
# Omits table from #tables if table_filter returns true
|
20
20
|
def table_filter(schema_name, table_type)
|
data/lib/odbc_adapter/column.rb
CHANGED
@@ -2,24 +2,9 @@ module ODBCAdapter
|
|
2
2
|
class Column < ActiveRecord::ConnectionAdapters::Column
|
3
3
|
attr_reader :native_type
|
4
4
|
|
5
|
-
def initialize(name, default,
|
6
|
-
|
7
|
-
@default = default
|
8
|
-
@cast_type = cast_type
|
9
|
-
@sql_type = sql_type
|
10
|
-
@null = null
|
5
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, native_type = nil, default_function = nil, collation = nil)
|
6
|
+
super(name, default, sql_type_metadata, null, table_name, default_function, collation)
|
11
7
|
@native_type = native_type
|
12
|
-
|
13
|
-
if [ODBC::SQL_DECIMAL, ODBC::SQL_NUMERIC].include?(sql_type)
|
14
|
-
set_numeric_params(scale, limit)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def set_numeric_params(scale, limit)
|
21
|
-
@cast_type.instance_variable_set(:@scale, scale || 0)
|
22
|
-
@cast_type.instance_variable_set(:@precision, limit)
|
23
8
|
end
|
24
9
|
end
|
25
10
|
end
|
@@ -5,32 +5,30 @@ module ODBCAdapter
|
|
5
5
|
SQL_NULLABLE = 1
|
6
6
|
SQL_NULLABLE_UNKNOWN = 2
|
7
7
|
|
8
|
-
# Returns an array of arrays containing the field values.
|
9
|
-
# Order is the same as that returned by #columns.
|
10
|
-
def select_rows(sql, name = nil)
|
11
|
-
log(sql, name) do
|
12
|
-
stmt = @connection.run(sql)
|
13
|
-
result = stmt.fetch_all
|
14
|
-
stmt.drop
|
15
|
-
result
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
8
|
# Executes the SQL statement in the context of this connection.
|
20
9
|
# Returns the number of rows affected.
|
21
|
-
# TODO: Currently ignoring binds until we can get prepared statements working.
|
22
10
|
def execute(sql, name = nil, binds = [])
|
23
11
|
log(sql, name) do
|
24
|
-
|
12
|
+
if prepared_statements
|
13
|
+
@connection.do(sql, *prepared_binds(binds))
|
14
|
+
else
|
15
|
+
@connection.do(sql)
|
16
|
+
end
|
25
17
|
end
|
26
18
|
end
|
27
19
|
|
28
20
|
# Executes +sql+ statement in the context of this connection using
|
29
21
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
30
22
|
# the executed +sql+ statement.
|
31
|
-
def exec_query(sql, name = 'SQL', binds = [])
|
23
|
+
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
|
32
24
|
log(sql, name) do
|
33
|
-
stmt
|
25
|
+
stmt =
|
26
|
+
if prepared_statements
|
27
|
+
@connection.run(sql, *prepared_binds(binds))
|
28
|
+
else
|
29
|
+
@connection.run(sql)
|
30
|
+
end
|
31
|
+
|
34
32
|
columns = stmt.columns
|
35
33
|
values = stmt.to_a
|
36
34
|
stmt.drop
|
@@ -81,33 +79,12 @@ module ODBCAdapter
|
|
81
79
|
"#{table}_seq"
|
82
80
|
end
|
83
81
|
|
84
|
-
protected
|
85
|
-
|
86
|
-
# Returns the last auto-generated ID from the affected table.
|
87
|
-
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
88
|
-
begin
|
89
|
-
stmt = log(sql, name) { @connection.run(sql) }
|
90
|
-
table = extract_table_ref_from_insert_sql(sql)
|
91
|
-
|
92
|
-
seq = sequence_name || default_sequence_name(table, pk)
|
93
|
-
res = id_value || last_insert_id(table, seq, stmt)
|
94
|
-
ensure
|
95
|
-
stmt.drop unless stmt.nil?
|
96
|
-
end
|
97
|
-
res
|
98
|
-
end
|
99
|
-
|
100
82
|
private
|
101
83
|
|
102
84
|
def dbms_type_cast(columns, values)
|
103
85
|
values
|
104
86
|
end
|
105
87
|
|
106
|
-
def extract_table_ref_from_insert_sql(sql)
|
107
|
-
sql[/into\s+([^\(]*).*values\s*\(/i]
|
108
|
-
$1.strip if $1
|
109
|
-
end
|
110
|
-
|
111
88
|
# Assume received identifier is in DBMS's data dictionary case.
|
112
89
|
def format_case(identifier)
|
113
90
|
case dbms.field_for(ODBC::SQL_IDENTIFIER_CASE)
|
@@ -155,5 +132,9 @@ module ODBCAdapter
|
|
155
132
|
# So force nullability of 'id' columns
|
156
133
|
col_name == 'id' ? false : result
|
157
134
|
end
|
135
|
+
|
136
|
+
def prepared_binds(binds)
|
137
|
+
prepare_binds_for_database(binds).map { |bind| _type_cast(bind) }
|
138
|
+
end
|
158
139
|
end
|
159
140
|
end
|
@@ -7,16 +7,6 @@ module ODBCAdapter
|
|
7
7
|
@native_database_types ||= ColumnMetadata.new(self).native_database_types
|
8
8
|
end
|
9
9
|
|
10
|
-
# Ensure it's shorter than the maximum identifier length for the current dbms
|
11
|
-
def index_name(table_name, options)
|
12
|
-
maximum = dbms.field_for(ODBC::SQL_MAX_IDENTIFIER_LEN) || 255
|
13
|
-
super(table_name, options)[0...maximum]
|
14
|
-
end
|
15
|
-
|
16
|
-
def current_database
|
17
|
-
dbms.field_for(ODBC::SQL_DATABASE_NAME).strip
|
18
|
-
end
|
19
|
-
|
20
10
|
# Returns an array of table names, for database tables visible on the
|
21
11
|
# current connection.
|
22
12
|
def tables(_name = nil)
|
@@ -31,31 +21,9 @@ module ODBCAdapter
|
|
31
21
|
end
|
32
22
|
end
|
33
23
|
|
34
|
-
# Returns an array of
|
35
|
-
def
|
36
|
-
|
37
|
-
result = stmt.fetch_all || []
|
38
|
-
stmt.drop
|
39
|
-
|
40
|
-
result.each_with_object([]) do |col, cols|
|
41
|
-
col_name = col[3] # SQLColumns: COLUMN_NAME
|
42
|
-
col_default = col[12] # SQLColumns: COLUMN_DEF
|
43
|
-
col_sql_type = col[4] # SQLColumns: DATA_TYPE
|
44
|
-
col_native_type = col[5] # SQLColumns: TYPE_NAME
|
45
|
-
col_limit = col[6] # SQLColumns: COLUMN_SIZE
|
46
|
-
col_scale = col[8] # SQLColumns: DECIMAL_DIGITS
|
47
|
-
|
48
|
-
# SQLColumns: IS_NULLABLE, SQLColumns: NULLABLE
|
49
|
-
col_nullable = nullability(col_name, col[17], col[10])
|
50
|
-
|
51
|
-
cast_type =
|
52
|
-
if col_native_type == self.class::BOOLEAN_TYPE
|
53
|
-
ActiveRecord::Type::Boolean.new
|
54
|
-
else
|
55
|
-
lookup_cast_type(col_sql_type)
|
56
|
-
end
|
57
|
-
cols << new_column(format_case(col_name), col_default, cast_type, col_sql_type, col_nullable, col_native_type, col_scale, col_limit)
|
58
|
-
end
|
24
|
+
# Returns an array of view names defined in the database.
|
25
|
+
def views
|
26
|
+
[]
|
59
27
|
end
|
60
28
|
|
61
29
|
# Returns an array of indexes for the given table.
|
@@ -88,6 +56,37 @@ module ODBCAdapter
|
|
88
56
|
end
|
89
57
|
end
|
90
58
|
|
59
|
+
# Returns an array of Column objects for the table specified by
|
60
|
+
# +table_name+.
|
61
|
+
def columns(table_name, name = nil)
|
62
|
+
stmt = @connection.columns(native_case(table_name.to_s))
|
63
|
+
result = stmt.fetch_all || []
|
64
|
+
stmt.drop
|
65
|
+
|
66
|
+
result.each_with_object([]) do |col, cols|
|
67
|
+
col_name = col[3] # SQLColumns: COLUMN_NAME
|
68
|
+
col_default = col[12] # SQLColumns: COLUMN_DEF
|
69
|
+
col_sql_type = col[4] # SQLColumns: DATA_TYPE
|
70
|
+
col_native_type = col[5] # SQLColumns: TYPE_NAME
|
71
|
+
col_limit = col[6] # SQLColumns: COLUMN_SIZE
|
72
|
+
col_scale = col[8] # SQLColumns: DECIMAL_DIGITS
|
73
|
+
|
74
|
+
# SQLColumns: IS_NULLABLE, SQLColumns: NULLABLE
|
75
|
+
col_nullable = nullability(col_name, col[17], col[10])
|
76
|
+
|
77
|
+
args = { sql_type: col_sql_type, type: col_sql_type, limit: col_limit }
|
78
|
+
args[:sql_type] = 'boolean' if col_native_type == self.class::BOOLEAN_TYPE
|
79
|
+
|
80
|
+
if [ODBC::SQL_DECIMAL, ODBC::SQL_NUMERIC].include?(col_sql_type)
|
81
|
+
args[:scale] = col_scale || 0
|
82
|
+
args[:precision] = col_limit
|
83
|
+
end
|
84
|
+
sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**args)
|
85
|
+
|
86
|
+
cols << new_column(format_case(col_name), col_default, sql_type_metadata, col_nullable, table_name, col_native_type)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
91
90
|
# Returns just a table's primary key
|
92
91
|
def primary_key(table_name)
|
93
92
|
stmt = @connection.primary_keys(native_case(table_name.to_s))
|
@@ -95,5 +94,16 @@ module ODBCAdapter
|
|
95
94
|
stmt.drop unless stmt.nil?
|
96
95
|
result[0] && result[0][3]
|
97
96
|
end
|
97
|
+
|
98
|
+
# Ensure it's shorter than the maximum identifier length for the current
|
99
|
+
# dbms
|
100
|
+
def index_name(table_name, options)
|
101
|
+
maximum = dbms.field_for(ODBC::SQL_MAX_IDENTIFIER_LEN) || 255
|
102
|
+
super(table_name, options)[0...maximum]
|
103
|
+
end
|
104
|
+
|
105
|
+
def current_database
|
106
|
+
dbms.field_for(ODBC::SQL_DATABASE_NAME).strip
|
107
|
+
end
|
98
108
|
end
|
99
109
|
end
|
data/lib/odbc_adapter/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: odbc_adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Localytics
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-odbc
|