Class: Tina4::Drivers::MysqlDriver
Constant Summary
collapse
- WRITE_VERBS =
First SIX characters of the statements whose row count #affected_rows
reports. "REPLAC" is REPLACE truncated to the same width.
%w[INSERT UPDATE DELETE REPLAC].freeze
Tina4::DatabaseAdapter::ABSTRACT_CONTRACT, Tina4::DatabaseAdapter::CONNECT_TIMEOUT_SLACK_SECONDS, Tina4::DatabaseAdapter::CONNECT_TIMEOUT_VAR, Tina4::DatabaseAdapter::CONTRACT, Tina4::DatabaseAdapter::DEFAULT_CONNECT_TIMEOUT_SECONDS
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#affected_rows ⇒ Object
Rows changed by the most recent INSERT/UPDATE/DELETE on this connection.
-
#apply_limit(sql, limit, offset = 0) ⇒ Object
NEW LINE, not a space — appended inline the clause lands inside a trailing -- comment and the engine ignores it (see the note on Drivers::SqliteDriver#apply_limit).
-
#begin_transaction ⇒ Object
-
#close ⇒ Object
-
#columns(table_name) ⇒ Object
-
#commit ⇒ Object
-
#connect(connection_string, username: nil, password: nil) ⇒ Object
-
#count_subquery_alias ⇒ Object
Postgres, MySQL, MSSQL and ODBC all REQUIRE a name for a derived table, so the COUNT probe in Database#count_probe wraps as FROM (sql) AS _count_query.
-
#execute(sql, params = []) ⇒ Object
-
#execute_query(sql, params = []) ⇒ Object
-
#get_database_type ⇒ Object
ADR-0044 required adapter capability.
-
#last_insert_id ⇒ Object
-
#placeholder ⇒ Object
-
#placeholders(count) ⇒ Object
-
#rollback ⇒ Object
-
#table_exists?(name) ⇒ Boolean
v3.13.14 (#48): MySQL's "schema" is the database.
-
#tables ⇒ Object
-
#translate_dialect(sql) ⇒ Object
Apply the MySQL dialect rewrites the translator owns: || -> CONCAT and ILIKE -> LOWER() LIKE LOWER() (both literal-safe).
#split_schema
#autocommit, #autocommit=, bounding_connect, connect_timed_out!, connect_timeout_seconds, connect_timeout_whole_seconds, #execute_many, #fetch, #fetch_one, implemented_by?, #open, #start_transaction, #supports_atomic_batch, #supports_atomic_batch=, validate!
Instance Attribute Details
#connection ⇒ Object
Returns the value of attribute connection.
23
24
25
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 23
def connection
@connection
end
|
Instance Method Details
#affected_rows ⇒ Object
Rows changed by the most recent INSERT/UPDATE/DELETE on this connection.
Parity with SQLite (connection.changes), PostgreSQL (cmd_tuples), MSSQL
(TinyTds::Result#do) and the Python master (cursor.rowcount).
156
157
158
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 156
def affected_rows
@affected_rows.to_i
end
|
#apply_limit(sql, limit, offset = 0) ⇒ Object
NEW LINE, not a space — appended inline the clause lands inside a
trailing -- comment and the engine ignores it (see the note on
Drivers::SqliteDriver#apply_limit).
171
172
173
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 171
def apply_limit(sql, limit, offset = 0)
"#{sql}\nLIMIT #{limit} OFFSET #{offset}"
end
|
#begin_transaction ⇒ Object
175
176
177
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 175
def begin_transaction
@connection.query("START TRANSACTION")
end
|
#close ⇒ Object
64
65
66
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 64
def close
@connection&.close
end
|
#columns(table_name) ⇒ Object
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 210
def columns(table_name)
rows = execute_query("DESCRIBE #{quote_mysql_identifier(table_name)}")
rows.map do |r|
{
name: r[:Field],
type: r[:Type],
nullable: r[:Null] == "YES",
default: r[:Default],
primary_key: r[:Key] == "PRI"
}
end
end
|
#commit ⇒ Object
179
180
181
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 179
def commit
@connection.query("COMMIT")
end
|
#connect(connection_string, username: nil, password: nil) ⇒ Object
25
26
27
28
29
30
31
32
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
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 25
def connect(connection_string, username: nil, password: nil)
begin
require "mysql2"
rescue LoadError
raise LoadError,
"The 'mysql2' gem is required for MySQL connections. Install one of:\n" \
" bundle add mysql2 # if your project uses Bundler\n" \
" gem install mysql2 # bare driver"
end
uri = URI.parse(connection_string)
host = uri.host || "127.0.0.1"
host = "127.0.0.1" if host == "localhost" && uri.port
options = {
host: host,
port: uri.port || 3306,
username: username || uri.user,
password: password || uri.password,
database: uri.path&.sub("/", "")
}
seconds = Tina4::DatabaseAdapter.connect_timeout_whole_seconds
options[:connect_timeout] = seconds if seconds
Tina4::DatabaseAdapter.bounding_connect(options[:host], options[:port]) do
@connection = Mysql2::Client.new(**options)
end
end
|
#count_subquery_alias ⇒ Object
Postgres, MySQL, MSSQL and ODBC all REQUIRE a name for a derived
table, so the COUNT probe in Database#count_probe wraps as
FROM (sql) AS _count_query. SQLite and Firebird do not define
this and get no alias - Firebird rejects AS in that position.
13
14
15
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 13
def count_subquery_alias
"_count_query"
end
|
#execute(sql, params = []) ⇒ Object
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
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 90
def execute(sql, params = [])
sql = translate_dialect(sql)
stmt = nil
result =
if params.empty?
@connection.query(sql)
else
stmt = @connection.prepare(sql)
stmt.execute(*params)
end
if WRITE_VERBS.include?(sql.to_s.lstrip[0, 6].upcase)
@affected_rows = stmt ? stmt.affected_rows.to_i : @connection.affected_rows.to_i
end
if sql.to_s.lstrip[0, 6].casecmp?("INSERT")
first_id = @connection.last_id
@last_insert_id =
if first_id.to_i.positive?
Tina4::SQLTranslator.batch_last_id(first_id, @affected_rows.to_i, "mysql")
else
first_id
end
end
result
end
|
#execute_query(sql, params = []) ⇒ Object
79
80
81
82
83
84
85
86
87
88
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 79
def execute_query(sql, params = [])
sql = translate_dialect(sql)
if params.empty?
results = @connection.query(sql, symbolize_keys: true)
else
stmt = @connection.prepare(sql)
results = stmt.execute(*params, symbolize_keys: true)
end
results.to_a
end
|
#get_database_type ⇒ Object
ADR-0044 required adapter capability.
201
202
203
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 201
def get_database_type
'mysql'
end
|
#last_insert_id ⇒ Object
149
150
151
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 149
def last_insert_id
@last_insert_id
end
|
#placeholder ⇒ Object
160
161
162
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 160
def placeholder
"?"
end
|
#placeholders(count) ⇒ Object
164
165
166
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 164
def placeholders(count)
(["?"] * count).join(", ")
end
|
#rollback ⇒ Object
183
184
185
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 183
def rollback
@connection.query("ROLLBACK")
end
|
#table_exists?(name) ⇒ Boolean
v3.13.14 (#48): MySQL's "schema" is the database. A qualified name
("otherdb.table") checks that catalog; a bare name defaults to the
connection's current database via DATABASE().
190
191
192
193
194
195
196
197
198
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 190
def table_exists?(name)
schema, tbl = split_schema(name)
rows = execute_query(
"SELECT 1 FROM information_schema.tables " \
"WHERE table_schema = COALESCE(?, DATABASE()) AND table_name = ?",
[schema, tbl]
)
!rows.empty?
end
|
#tables ⇒ Object
205
206
207
208
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 205
def tables
rows = execute_query("SHOW TABLES")
rows.map { |r| r.values.first }
end
|
#translate_dialect(sql) ⇒ Object
Apply the MySQL dialect rewrites the translator owns: || -> CONCAT and
ILIKE -> LOWER() LIKE LOWER() (both literal-safe). MySQL reads a bare ||
as logical OR and has no ILIKE, so portable canonical SQL must be rewritten
before it reaches the driver. A statement with neither token short-circuits
unchanged, so ordinary queries are untouched. (SQLTRANS-DEC-02: this is the
wiring that makes the previously-dead concat/ilike helpers live in Ruby.)