viking-sequel 3.10.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 +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
Sequel.require 'adapters/shared/sqlite'
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module JDBC
|
5
|
+
# Database and Dataset support for SQLite databases accessed via JDBC.
|
6
|
+
module SQLite
|
7
|
+
# Instance methods for SQLite Database objects accessed via JDBC.
|
8
|
+
module DatabaseMethods
|
9
|
+
include Sequel::SQLite::DatabaseMethods
|
10
|
+
|
11
|
+
# Return Sequel::JDBC::SQLite::Dataset object with the given opts.
|
12
|
+
def dataset(opts=nil)
|
13
|
+
Sequel::JDBC::SQLite::Dataset.new(self, opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Use last_insert_rowid() to get the last inserted id.
|
19
|
+
def last_insert_id(conn, opts={})
|
20
|
+
stmt = conn.createStatement
|
21
|
+
begin
|
22
|
+
rs = stmt.executeQuery('SELECT last_insert_rowid()')
|
23
|
+
rs.next
|
24
|
+
rs.getInt(1)
|
25
|
+
ensure
|
26
|
+
stmt.close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Default to a single connection for a memory database.
|
31
|
+
def connection_pool_default_options
|
32
|
+
o = super
|
33
|
+
uri == 'jdbc:sqlite::memory:' ? o.merge(:max_connections=>1) : o
|
34
|
+
end
|
35
|
+
|
36
|
+
# Execute the connection pragmas on the connection.
|
37
|
+
def setup_connection(conn)
|
38
|
+
conn = super(conn)
|
39
|
+
begin
|
40
|
+
stmt = conn.createStatement
|
41
|
+
connection_pragmas.each{|s| log_yield(s){stmt.execute(s)}}
|
42
|
+
ensure
|
43
|
+
stmt.close if stmt
|
44
|
+
end
|
45
|
+
conn
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Dataset class for SQLite datasets accessed via JDBC.
|
50
|
+
class Dataset < JDBC::Dataset
|
51
|
+
include Sequel::SQLite::DatasetMethods
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,421 @@
|
|
1
|
+
begin
|
2
|
+
require "mysqlplus"
|
3
|
+
rescue LoadError
|
4
|
+
require 'mysql'
|
5
|
+
end
|
6
|
+
raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
|
7
|
+
|
8
|
+
Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
|
9
|
+
|
10
|
+
module Sequel
|
11
|
+
# Module for holding all MySQL-related classes and modules for Sequel.
|
12
|
+
module MySQL
|
13
|
+
# Mapping of type numbers to conversion procs
|
14
|
+
MYSQL_TYPES = {}
|
15
|
+
|
16
|
+
# Use only a single proc for each type to save on memory
|
17
|
+
MYSQL_TYPE_PROCS = {
|
18
|
+
[0, 246] => lambda{|v| BigDecimal.new(v)}, # decimal
|
19
|
+
[1] => lambda{|v| convert_tinyint_to_bool ? v.to_i != 0 : v.to_i}, # tinyint
|
20
|
+
[2, 3, 8, 9, 13, 247, 248] => lambda{|v| v.to_i}, # integer
|
21
|
+
[4, 5] => lambda{|v| v.to_f}, # float
|
22
|
+
[10, 14] => lambda{|v| convert_date_time(:string_to_date, v)}, # date
|
23
|
+
[7, 12] => lambda{|v| convert_date_time(:database_to_application_timestamp, v)}, # datetime
|
24
|
+
[11] => lambda{|v| convert_date_time(:string_to_time, v)}, # time
|
25
|
+
[249, 250, 251, 252] => lambda{|v| Sequel::SQL::Blob.new(v)} # blob
|
26
|
+
}
|
27
|
+
MYSQL_TYPE_PROCS.each do |k,v|
|
28
|
+
k.each{|n| MYSQL_TYPES[n] = v}
|
29
|
+
end
|
30
|
+
|
31
|
+
@convert_invalid_date_time = false
|
32
|
+
@convert_tinyint_to_bool = true
|
33
|
+
|
34
|
+
class << self
|
35
|
+
# By default, Sequel raises an exception if in invalid date or time is used.
|
36
|
+
# However, if this is set to nil or :nil, the adapter treats dates
|
37
|
+
# like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
|
38
|
+
# it returns the strings as is.
|
39
|
+
attr_accessor :convert_invalid_date_time
|
40
|
+
|
41
|
+
# Sequel converts the column type tinyint(1) to a boolean by default when
|
42
|
+
# using the native MySQL adapter. You can turn off the conversion by setting
|
43
|
+
# this to false.
|
44
|
+
attr_accessor :convert_tinyint_to_bool
|
45
|
+
end
|
46
|
+
|
47
|
+
# If convert_invalid_date_time is nil, :nil, or :string and
|
48
|
+
# the conversion raises an InvalidValue exception, return v
|
49
|
+
# if :string and nil otherwise.
|
50
|
+
def self.convert_date_time(meth, v)
|
51
|
+
begin
|
52
|
+
Sequel.send(meth, v)
|
53
|
+
rescue InvalidValue
|
54
|
+
case @convert_invalid_date_time
|
55
|
+
when nil, :nil
|
56
|
+
nil
|
57
|
+
when :string
|
58
|
+
v
|
59
|
+
else
|
60
|
+
raise
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Database class for MySQL databases used with Sequel.
|
66
|
+
class Database < Sequel::Database
|
67
|
+
include Sequel::MySQL::DatabaseMethods
|
68
|
+
|
69
|
+
# Mysql::Error messages that indicate the current connection should be disconnected
|
70
|
+
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away)/
|
71
|
+
|
72
|
+
set_adapter_scheme :mysql
|
73
|
+
|
74
|
+
# Support stored procedures on MySQL
|
75
|
+
def call_sproc(name, opts={}, &block)
|
76
|
+
args = opts[:args] || []
|
77
|
+
execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Connect to the database. In addition to the usual database options,
|
81
|
+
# the following options have effect:
|
82
|
+
#
|
83
|
+
# * :auto_is_null - Set to true to use MySQL default behavior of having
|
84
|
+
# a filter for an autoincrement column equals NULL to return the last
|
85
|
+
# inserted row.
|
86
|
+
# * :charset - Same as :encoding (:encoding takes precendence)
|
87
|
+
# * :compress - Set to false to not compress results from the server
|
88
|
+
# * :config_default_group - The default group to read from the in
|
89
|
+
# the MySQL config file.
|
90
|
+
# * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
|
91
|
+
# option on the connection with the given value.
|
92
|
+
# * :encoding - Set all the related character sets for this
|
93
|
+
# connection (connection, client, database, server, and results).
|
94
|
+
# * :socket - Use a unix socket file instead of connecting via TCP/IP.
|
95
|
+
# * :timeout - Set the timeout in seconds before the server will
|
96
|
+
# disconnect this connection.
|
97
|
+
def connect(server)
|
98
|
+
opts = server_opts(server)
|
99
|
+
conn = Mysql.init
|
100
|
+
conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
|
101
|
+
conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
|
102
|
+
if encoding = opts[:encoding] || opts[:charset]
|
103
|
+
# set charset _before_ the connect. using an option instead of "SET (NAMES|CHARACTER_SET_*)" works across reconnects
|
104
|
+
conn.options(Mysql::SET_CHARSET_NAME, encoding)
|
105
|
+
end
|
106
|
+
conn.real_connect(
|
107
|
+
opts[:host] || 'localhost',
|
108
|
+
opts[:user],
|
109
|
+
opts[:password],
|
110
|
+
opts[:database],
|
111
|
+
opts[:port],
|
112
|
+
opts[:socket],
|
113
|
+
Mysql::CLIENT_MULTI_RESULTS +
|
114
|
+
Mysql::CLIENT_MULTI_STATEMENTS +
|
115
|
+
(opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
|
116
|
+
)
|
117
|
+
|
118
|
+
# increase timeout so mysql server doesn't disconnect us
|
119
|
+
conn.query("set @@wait_timeout = #{opts[:timeout] || 2592000}")
|
120
|
+
|
121
|
+
# By default, MySQL 'where id is null' selects the last inserted id
|
122
|
+
conn.query("set SQL_AUTO_IS_NULL=0") unless opts[:auto_is_null]
|
123
|
+
|
124
|
+
class << conn
|
125
|
+
attr_accessor :prepared_statements
|
126
|
+
end
|
127
|
+
conn.prepared_statements = {}
|
128
|
+
conn
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns instance of Sequel::MySQL::Dataset with the given options.
|
132
|
+
def dataset(opts = nil)
|
133
|
+
MySQL::Dataset.new(self, opts)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Executes the given SQL using an available connection, yielding the
|
137
|
+
# connection if the block is given.
|
138
|
+
def execute(sql, opts={}, &block)
|
139
|
+
if opts[:sproc]
|
140
|
+
call_sproc(sql, opts, &block)
|
141
|
+
elsif sql.is_a?(Symbol)
|
142
|
+
execute_prepared_statement(sql, opts, &block)
|
143
|
+
else
|
144
|
+
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Return the version of the MySQL server two which we are connecting.
|
149
|
+
def server_version(server=nil)
|
150
|
+
@server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
# Execute the given SQL on the given connection. If the :type
|
156
|
+
# option is :select, yield the result of the query, otherwise
|
157
|
+
# yield the connection if a block is given.
|
158
|
+
def _execute(conn, sql, opts)
|
159
|
+
begin
|
160
|
+
r = log_yield(sql){conn.query(sql)}
|
161
|
+
if opts[:type] == :select
|
162
|
+
yield r if r
|
163
|
+
elsif block_given?
|
164
|
+
yield conn
|
165
|
+
end
|
166
|
+
if conn.respond_to?(:more_results?)
|
167
|
+
while conn.more_results? do
|
168
|
+
if r
|
169
|
+
r.free
|
170
|
+
r = nil
|
171
|
+
end
|
172
|
+
begin
|
173
|
+
conn.next_result
|
174
|
+
r = conn.use_result
|
175
|
+
rescue Mysql::Error => e
|
176
|
+
raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
|
177
|
+
break
|
178
|
+
end
|
179
|
+
yield r if opts[:type] == :select
|
180
|
+
end
|
181
|
+
end
|
182
|
+
rescue Mysql::Error => e
|
183
|
+
raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
|
184
|
+
ensure
|
185
|
+
r.free if r
|
186
|
+
# Use up all results to avoid a commands out of sync message.
|
187
|
+
if conn.respond_to?(:more_results?)
|
188
|
+
while conn.more_results? do
|
189
|
+
begin
|
190
|
+
conn.next_result
|
191
|
+
r = conn.use_result
|
192
|
+
rescue Mysql::Error => e
|
193
|
+
raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
|
194
|
+
break
|
195
|
+
end
|
196
|
+
r.free if r
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# MySQL connections use the query method to execute SQL without a result
|
203
|
+
def connection_execute_method
|
204
|
+
:query
|
205
|
+
end
|
206
|
+
|
207
|
+
# The MySQL adapter main error class is Mysql::Error
|
208
|
+
def database_error_classes
|
209
|
+
[Mysql::Error]
|
210
|
+
end
|
211
|
+
|
212
|
+
# The database name when using the native adapter is always stored in
|
213
|
+
# the :database option.
|
214
|
+
def database_name
|
215
|
+
@opts[:database]
|
216
|
+
end
|
217
|
+
|
218
|
+
# Closes given database connection.
|
219
|
+
def disconnect_connection(c)
|
220
|
+
c.close
|
221
|
+
end
|
222
|
+
|
223
|
+
# Executes a prepared statement on an available connection. If the
|
224
|
+
# prepared statement already exists for the connection and has the same
|
225
|
+
# SQL, reuse it, otherwise, prepare the new statement. Because of the
|
226
|
+
# usual MySQL stupidity, we are forced to name arguments via separate
|
227
|
+
# SET queries. Use @sequel_arg_N (for N starting at 1) for these
|
228
|
+
# arguments.
|
229
|
+
def execute_prepared_statement(ps_name, opts, &block)
|
230
|
+
args = opts[:arguments]
|
231
|
+
ps = prepared_statements[ps_name]
|
232
|
+
sql = ps.prepared_sql
|
233
|
+
synchronize(opts[:server]) do |conn|
|
234
|
+
unless conn.prepared_statements[ps_name] == sql
|
235
|
+
conn.prepared_statements[ps_name] = sql
|
236
|
+
_execute(conn, "PREPARE #{ps_name} FROM '#{::Mysql.quote(sql)}'", opts)
|
237
|
+
end
|
238
|
+
i = 0
|
239
|
+
_execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
|
240
|
+
_execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
245
|
+
def schema_column_type(db_type)
|
246
|
+
Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Dataset class for MySQL datasets accessed via the native driver.
|
251
|
+
class Dataset < Sequel::Dataset
|
252
|
+
include Sequel::MySQL::DatasetMethods
|
253
|
+
include StoredProcedures
|
254
|
+
|
255
|
+
# Methods to add to MySQL prepared statement calls without using a
|
256
|
+
# real database prepared statement and bound variables.
|
257
|
+
module CallableStatementMethods
|
258
|
+
# Extend given dataset with this module so subselects inside subselects in
|
259
|
+
# prepared statements work.
|
260
|
+
def subselect_sql(ds)
|
261
|
+
ps = ds.to_prepared_statement(:select)
|
262
|
+
ps.extend(CallableStatementMethods)
|
263
|
+
ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
|
264
|
+
ps.prepared_args = prepared_args
|
265
|
+
ps.prepared_sql
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Methods for MySQL prepared statements using the native driver.
|
270
|
+
module PreparedStatementMethods
|
271
|
+
include Sequel::Dataset::UnnumberedArgumentMapper
|
272
|
+
|
273
|
+
private
|
274
|
+
|
275
|
+
# Execute the prepared statement with the bind arguments instead of
|
276
|
+
# the given SQL.
|
277
|
+
def execute(sql, opts={}, &block)
|
278
|
+
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
279
|
+
end
|
280
|
+
|
281
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
282
|
+
def execute_dui(sql, opts={}, &block)
|
283
|
+
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Methods for MySQL stored procedures using the native driver.
|
288
|
+
module StoredProcedureMethods
|
289
|
+
include Sequel::Dataset::StoredProcedureMethods
|
290
|
+
|
291
|
+
private
|
292
|
+
|
293
|
+
# Execute the database stored procedure with the stored arguments.
|
294
|
+
def execute(sql, opts={}, &block)
|
295
|
+
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
299
|
+
def execute_dui(sql, opts={}, &block)
|
300
|
+
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# MySQL is different in that it supports prepared statements but not bound
|
305
|
+
# variables outside of prepared statements. The default implementation
|
306
|
+
# breaks the use of subselects in prepared statements, so extend the
|
307
|
+
# temporary prepared statement that this creates with a module that
|
308
|
+
# fixes it.
|
309
|
+
def call(type, bind_arguments={}, *values, &block)
|
310
|
+
ps = to_prepared_statement(type, values)
|
311
|
+
ps.extend(CallableStatementMethods)
|
312
|
+
ps.call(bind_arguments, &block)
|
313
|
+
end
|
314
|
+
|
315
|
+
# Delete rows matching this dataset
|
316
|
+
def delete
|
317
|
+
execute_dui(delete_sql){|c| return c.affected_rows}
|
318
|
+
end
|
319
|
+
|
320
|
+
# Yield all rows matching this dataset. If the dataset is set to
|
321
|
+
# split multiple statements, yield arrays of hashes one per statement
|
322
|
+
# instead of yielding results for all statements as hashes.
|
323
|
+
def fetch_rows(sql, &block)
|
324
|
+
execute(sql) do |r|
|
325
|
+
i = -1
|
326
|
+
cols = r.fetch_fields.map{|f| [output_identifier(f.name), MYSQL_TYPES[f.type], i+=1]}
|
327
|
+
@columns = cols.map{|c| c.first}
|
328
|
+
if opts[:split_multiple_result_sets]
|
329
|
+
s = []
|
330
|
+
yield_rows(r, cols){|h| s << h}
|
331
|
+
yield s
|
332
|
+
else
|
333
|
+
yield_rows(r, cols, &block)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
self
|
337
|
+
end
|
338
|
+
|
339
|
+
# Don't allow graphing a dataset that splits multiple statements
|
340
|
+
def graph(*)
|
341
|
+
raise(Error, "Can't graph a dataset that splits multiple result sets") if opts[:split_multiple_result_sets]
|
342
|
+
super
|
343
|
+
end
|
344
|
+
|
345
|
+
# Insert a new value into this dataset
|
346
|
+
def insert(*values)
|
347
|
+
execute_dui(insert_sql(*values)){|c| return c.insert_id}
|
348
|
+
end
|
349
|
+
|
350
|
+
# Store the given type of prepared statement in the associated database
|
351
|
+
# with the given name.
|
352
|
+
def prepare(type, name=nil, *values)
|
353
|
+
ps = to_prepared_statement(type, values)
|
354
|
+
ps.extend(PreparedStatementMethods)
|
355
|
+
if name
|
356
|
+
ps.prepared_statement_name = name
|
357
|
+
db.prepared_statements[name] = ps
|
358
|
+
end
|
359
|
+
ps
|
360
|
+
end
|
361
|
+
|
362
|
+
# Replace (update or insert) the matching row.
|
363
|
+
def replace(*args)
|
364
|
+
execute_dui(replace_sql(*args)){|c| return c.insert_id}
|
365
|
+
end
|
366
|
+
|
367
|
+
# Makes each yield arrays of rows, with each array containing the rows
|
368
|
+
# for a given result set. Does not work with graphing. So you can submit
|
369
|
+
# SQL with multiple statements and easily determine which statement
|
370
|
+
# returned which results.
|
371
|
+
#
|
372
|
+
# Modifies the row_proc of the returned dataset so that it still works
|
373
|
+
# as expected (running on the hashes instead of on the arrays of hashes).
|
374
|
+
# If you modify the row_proc afterward, note that it will receive an array
|
375
|
+
# of hashes instead of a hash.
|
376
|
+
def split_multiple_result_sets
|
377
|
+
raise(Error, "Can't split multiple statements on a graphed dataset") if opts[:graph]
|
378
|
+
ds = clone(:split_multiple_result_sets=>true)
|
379
|
+
ds.row_proc = proc{|x| x.map{|h| row_proc.call(h)}} if row_proc
|
380
|
+
ds
|
381
|
+
end
|
382
|
+
|
383
|
+
# Update the matching rows.
|
384
|
+
def update(values={})
|
385
|
+
execute_dui(update_sql(values)){|c| return c.affected_rows}
|
386
|
+
end
|
387
|
+
|
388
|
+
private
|
389
|
+
|
390
|
+
# Set the :type option to :select if it hasn't been set.
|
391
|
+
def execute(sql, opts={}, &block)
|
392
|
+
super(sql, {:type=>:select}.merge(opts), &block)
|
393
|
+
end
|
394
|
+
|
395
|
+
# Set the :type option to :dui if it hasn't been set.
|
396
|
+
def execute_dui(sql, opts={}, &block)
|
397
|
+
super(sql, {:type=>:dui}.merge(opts), &block)
|
398
|
+
end
|
399
|
+
|
400
|
+
# Handle correct quoting of strings using ::MySQL.quote.
|
401
|
+
def literal_string(v)
|
402
|
+
"'#{::Mysql.quote(v)}'"
|
403
|
+
end
|
404
|
+
|
405
|
+
# Extend the dataset with the MySQL stored procedure methods.
|
406
|
+
def prepare_extend_sproc(ds)
|
407
|
+
ds.extend(StoredProcedureMethods)
|
408
|
+
end
|
409
|
+
|
410
|
+
# Yield each row of the given result set r with columns cols
|
411
|
+
# as a hash with symbol keys
|
412
|
+
def yield_rows(r, cols)
|
413
|
+
while row = r.fetch_row
|
414
|
+
h = {}
|
415
|
+
cols.each{|n, p, i| v = row[i]; h[n] = (v && p) ? p.call(v) : v}
|
416
|
+
yield h
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|