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_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=, bound_reached?, 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).
164
165
166
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 164
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).
179
180
181
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 179
def apply_limit(sql, limit, offset = 0)
"#{sql}\nLIMIT #{limit} OFFSET #{offset}"
end
|
#begin_transaction ⇒ Object
183
184
185
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 183
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 218
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
187
188
189
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 187
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
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
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 98
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
87
88
89
90
91
92
93
94
95
96
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 87
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.
209
210
211
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 209
def get_database_type
'mysql'
end
|
#last_insert_id ⇒ Object
157
158
159
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 157
def last_insert_id
@last_insert_id
end
|
#placeholder ⇒ Object
168
169
170
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 168
def placeholder
"?"
end
|
#placeholders(count) ⇒ Object
172
173
174
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 172
def placeholders(count)
(["?"] * count).join(", ")
end
|
#rollback ⇒ Object
191
192
193
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 191
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().
198
199
200
201
202
203
204
205
206
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 198
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
213
214
215
216
|
# File 'lib/tina4/drivers/mysql_driver.rb', line 213
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.)