sequel 3.21.0 → 3.32.0
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.
- data/CHANGELOG +539 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/advanced_associations.rdoc +17 -0
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +52 -0
- data/lib/sequel/adapters/jdbc/derby.rb +260 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +66 -30
- data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +339 -0
- data/lib/sequel/adapters/mysql.rb +85 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +355 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +328 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +278 -107
- data/lib/sequel/adapters/shared/mysql.rb +203 -65
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +237 -46
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +200 -50
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +68 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +163 -59
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +240 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +59 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +676 -267
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1081 -344
- data/lib/sequel/model/base.rb +420 -133
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +20 -10
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +43 -10
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +68 -7
- data/lib/sequel/plugins/nested_attributes.rb +65 -10
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +135 -30
- data/spec/adapters/mysql_spec.rb +244 -212
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +789 -512
- data/spec/core/dataset_spec.rb +1176 -1061
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +81 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +661 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +500 -67
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +367 -119
- data/spec/integration/prepared_statement_test.rb +222 -125
- data/spec/integration/schema_test.rb +217 -39
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +182 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +955 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +349 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +117 -73
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
Dataset::NON_SQL_OPTIONS << :insert_ignore
|
|
3
|
+
Dataset::NON_SQL_OPTIONS << :update_ignore
|
|
3
4
|
Dataset::NON_SQL_OPTIONS << :on_duplicate_key_update
|
|
4
5
|
|
|
5
6
|
module MySQL
|
|
@@ -83,13 +84,9 @@ module Sequel
|
|
|
83
84
|
@server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
84
85
|
end
|
|
85
86
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# * :server - Set the server to use
|
|
90
|
-
def tables(opts={})
|
|
91
|
-
m = output_identifier_meth
|
|
92
|
-
metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
|
|
87
|
+
# MySQL supports CREATE TABLE IF NOT EXISTS syntax.
|
|
88
|
+
def supports_create_table_if_not_exists?
|
|
89
|
+
true
|
|
93
90
|
end
|
|
94
91
|
|
|
95
92
|
# MySQL supports prepared transactions (two-phase commit) using XA
|
|
@@ -107,6 +104,14 @@ module Sequel
|
|
|
107
104
|
true
|
|
108
105
|
end
|
|
109
106
|
|
|
107
|
+
# Return an array of symbols specifying table names in the current database.
|
|
108
|
+
#
|
|
109
|
+
# Options:
|
|
110
|
+
# * :server - Set the server to use
|
|
111
|
+
def tables(opts={})
|
|
112
|
+
full_tables('BASE TABLE', opts)
|
|
113
|
+
end
|
|
114
|
+
|
|
110
115
|
# Changes the database in use by issuing a USE statement. I would be
|
|
111
116
|
# very careful if I used this.
|
|
112
117
|
def use(db_name)
|
|
@@ -116,6 +121,14 @@ module Sequel
|
|
|
116
121
|
self
|
|
117
122
|
end
|
|
118
123
|
|
|
124
|
+
# Return an array of symbols specifying view names in the current database.
|
|
125
|
+
#
|
|
126
|
+
# Options:
|
|
127
|
+
# * :server - Set the server to use
|
|
128
|
+
def views(opts={})
|
|
129
|
+
full_tables('VIEW', opts)
|
|
130
|
+
end
|
|
131
|
+
|
|
119
132
|
private
|
|
120
133
|
|
|
121
134
|
# Use MySQL specific syntax for rename column, set column type, and
|
|
@@ -142,6 +155,18 @@ module Sequel
|
|
|
142
155
|
"ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
|
|
143
156
|
when :drop_index
|
|
144
157
|
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
|
|
158
|
+
when :drop_constraint
|
|
159
|
+
type = case op[:type]
|
|
160
|
+
when :primary_key
|
|
161
|
+
return "ALTER TABLE #{quote_schema_table(table)} DROP PRIMARY KEY"
|
|
162
|
+
when :foreign_key
|
|
163
|
+
'FOREIGN KEY'
|
|
164
|
+
when :unique
|
|
165
|
+
'INDEX'
|
|
166
|
+
else
|
|
167
|
+
raise(Error, "must specify constraint type via :type=>(:foreign_key|:primary_key|:unique) when dropping constraints on MySQL")
|
|
168
|
+
end
|
|
169
|
+
"ALTER TABLE #{quote_schema_table(table)} DROP #{type} #{quote_identifier(op[:name])}"
|
|
145
170
|
else
|
|
146
171
|
super(table, op)
|
|
147
172
|
end
|
|
@@ -161,9 +186,9 @@ module Sequel
|
|
|
161
186
|
# Use XA START to start a new prepared transaction if the :prepare
|
|
162
187
|
# option is given.
|
|
163
188
|
def begin_transaction(conn, opts={})
|
|
164
|
-
if s = opts[:prepare]
|
|
189
|
+
if (s = opts[:prepare]) && (th = @transactions[conn])[:savepoint_level] == 0
|
|
165
190
|
log_connection_execute(conn, "XA START #{literal(s)}")
|
|
166
|
-
|
|
191
|
+
th[:savepoint_level] += 1
|
|
167
192
|
else
|
|
168
193
|
super
|
|
169
194
|
end
|
|
@@ -184,7 +209,7 @@ module Sequel
|
|
|
184
209
|
# Prepare the XA transaction for a two-phase commit if the
|
|
185
210
|
# :prepare option is given.
|
|
186
211
|
def commit_transaction(conn, opts={})
|
|
187
|
-
if s = opts[:prepare]
|
|
212
|
+
if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
|
|
188
213
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
189
214
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
190
215
|
else
|
|
@@ -205,6 +230,12 @@ module Sequel
|
|
|
205
230
|
"#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
|
|
206
231
|
end
|
|
207
232
|
|
|
233
|
+
# Backbone of the tables and views support using SHOW FULL TABLES.
|
|
234
|
+
def full_tables(type, opts)
|
|
235
|
+
m = output_identifier_meth
|
|
236
|
+
metadata_dataset.with_sql('SHOW FULL TABLES').server(opts[:server]).map{|r| m.call(r.values.first) if r.delete(:Table_type) == type}.compact
|
|
237
|
+
end
|
|
238
|
+
|
|
208
239
|
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
|
209
240
|
def identifier_input_method_default
|
|
210
241
|
nil
|
|
@@ -232,7 +263,7 @@ module Sequel
|
|
|
232
263
|
|
|
233
264
|
# Rollback the currently open XA transaction
|
|
234
265
|
def rollback_transaction(conn, opts={})
|
|
235
|
-
if s = opts[:prepare]
|
|
266
|
+
if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
|
|
236
267
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
237
268
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
238
269
|
log_connection_execute(conn, "XA ROLLBACK #{literal(s)}")
|
|
@@ -248,8 +279,8 @@ module Sequel
|
|
|
248
279
|
|
|
249
280
|
# Use the MySQL specific DESCRIBE syntax to get a table description.
|
|
250
281
|
def schema_parse_table(table_name, opts)
|
|
251
|
-
m = output_identifier_meth
|
|
252
|
-
im = input_identifier_meth
|
|
282
|
+
m = output_identifier_meth(opts[:dataset])
|
|
283
|
+
im = input_identifier_meth(opts[:dataset])
|
|
253
284
|
metadata_dataset.with_sql("DESCRIBE ?", SQL::Identifier.new(im.call(table_name))).map do |row|
|
|
254
285
|
row[:auto_increment] = true if row.delete(:Extra).to_s =~ /auto_increment/io
|
|
255
286
|
row[:allow_null] = row.delete(:Null) == 'YES'
|
|
@@ -281,13 +312,17 @@ module Sequel
|
|
|
281
312
|
# MySQL has both datetime and timestamp classes, most people are going
|
|
282
313
|
# to want datetime
|
|
283
314
|
def type_literal_generic_datetime(column)
|
|
284
|
-
:
|
|
315
|
+
if column[:default] == Sequel::CURRENT_TIMESTAMP
|
|
316
|
+
:timestamp
|
|
317
|
+
else
|
|
318
|
+
:datetime
|
|
319
|
+
end
|
|
285
320
|
end
|
|
286
321
|
|
|
287
322
|
# MySQL has both datetime and timestamp classes, most people are going
|
|
288
323
|
# to want datetime
|
|
289
324
|
def type_literal_generic_time(column)
|
|
290
|
-
column[:only_time] ? :time :
|
|
325
|
+
column[:only_time] ? :time : type_literal_generic_datetime(column)
|
|
291
326
|
end
|
|
292
327
|
|
|
293
328
|
# MySQL doesn't have a true boolean class, so it uses tinyint(1)
|
|
@@ -303,27 +338,71 @@ module Sequel
|
|
|
303
338
|
COMMA_SEPARATOR = ', '.freeze
|
|
304
339
|
FOR_SHARE = ' LOCK IN SHARE MODE'.freeze
|
|
305
340
|
SQL_CALC_FOUND_ROWS = ' SQL_CALC_FOUND_ROWS'.freeze
|
|
306
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from where order limit')
|
|
307
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'ignore into columns values on_duplicate_key_update')
|
|
308
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct calc_found_rows columns from join where group having compounds order limit lock')
|
|
309
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set where order limit')
|
|
341
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from where order limit')
|
|
342
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert ignore into columns values on_duplicate_key_update')
|
|
343
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
|
|
344
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update ignore table set where order limit')
|
|
345
|
+
SPACE = Dataset::SPACE
|
|
346
|
+
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
347
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
348
|
+
NOT_SPACE = Dataset::NOT_SPACE
|
|
349
|
+
FROM = Dataset::FROM
|
|
350
|
+
INSERT = Dataset::INSERT
|
|
351
|
+
COMMA = Dataset::COMMA
|
|
352
|
+
LIMIT = Dataset::LIMIT
|
|
353
|
+
GROUP_BY = Dataset::GROUP_BY
|
|
354
|
+
REGEXP = 'REGEXP'.freeze
|
|
355
|
+
LIKE = 'LIKE'.freeze
|
|
356
|
+
BINARY = 'BINARY '.freeze
|
|
357
|
+
CONCAT = "CONCAT".freeze
|
|
358
|
+
CAST_BITCOMP_OPEN = "CAST(~".freeze
|
|
359
|
+
CAST_BITCOMP_CLOSE = " AS SIGNED INTEGER)".freeze
|
|
360
|
+
STRAIGHT_JOIN = 'STRAIGHT_JOIN'.freeze
|
|
361
|
+
NATURAL_LEFT_JOIN = 'NATURAL LEFT JOIN'.freeze
|
|
362
|
+
BACKTICK = '`'.freeze
|
|
363
|
+
EMPTY_COLUMNS = " ()".freeze
|
|
364
|
+
EMPTY_VALUES = " VALUES ()".freeze
|
|
365
|
+
IGNORE = " IGNORE".freeze
|
|
366
|
+
REPLACE = 'REPLACE'.freeze
|
|
367
|
+
ON_DUPLICATE_KEY_UPDATE = " ON DUPLICATE KEY UPDATE ".freeze
|
|
368
|
+
EQ_VALUES = '=VALUES('.freeze
|
|
369
|
+
EQ = '='.freeze
|
|
370
|
+
WITH_ROLLUP = ' WITH ROLLUP'.freeze
|
|
310
371
|
|
|
311
372
|
# MySQL specific syntax for LIKE/REGEXP searches, as well as
|
|
312
373
|
# string concatenation.
|
|
313
|
-
def
|
|
374
|
+
def complex_expression_sql_append(sql, op, args)
|
|
314
375
|
case op
|
|
376
|
+
when :IN, :"NOT IN"
|
|
377
|
+
ds = args.at(1)
|
|
378
|
+
if ds.is_a?(Sequel::Dataset) && ds.opts[:limit]
|
|
379
|
+
super(sql, op, [args.at(0), ds.from_self])
|
|
380
|
+
else
|
|
381
|
+
super
|
|
382
|
+
end
|
|
315
383
|
when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
|
|
316
|
-
|
|
384
|
+
sql << PAREN_OPEN
|
|
385
|
+
literal_append(sql, args.at(0))
|
|
386
|
+
sql << SPACE
|
|
387
|
+
sql << 'NOT ' if [:'NOT LIKE', :'NOT ILIKE', :'!~', :'!~*'].include?(op)
|
|
388
|
+
sql << ([:~, :'!~', :'~*', :'!~*'].include?(op) ? REGEXP : LIKE)
|
|
389
|
+
sql << SPACE
|
|
390
|
+
sql << BINARY if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)
|
|
391
|
+
literal_append(sql, args.at(1))
|
|
392
|
+
sql << PAREN_CLOSE
|
|
317
393
|
when :'||'
|
|
318
394
|
if args.length > 1
|
|
319
|
-
|
|
395
|
+
sql << CONCAT
|
|
396
|
+
array_sql_append(sql, args)
|
|
320
397
|
else
|
|
321
|
-
|
|
398
|
+
literal_append(sql, args.at(0))
|
|
322
399
|
end
|
|
323
400
|
when :'B~'
|
|
324
|
-
|
|
401
|
+
sql << CAST_BITCOMP_OPEN
|
|
402
|
+
literal_append(sql, args.at(0))
|
|
403
|
+
sql << CAST_BITCOMP_CLOSE
|
|
325
404
|
else
|
|
326
|
-
super
|
|
405
|
+
super
|
|
327
406
|
end
|
|
328
407
|
end
|
|
329
408
|
|
|
@@ -351,8 +430,9 @@ module Sequel
|
|
|
351
430
|
end
|
|
352
431
|
|
|
353
432
|
# MySQL specific full text search syntax.
|
|
354
|
-
def full_text_sql(cols,
|
|
355
|
-
|
|
433
|
+
def full_text_sql(cols, terms, opts = {})
|
|
434
|
+
terms = terms.join(' ') if terms.is_a?(Array)
|
|
435
|
+
SQL::PlaceholderLiteralString.new("MATCH ? AGAINST (?#{" IN BOOLEAN MODE" if opts[:boolean]})", [Array(cols), terms], true)
|
|
356
436
|
end
|
|
357
437
|
|
|
358
438
|
# MySQL allows HAVING clause on ungrouped datasets.
|
|
@@ -372,9 +452,12 @@ module Sequel
|
|
|
372
452
|
# STRAIGHT_JOIN.
|
|
373
453
|
def join_type_sql(join_type)
|
|
374
454
|
case join_type
|
|
375
|
-
when :straight
|
|
376
|
-
|
|
377
|
-
|
|
455
|
+
when :straight
|
|
456
|
+
STRAIGHT_JOIN
|
|
457
|
+
when :natural_inner
|
|
458
|
+
NATURAL_LEFT_JOIN
|
|
459
|
+
else
|
|
460
|
+
super
|
|
378
461
|
end
|
|
379
462
|
end
|
|
380
463
|
|
|
@@ -389,7 +472,7 @@ module Sequel
|
|
|
389
472
|
def insert_ignore
|
|
390
473
|
clone(:insert_ignore=>true)
|
|
391
474
|
end
|
|
392
|
-
|
|
475
|
+
|
|
393
476
|
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
|
394
477
|
# If you pass no arguments, ALL fields will be
|
|
395
478
|
# updated with the new values. If you pass the fields you
|
|
@@ -415,7 +498,9 @@ module Sequel
|
|
|
415
498
|
|
|
416
499
|
# MySQL specific syntax for inserting multiple values at once.
|
|
417
500
|
def multi_insert_sql(columns, values)
|
|
418
|
-
|
|
501
|
+
sql = LiteralString.new('VALUES ')
|
|
502
|
+
expression_list_append(sql, values.map{|r| Array(r)})
|
|
503
|
+
[insert_sql(columns, sql)]
|
|
419
504
|
end
|
|
420
505
|
|
|
421
506
|
# MySQL uses the number of rows actually modified in the update,
|
|
@@ -425,8 +510,8 @@ module Sequel
|
|
|
425
510
|
end
|
|
426
511
|
|
|
427
512
|
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
|
|
428
|
-
def
|
|
429
|
-
|
|
513
|
+
def quoted_identifier_append(sql, c)
|
|
514
|
+
sql << BACKTICK << c.to_s << BACKTICK
|
|
430
515
|
end
|
|
431
516
|
|
|
432
517
|
# MySQL specific syntax for REPLACE (aka UPSERT, or update if exists,
|
|
@@ -441,6 +526,11 @@ module Sequel
|
|
|
441
526
|
true
|
|
442
527
|
end
|
|
443
528
|
|
|
529
|
+
# MySQL supports GROUP BY WITH ROLLUP (but not CUBE)
|
|
530
|
+
def supports_group_rollup?
|
|
531
|
+
true
|
|
532
|
+
end
|
|
533
|
+
|
|
444
534
|
# MySQL does not support INTERSECT or EXCEPT
|
|
445
535
|
def supports_intersect_except?
|
|
446
536
|
false
|
|
@@ -451,6 +541,12 @@ module Sequel
|
|
|
451
541
|
true
|
|
452
542
|
end
|
|
453
543
|
|
|
544
|
+
# MySQL's DISTINCT ON emulation using GROUP BY does not respect the
|
|
545
|
+
# queries ORDER BY clause.
|
|
546
|
+
def supports_ordered_distinct_on?
|
|
547
|
+
false
|
|
548
|
+
end
|
|
549
|
+
|
|
454
550
|
# MySQL does support fractional timestamps in literal timestamps, but it
|
|
455
551
|
# ignores them. Also, using them seems to cause problems on 1.9. Since
|
|
456
552
|
# they are ignored anyway, not using them is probably best.
|
|
@@ -458,13 +554,16 @@ module Sequel
|
|
|
458
554
|
false
|
|
459
555
|
end
|
|
460
556
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
#
|
|
464
|
-
|
|
465
|
-
|
|
557
|
+
# Sets up the update methods to use UPDATE IGNORE.
|
|
558
|
+
# Useful if you have a unique key and want to just skip
|
|
559
|
+
# updating rows that violate the unique key restriction.
|
|
560
|
+
#
|
|
561
|
+
# dataset.update_ignore.update({:name => 'a', :value => 1})
|
|
562
|
+
# # UPDATE IGNORE tablename SET name = 'a', value = 1
|
|
563
|
+
def update_ignore
|
|
564
|
+
clone(:update_ignore=>true)
|
|
466
565
|
end
|
|
467
|
-
|
|
566
|
+
|
|
468
567
|
private
|
|
469
568
|
|
|
470
569
|
# MySQL supports the ORDER BY and LIMIT clauses for DELETE statements
|
|
@@ -476,7 +575,10 @@ module Sequel
|
|
|
476
575
|
# from, but include the others for the purposes of selecting rows.
|
|
477
576
|
def delete_from_sql(sql)
|
|
478
577
|
if joined_dataset?
|
|
479
|
-
sql <<
|
|
578
|
+
sql << SPACE
|
|
579
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
580
|
+
sql << FROM
|
|
581
|
+
source_list_append(sql, @opts[:from])
|
|
480
582
|
select_join_sql(sql)
|
|
481
583
|
else
|
|
482
584
|
super
|
|
@@ -493,7 +595,7 @@ module Sequel
|
|
|
493
595
|
def insert_columns_sql(sql)
|
|
494
596
|
values = opts[:values]
|
|
495
597
|
if values.is_a?(Array) && values.empty?
|
|
496
|
-
sql <<
|
|
598
|
+
sql << EMPTY_COLUMNS
|
|
497
599
|
else
|
|
498
600
|
super
|
|
499
601
|
end
|
|
@@ -501,19 +603,62 @@ module Sequel
|
|
|
501
603
|
|
|
502
604
|
# MySQL supports INSERT IGNORE INTO
|
|
503
605
|
def insert_ignore_sql(sql)
|
|
504
|
-
sql <<
|
|
606
|
+
sql << IGNORE if opts[:insert_ignore]
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
# MySQL supports UPDATE IGNORE
|
|
610
|
+
def update_ignore_sql(sql)
|
|
611
|
+
sql << IGNORE if opts[:update_ignore]
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
# If this is an replace instead of an insert, use replace instead
|
|
615
|
+
def insert_insert_sql(sql)
|
|
616
|
+
sql << (@opts[:replace] ? REPLACE : INSERT)
|
|
505
617
|
end
|
|
506
618
|
|
|
507
619
|
# MySQL supports INSERT ... ON DUPLICATE KEY UPDATE
|
|
508
620
|
def insert_on_duplicate_key_update_sql(sql)
|
|
509
|
-
|
|
621
|
+
if update_cols = opts[:on_duplicate_key_update]
|
|
622
|
+
update_vals = nil
|
|
623
|
+
|
|
624
|
+
if update_cols.empty?
|
|
625
|
+
update_cols = columns
|
|
626
|
+
elsif update_cols.last.is_a?(Hash)
|
|
627
|
+
update_vals = update_cols.last
|
|
628
|
+
update_cols = update_cols[0..-2]
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
sql << ON_DUPLICATE_KEY_UPDATE
|
|
632
|
+
c = false
|
|
633
|
+
co = COMMA
|
|
634
|
+
values = EQ_VALUES
|
|
635
|
+
endp = PAREN_CLOSE
|
|
636
|
+
update_cols.each do |col|
|
|
637
|
+
sql << co if c
|
|
638
|
+
quote_identifier_append(sql, col)
|
|
639
|
+
sql << values
|
|
640
|
+
quote_identifier_append(sql, col)
|
|
641
|
+
sql << endp
|
|
642
|
+
c ||= true
|
|
643
|
+
end
|
|
644
|
+
if update_vals
|
|
645
|
+
eq = EQ
|
|
646
|
+
update_vals.map do |col,v|
|
|
647
|
+
sql << co if c
|
|
648
|
+
quote_identifier_append(sql, col)
|
|
649
|
+
sql << eq
|
|
650
|
+
literal_append(sql, v)
|
|
651
|
+
c ||= true
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
end
|
|
510
655
|
end
|
|
511
656
|
|
|
512
657
|
# MySQL doesn't use the standard DEFAULT VALUES for empty values.
|
|
513
658
|
def insert_values_sql(sql)
|
|
514
659
|
values = opts[:values]
|
|
515
660
|
if values.is_a?(Array) && values.empty?
|
|
516
|
-
sql <<
|
|
661
|
+
sql << EMPTY_VALUES
|
|
517
662
|
else
|
|
518
663
|
super
|
|
519
664
|
end
|
|
@@ -521,7 +666,10 @@ module Sequel
|
|
|
521
666
|
|
|
522
667
|
# MySQL allows a LIMIT in DELETE and UPDATE statements.
|
|
523
668
|
def limit_sql(sql)
|
|
524
|
-
|
|
669
|
+
if l = @opts[:limit]
|
|
670
|
+
sql << LIMIT
|
|
671
|
+
literal_append(sql, @opts[:limit])
|
|
672
|
+
end
|
|
525
673
|
end
|
|
526
674
|
alias delete_limit_sql limit_sql
|
|
527
675
|
alias update_limit_sql limit_sql
|
|
@@ -536,30 +684,20 @@ module Sequel
|
|
|
536
684
|
BOOL_TRUE
|
|
537
685
|
end
|
|
538
686
|
|
|
539
|
-
# MySQL specific syntax for ON DUPLICATE KEY UPDATE
|
|
540
|
-
def on_duplicate_key_update_sql
|
|
541
|
-
if update_cols = opts[:on_duplicate_key_update]
|
|
542
|
-
update_vals = nil
|
|
543
|
-
|
|
544
|
-
if update_cols.empty?
|
|
545
|
-
update_cols = columns
|
|
546
|
-
elsif update_cols.last.is_a?(Hash)
|
|
547
|
-
update_vals = update_cols.last
|
|
548
|
-
update_cols = update_cols[0..-2]
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
updating = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})" }
|
|
552
|
-
updating += update_vals.map{|c,v| "#{quote_identifier(c)}=#{literal(v)}" } if update_vals
|
|
553
|
-
|
|
554
|
-
" ON DUPLICATE KEY UPDATE #{updating.join(COMMA_SEPARATOR)}"
|
|
555
|
-
end
|
|
556
|
-
end
|
|
557
|
-
|
|
558
687
|
# MySQL does not support the SQL WITH clause for SELECT statements
|
|
559
688
|
def select_clause_methods
|
|
560
689
|
SELECT_CLAUSE_METHODS
|
|
561
690
|
end
|
|
562
691
|
|
|
692
|
+
# MySQL supports ROLLUP via nonstandard SQL syntax
|
|
693
|
+
def select_group_sql(sql)
|
|
694
|
+
if group = @opts[:group]
|
|
695
|
+
sql << GROUP_BY
|
|
696
|
+
expression_list_append(sql, group)
|
|
697
|
+
sql << WITH_ROLLUP if @opts[:group_options] == :rollup
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
|
|
563
701
|
# Support FOR SHARE locking when using the :share lock style.
|
|
564
702
|
def select_lock_sql(sql)
|
|
565
703
|
@opts[:lock] == :share ? (sql << FOR_SHARE) : super
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module MySQL
|
|
5
|
+
# This module is used by the mysql and mysql2 adapters to support
|
|
6
|
+
# prepared statements and stored procedures.
|
|
7
|
+
module PreparedStatements
|
|
8
|
+
module DatabaseMethods
|
|
9
|
+
# Support stored procedures on MySQL
|
|
10
|
+
def call_sproc(name, opts={}, &block)
|
|
11
|
+
args = opts[:args] || []
|
|
12
|
+
execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Executes the given SQL using an available connection, yielding the
|
|
16
|
+
# connection if the block is given.
|
|
17
|
+
def execute(sql, opts={}, &block)
|
|
18
|
+
if opts[:sproc]
|
|
19
|
+
call_sproc(sql, opts, &block)
|
|
20
|
+
elsif sql.is_a?(Symbol)
|
|
21
|
+
execute_prepared_statement(sql, opts, &block)
|
|
22
|
+
else
|
|
23
|
+
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def add_prepared_statements_cache(conn)
|
|
30
|
+
class << conn
|
|
31
|
+
attr_accessor :prepared_statements
|
|
32
|
+
end
|
|
33
|
+
conn.prepared_statements = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Executes a prepared statement on an available connection. If the
|
|
37
|
+
# prepared statement already exists for the connection and has the same
|
|
38
|
+
# SQL, reuse it, otherwise, prepare the new statement. Because of the
|
|
39
|
+
# usual MySQL stupidity, we are forced to name arguments via separate
|
|
40
|
+
# SET queries. Use @sequel_arg_N (for N starting at 1) for these
|
|
41
|
+
# arguments.
|
|
42
|
+
def execute_prepared_statement(ps_name, opts, &block)
|
|
43
|
+
args = opts[:arguments]
|
|
44
|
+
ps = prepared_statements[ps_name]
|
|
45
|
+
sql = ps.prepared_sql
|
|
46
|
+
synchronize(opts[:server]) do |conn|
|
|
47
|
+
unless conn.prepared_statements[ps_name] == sql
|
|
48
|
+
conn.prepared_statements[ps_name] = sql
|
|
49
|
+
_execute(conn, "PREPARE #{ps_name} FROM #{literal(sql)}", opts)
|
|
50
|
+
end
|
|
51
|
+
i = 0
|
|
52
|
+
_execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
|
|
53
|
+
_execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
module DatasetMethods
|
|
59
|
+
include Sequel::Dataset::StoredProcedures
|
|
60
|
+
|
|
61
|
+
# Methods to add to MySQL prepared statement calls without using a
|
|
62
|
+
# real database prepared statement and bound variables.
|
|
63
|
+
module CallableStatementMethods
|
|
64
|
+
# Extend given dataset with this module so subselects inside subselects in
|
|
65
|
+
# prepared statements work.
|
|
66
|
+
def subselect_sql_append(sql, ds)
|
|
67
|
+
ps = ds.to_prepared_statement(:select).clone(:append_sql => sql)
|
|
68
|
+
ps.extend(CallableStatementMethods)
|
|
69
|
+
ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
|
|
70
|
+
ps.prepared_args = prepared_args
|
|
71
|
+
ps.prepared_sql
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Methods for MySQL prepared statements using the native driver.
|
|
76
|
+
module PreparedStatementMethods
|
|
77
|
+
include Sequel::Dataset::UnnumberedArgumentMapper
|
|
78
|
+
|
|
79
|
+
# Raise a more obvious error if you attempt to call a unnamed prepared statement.
|
|
80
|
+
def call(*)
|
|
81
|
+
raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# Execute the prepared statement with the bind arguments instead of
|
|
88
|
+
# the given SQL.
|
|
89
|
+
def execute(sql, opts={}, &block)
|
|
90
|
+
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
94
|
+
def execute_dui(sql, opts={}, &block)
|
|
95
|
+
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Methods for MySQL stored procedures using the native driver.
|
|
100
|
+
module StoredProcedureMethods
|
|
101
|
+
include Sequel::Dataset::StoredProcedureMethods
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# Execute the database stored procedure with the stored arguments.
|
|
106
|
+
def execute(sql, opts={}, &block)
|
|
107
|
+
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
111
|
+
def execute_dui(sql, opts={}, &block)
|
|
112
|
+
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# MySQL is different in that it supports prepared statements but not bound
|
|
117
|
+
# variables outside of prepared statements. The default implementation
|
|
118
|
+
# breaks the use of subselects in prepared statements, so extend the
|
|
119
|
+
# temporary prepared statement that this creates with a module that
|
|
120
|
+
# fixes it.
|
|
121
|
+
def call(type, bind_arguments={}, *values, &block)
|
|
122
|
+
ps = to_prepared_statement(type, values)
|
|
123
|
+
ps.extend(CallableStatementMethods)
|
|
124
|
+
ps.call(bind_arguments, &block)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Store the given type of prepared statement in the associated database
|
|
128
|
+
# with the given name.
|
|
129
|
+
def prepare(type, name=nil, *values)
|
|
130
|
+
ps = to_prepared_statement(type, values)
|
|
131
|
+
ps.extend(PreparedStatementMethods)
|
|
132
|
+
if name
|
|
133
|
+
ps.prepared_statement_name = name
|
|
134
|
+
db.prepared_statements[name] = ps
|
|
135
|
+
end
|
|
136
|
+
ps
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
# Extend the dataset with the MySQL stored procedure methods.
|
|
142
|
+
def prepare_extend_sproc(ds)
|
|
143
|
+
ds.extend(StoredProcedureMethods)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|