sequel 3.10.0 → 3.11.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 +68 -0
- data/COPYING +1 -1
- data/README.rdoc +87 -27
- data/bin/sequel +2 -4
- data/doc/association_basics.rdoc +1383 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/opening_databases.rdoc +45 -16
- data/doc/querying.rdoc +210 -0
- data/doc/release_notes/3.11.0.txt +254 -0
- data/doc/virtual_rows.rdoc +217 -31
- data/lib/sequel/adapters/ado.rb +28 -12
- data/lib/sequel/adapters/ado/mssql.rb +33 -1
- data/lib/sequel/adapters/amalgalite.rb +13 -8
- data/lib/sequel/adapters/db2.rb +1 -2
- data/lib/sequel/adapters/dbi.rb +7 -4
- data/lib/sequel/adapters/do.rb +14 -15
- data/lib/sequel/adapters/do/postgres.rb +4 -5
- data/lib/sequel/adapters/do/sqlite.rb +9 -0
- data/lib/sequel/adapters/firebird.rb +5 -10
- data/lib/sequel/adapters/informix.rb +2 -4
- data/lib/sequel/adapters/jdbc.rb +111 -49
- data/lib/sequel/adapters/jdbc/mssql.rb +1 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +4 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +8 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +12 -0
- data/lib/sequel/adapters/mysql.rb +14 -5
- data/lib/sequel/adapters/odbc.rb +2 -4
- data/lib/sequel/adapters/odbc/mssql.rb +2 -4
- data/lib/sequel/adapters/openbase.rb +1 -2
- data/lib/sequel/adapters/oracle.rb +4 -8
- data/lib/sequel/adapters/postgres.rb +4 -11
- data/lib/sequel/adapters/shared/mssql.rb +22 -9
- data/lib/sequel/adapters/shared/mysql.rb +33 -30
- data/lib/sequel/adapters/shared/oracle.rb +0 -5
- data/lib/sequel/adapters/shared/postgres.rb +13 -11
- data/lib/sequel/adapters/shared/sqlite.rb +56 -10
- data/lib/sequel/adapters/sqlite.rb +16 -9
- data/lib/sequel/connection_pool.rb +6 -1
- data/lib/sequel/connection_pool/single.rb +1 -0
- data/lib/sequel/core.rb +6 -1
- data/lib/sequel/database.rb +52 -23
- data/lib/sequel/database/schema_generator.rb +6 -0
- data/lib/sequel/database/schema_methods.rb +5 -5
- data/lib/sequel/database/schema_sql.rb +1 -1
- data/lib/sequel/dataset.rb +4 -190
- data/lib/sequel/dataset/actions.rb +323 -1
- data/lib/sequel/dataset/features.rb +18 -2
- data/lib/sequel/dataset/graph.rb +7 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +6 -0
- data/lib/sequel/dataset/query.rb +272 -6
- data/lib/sequel/dataset/sql.rb +186 -394
- data/lib/sequel/model.rb +4 -2
- data/lib/sequel/model/associations.rb +31 -14
- data/lib/sequel/model/base.rb +32 -13
- data/lib/sequel/model/exceptions.rb +8 -4
- data/lib/sequel/model/plugins.rb +3 -13
- data/lib/sequel/plugins/active_model.rb +26 -7
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/optimistic_locking.rb +25 -9
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +26 -0
- data/spec/adapters/mysql_spec.rb +33 -4
- data/spec/adapters/postgres_spec.rb +24 -1
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +28 -0
- data/spec/core/connection_pool_spec.rb +17 -5
- data/spec/core/database_spec.rb +101 -1
- data/spec/core/dataset_spec.rb +42 -4
- data/spec/core/schema_spec.rb +13 -0
- data/spec/extensions/active_model_spec.rb +34 -11
- data/spec/extensions/caching_spec.rb +2 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/spec_helper.rb +2 -0
- data/spec/integration/dataset_test.rb +12 -1
- data/spec/integration/model_test.rb +12 -0
- data/spec/integration/plugin_test.rb +61 -1
- data/spec/integration/schema_test.rb +14 -3
- data/spec/model/base_spec.rb +27 -0
- data/spec/model/plugins_spec.rb +0 -22
- data/spec/model/record_spec.rb +32 -1
- data/spec/model/spec_helper.rb +2 -0
- metadata +14 -3
- data/lib/sequel/dataset/convenience.rb +0 -326
@@ -28,8 +28,7 @@ module Sequel
|
|
28
28
|
stmt = conn.createStatement
|
29
29
|
begin
|
30
30
|
sql = opts[:prepared] ? 'SELECT @@IDENTITY' : 'SELECT SCOPE_IDENTITY()'
|
31
|
-
|
32
|
-
rs = stmt.executeQuery(sql)
|
31
|
+
rs = log_yield(sql){stmt.executeQuery(sql)}
|
33
32
|
rs.next
|
34
33
|
rs.getInt(1)
|
35
34
|
ensure
|
@@ -48,6 +48,12 @@ module Sequel
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
# MySQL 5.1.12 JDBC adapter requires this to be true,
|
53
|
+
# and previous versions don't mind.
|
54
|
+
def requires_return_generated_keys?
|
55
|
+
true
|
56
|
+
end
|
51
57
|
end
|
52
58
|
|
53
59
|
# Dataset class for MySQL datasets accessed via JDBC.
|
@@ -63,6 +69,11 @@ module Sequel
|
|
63
69
|
def replace(*args)
|
64
70
|
execute_insert(replace_sql(*args))
|
65
71
|
end
|
72
|
+
|
73
|
+
# MySQL on JDBC does provides an accurate number of rows matched.
|
74
|
+
def provides_accurate_rows_matched?
|
75
|
+
true
|
76
|
+
end
|
66
77
|
end
|
67
78
|
end
|
68
79
|
end
|
@@ -20,27 +20,24 @@ module Sequel
|
|
20
20
|
|
21
21
|
# Use JDBC connection's setAutoCommit to false to start transactions
|
22
22
|
def begin_transaction(conn)
|
23
|
-
|
24
|
-
conn.setAutoCommit(false)
|
23
|
+
log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
|
25
24
|
conn
|
26
25
|
end
|
27
26
|
|
28
27
|
# Use JDBC connection's commit method to commit transactions
|
29
28
|
def commit_transaction(conn)
|
30
|
-
|
31
|
-
conn.commit
|
29
|
+
log_yield(TRANSACTION_COMMIT){conn.commit}
|
32
30
|
end
|
33
31
|
|
34
32
|
# Use JDBC connection's setAutoCommit to true to enable non-transactional behavior
|
35
33
|
def remove_transaction(conn)
|
36
34
|
conn.setAutoCommit(true) if conn
|
37
|
-
|
35
|
+
@transactions.delete(Thread.current)
|
38
36
|
end
|
39
37
|
|
40
38
|
# Use JDBC connection's rollback method to rollback transactions
|
41
39
|
def rollback_transaction(conn)
|
42
|
-
|
43
|
-
conn.rollback
|
40
|
+
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
44
41
|
end
|
45
42
|
end
|
46
43
|
|
@@ -18,7 +18,7 @@ module Sequel
|
|
18
18
|
method = block_given? ? :executeQuery : :execute
|
19
19
|
stmt = createStatement
|
20
20
|
begin
|
21
|
-
rows = stmt.send(method, sql)
|
21
|
+
rows = @db.log_yield(sql){stmt.send(method, sql)}
|
22
22
|
yield(rows) if block_given?
|
23
23
|
rescue NativeException => e
|
24
24
|
raise_error(e)
|
@@ -78,6 +78,13 @@ module Sequel
|
|
78
78
|
def last_insert_id(conn, opts)
|
79
79
|
insert_result(conn, opts[:table], opts[:values])
|
80
80
|
end
|
81
|
+
|
82
|
+
# Override shared postgresql adapter method to actually log,
|
83
|
+
# since on JDBC the first argument is a statement and not a
|
84
|
+
# connection, so it wouldn't be logged otherwise.
|
85
|
+
def log_connection_execute(stmt, sql)
|
86
|
+
log_yield(sql){stmt.execute(sql)}
|
87
|
+
end
|
81
88
|
end
|
82
89
|
|
83
90
|
# Dataset subclass used for datasets that connect to PostgreSQL via JDBC.
|
@@ -32,6 +32,18 @@ module Sequel
|
|
32
32
|
o = super
|
33
33
|
uri == 'jdbc:sqlite::memory:' ? o.merge(:max_connections=>1) : o
|
34
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
|
35
47
|
end
|
36
48
|
|
37
49
|
# Dataset class for SQLite datasets accessed via JDBC.
|
@@ -85,6 +85,10 @@ module Sequel
|
|
85
85
|
# inserted row.
|
86
86
|
# * :charset - Same as :encoding (:encoding takes precendence)
|
87
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.
|
88
92
|
# * :encoding - Set all the related character sets for this
|
89
93
|
# connection (connection, client, database, server, and results).
|
90
94
|
# * :socket - Use a unix socket file instead of connecting via TCP/IP.
|
@@ -93,8 +97,8 @@ module Sequel
|
|
93
97
|
def connect(server)
|
94
98
|
opts = server_opts(server)
|
95
99
|
conn = Mysql.init
|
96
|
-
|
97
|
-
conn.options(Mysql::
|
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)
|
98
102
|
if encoding = opts[:encoding] || opts[:charset]
|
99
103
|
# set charset _before_ the connect. using an option instead of "SET (NAMES|CHARACTER_SET_*)" works across reconnects
|
100
104
|
conn.options(Mysql::SET_CHARSET_NAME, encoding)
|
@@ -153,8 +157,7 @@ module Sequel
|
|
153
157
|
# yield the connection if a block is given.
|
154
158
|
def _execute(conn, sql, opts)
|
155
159
|
begin
|
156
|
-
|
157
|
-
r = conn.query(sql)
|
160
|
+
r = log_yield(sql){conn.query(sql)}
|
158
161
|
if opts[:type] == :select
|
159
162
|
yield r if r
|
160
163
|
elsif block_given?
|
@@ -320,7 +323,13 @@ module Sequel
|
|
320
323
|
def fetch_rows(sql, &block)
|
321
324
|
execute(sql) do |r|
|
322
325
|
i = -1
|
323
|
-
cols = r.fetch_fields.map
|
326
|
+
cols = r.fetch_fields.map do |f|
|
327
|
+
# Pretend tinyint is another integer type if its length is not 1, to
|
328
|
+
# avoid casting to boolean if Sequel::MySQL.convert_tinyint_to_bool
|
329
|
+
# is set.
|
330
|
+
type_proc = f.type == 1 && f.length != 1 ? MYSQL_TYPES[2] : MYSQL_TYPES[f.type]
|
331
|
+
[output_identifier(f.name), type_proc, i+=1]
|
332
|
+
end
|
324
333
|
@columns = cols.map{|c| c.first}
|
325
334
|
if opts[:split_multiple_result_sets]
|
326
335
|
s = []
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -45,10 +45,9 @@ module Sequel
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def execute(sql, opts={})
|
48
|
-
log_info(sql)
|
49
48
|
synchronize(opts[:server]) do |conn|
|
50
49
|
begin
|
51
|
-
r = conn.run(sql)
|
50
|
+
r = log_yield(sql){conn.run(sql)}
|
52
51
|
yield(r) if block_given?
|
53
52
|
rescue ::ODBC::Error, ArgumentError => e
|
54
53
|
raise_error(e)
|
@@ -60,10 +59,9 @@ module Sequel
|
|
60
59
|
end
|
61
60
|
|
62
61
|
def execute_dui(sql, opts={})
|
63
|
-
log_info(sql)
|
64
62
|
synchronize(opts[:server]) do |conn|
|
65
63
|
begin
|
66
|
-
conn.do(sql)
|
64
|
+
log_yield(sql){conn.do(sql)}
|
67
65
|
rescue ::ODBC::Error, ArgumentError => e
|
68
66
|
raise_error(e)
|
69
67
|
end
|
@@ -16,13 +16,11 @@ module Sequel
|
|
16
16
|
|
17
17
|
# Return the last inserted identity value.
|
18
18
|
def execute_insert(sql, opts={})
|
19
|
-
log_info(sql)
|
20
19
|
synchronize(opts[:server]) do |conn|
|
21
20
|
begin
|
22
|
-
conn.do(sql)
|
23
|
-
log_info(LAST_INSERT_ID_SQL)
|
21
|
+
log_yield(sql){conn.do(sql)}
|
24
22
|
begin
|
25
|
-
s = conn.run(LAST_INSERT_ID_SQL)
|
23
|
+
s = log_yield(LAST_INSERT_ID_SQL){conn.run(LAST_INSERT_ID_SQL)}
|
26
24
|
if (rows = s.fetch_all) and (row = rows.first)
|
27
25
|
Integer(row.first)
|
28
26
|
end
|
@@ -60,10 +60,9 @@ module Sequel
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def execute(sql, opts={})
|
63
|
-
log_info(sql)
|
64
63
|
synchronize(opts[:server]) do |conn|
|
65
64
|
begin
|
66
|
-
r = conn.exec(sql)
|
65
|
+
r = log_yield(sql){conn.exec(sql)}
|
67
66
|
yield(r) if block_given?
|
68
67
|
r
|
69
68
|
rescue OCIException => e
|
@@ -76,14 +75,12 @@ module Sequel
|
|
76
75
|
private
|
77
76
|
|
78
77
|
def begin_transaction(conn)
|
79
|
-
|
80
|
-
conn.autocommit = false
|
78
|
+
log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
|
81
79
|
conn
|
82
80
|
end
|
83
81
|
|
84
82
|
def commit_transaction(conn)
|
85
|
-
|
86
|
-
conn.commit
|
83
|
+
log_yield(TRANSACTION_COMMIT){conn.commit}
|
87
84
|
end
|
88
85
|
|
89
86
|
def disconnect_connection(c)
|
@@ -96,8 +93,7 @@ module Sequel
|
|
96
93
|
end
|
97
94
|
|
98
95
|
def rollback_transaction(conn)
|
99
|
-
|
100
|
-
conn.rollback
|
96
|
+
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
101
97
|
end
|
102
98
|
end
|
103
99
|
|
@@ -133,7 +133,6 @@ module Sequel
|
|
133
133
|
super
|
134
134
|
if Postgres.use_iso_date_format
|
135
135
|
sql = "SET DateStyle = 'ISO'"
|
136
|
-
@db.log_info(sql)
|
137
136
|
execute(sql)
|
138
137
|
end
|
139
138
|
@prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
|
@@ -160,7 +159,7 @@ module Sequel
|
|
160
159
|
# Execute the given SQL with this connection. If a block is given,
|
161
160
|
# yield the results, otherwise, return the number of changed rows.
|
162
161
|
def execute(sql, args=nil)
|
163
|
-
q = check_disconnect_errors{args ? async_exec(sql, args) : async_exec(sql)}
|
162
|
+
q = check_disconnect_errors{@db.log_yield(sql, args){args ? async_exec(sql, args) : async_exec(sql)}}
|
164
163
|
begin
|
165
164
|
block_given? ? yield(q) : q.cmd_tuples
|
166
165
|
ensure
|
@@ -225,7 +224,6 @@ module Sequel
|
|
225
224
|
def execute(sql, opts={}, &block)
|
226
225
|
check_database_errors do
|
227
226
|
return execute_prepared_statement(sql, opts, &block) if Symbol === sql
|
228
|
-
log_info(sql, opts[:arguments])
|
229
227
|
synchronize(opts[:server]){|conn| conn.execute(sql, opts[:arguments], &block)}
|
230
228
|
end
|
231
229
|
end
|
@@ -235,7 +233,6 @@ module Sequel
|
|
235
233
|
def execute_insert(sql, opts={})
|
236
234
|
return execute(sql, opts) if Symbol === sql
|
237
235
|
check_database_errors do
|
238
|
-
log_info(sql, opts[:arguments])
|
239
236
|
synchronize(opts[:server]) do |conn|
|
240
237
|
conn.execute(sql, opts[:arguments])
|
241
238
|
insert_result(conn, opts[:table], opts[:values])
|
@@ -278,16 +275,12 @@ module Sequel
|
|
278
275
|
synchronize(opts[:server]) do |conn|
|
279
276
|
unless conn.prepared_statements[ps_name] == sql
|
280
277
|
if conn.prepared_statements.include?(ps_name)
|
281
|
-
|
282
|
-
log_info(s)
|
283
|
-
conn.execute(s) unless conn.prepared_statements[ps_name] == sql
|
278
|
+
conn.execute("DEALLOCATE #{ps_name}") unless conn.prepared_statements[ps_name] == sql
|
284
279
|
end
|
285
280
|
conn.prepared_statements[ps_name] = sql
|
286
|
-
|
287
|
-
conn.check_disconnect_errors{conn.prepare(ps_name, sql)}
|
281
|
+
conn.check_disconnect_errors{log_yield("PREPARE #{ps_name} AS #{sql}"){conn.prepare(ps_name, sql)}}
|
288
282
|
end
|
289
|
-
|
290
|
-
q = conn.check_disconnect_errors{conn.exec_prepared(ps_name, args)}
|
283
|
+
q = conn.check_disconnect_errors{log_yield("EXECUTE #{ps_name}", args){conn.exec_prepared(ps_name, args)}}
|
291
284
|
if opts[:table] && opts[:values]
|
292
285
|
insert_result(conn, opts[:table], opts[:values])
|
293
286
|
else
|
@@ -12,6 +12,10 @@ module Sequel
|
|
12
12
|
SQL_SAVEPOINT = 'SAVE TRANSACTION autopoint_%d'.freeze
|
13
13
|
TEMPORARY = "#".freeze
|
14
14
|
|
15
|
+
# The types to check for 0 scale to transform :decimal types
|
16
|
+
# to :integer.
|
17
|
+
DECIMAL_TYPE_RE = /number|numeric|decimal/io
|
18
|
+
|
15
19
|
# Microsoft SQL Server uses the :mssql type.
|
16
20
|
def database_type
|
17
21
|
:mssql
|
@@ -64,9 +68,11 @@ module Sequel
|
|
64
68
|
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(op)}"
|
65
69
|
when :set_column_null
|
66
70
|
sch = schema(table).find{|k,v| k.to_s == op[:name].to_s}.last
|
67
|
-
type =
|
68
|
-
|
69
|
-
|
71
|
+
type = sch[:db_type]
|
72
|
+
if [:string, :decimal].include?(sch[:type]) and size = (sch[:max_chars] || sch[:column_size])
|
73
|
+
type += "(#{size}#{", #{sch[:scale]}" if sch[:scale] && sch[:scale].to_i > 0})"
|
74
|
+
end
|
75
|
+
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(:type=>type)} #{'NOT ' unless op[:null]}NULL"
|
70
76
|
when :set_column_default
|
71
77
|
"ALTER TABLE #{quote_schema_table(table)} ADD CONSTRAINT #{quote_identifier("sequel_#{table}_#{op[:name]}_def")} DEFAULT #{literal(op[:default])} FOR #{quote_identifier(op[:name])}"
|
72
78
|
else
|
@@ -107,6 +113,11 @@ module Sequel
|
|
107
113
|
ds
|
108
114
|
end
|
109
115
|
|
116
|
+
# Use SP_RENAME to rename the table
|
117
|
+
def rename_table_sql(name, new_name)
|
118
|
+
"SP_RENAME #{quote_schema_table(name)}, #{quote_schema_table(new_name)}"
|
119
|
+
end
|
120
|
+
|
110
121
|
# SQL to rollback to a savepoint
|
111
122
|
def rollback_savepoint_sql(depth)
|
112
123
|
SQL_ROLLBACK_TO_SAVEPOINT % depth
|
@@ -125,7 +136,7 @@ module Sequel
|
|
125
136
|
ds = metadata_dataset.from(:information_schema__tables___t).
|
126
137
|
join(:information_schema__columns___c, :table_catalog=>:table_catalog,
|
127
138
|
:table_schema => :table_schema, :table_name => :table_name).
|
128
|
-
select(:column_name___column, :data_type___db_type, :character_maximum_length___max_chars, :column_default___default, :is_nullable___allow_null).
|
139
|
+
select(:column_name___column, :data_type___db_type, :character_maximum_length___max_chars, :column_default___default, :is_nullable___allow_null, :numeric_precision___column_size, :numeric_scale___scale).
|
129
140
|
filter(:c__table_name=>m2.call(table_name.to_s))
|
130
141
|
if schema = opts[:schema] || default_schema
|
131
142
|
ds.filter!(:c__table_schema=>schema)
|
@@ -133,7 +144,11 @@ module Sequel
|
|
133
144
|
ds.map do |row|
|
134
145
|
row[:allow_null] = row[:allow_null] == 'YES' ? true : false
|
135
146
|
row[:default] = nil if blank_object?(row[:default])
|
136
|
-
row[:type] =
|
147
|
+
row[:type] = if row[:db_type] =~ DECIMAL_TYPE_RE && row[:scale] == 0
|
148
|
+
:integer
|
149
|
+
else
|
150
|
+
schema_column_type(row[:db_type])
|
151
|
+
end
|
137
152
|
[m.call(row.delete(:column)), row]
|
138
153
|
end
|
139
154
|
end
|
@@ -183,7 +198,7 @@ module Sequel
|
|
183
198
|
COMMA_SEPARATOR = ', '.freeze
|
184
199
|
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with from output from2 where')
|
185
200
|
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with into columns output values')
|
186
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with limit
|
201
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct limit columns into from lock join where group having order compounds')
|
187
202
|
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with table set output from where')
|
188
203
|
NOLOCK = ' WITH (NOLOCK)'.freeze
|
189
204
|
UPDLOCK = ' WITH (UPDLOCK)'.freeze
|
@@ -301,11 +316,9 @@ module Sequel
|
|
301
316
|
raise(Error, 'MSSQL requires an order be provided if using an offset') unless order = @opts[:order]
|
302
317
|
dsa1 = dataset_alias(1)
|
303
318
|
rn = row_number_column
|
304
|
-
sel = [Sequel::SQL::WindowFunction.new(SQL::Function.new(:ROW_NUMBER), Sequel::SQL::Window.new(:order=>order)).as(rn)]
|
305
|
-
sel.unshift(WILDCARD) unless osel = @opts[:select] and !osel.empty?
|
306
319
|
subselect_sql(unlimited.
|
307
320
|
unordered.
|
308
|
-
|
321
|
+
select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
|
309
322
|
from_self(:alias=>dsa1).
|
310
323
|
limit(@opts[:limit]).
|
311
324
|
where(SQL::Identifier.new(rn) > o))
|
@@ -131,9 +131,9 @@ module Sequel
|
|
131
131
|
|
132
132
|
# Use MySQL specific syntax for engine type and character encoding
|
133
133
|
def create_table_sql(name, generator, options = {})
|
134
|
-
engine = options.
|
135
|
-
charset = options.
|
136
|
-
collate = options.
|
134
|
+
engine = options.fetch(:engine, Sequel::MySQL.default_engine)
|
135
|
+
charset = options.fetch(:charset, Sequel::MySQL.default_charset)
|
136
|
+
collate = options.fetch(:collate, Sequel::MySQL.default_collate)
|
137
137
|
generator.columns.each do |c|
|
138
138
|
if t = c.delete(:table)
|
139
139
|
generator.foreign_key([c[:name]], t, c.merge(:name=>nil, :type=>:foreign_key))
|
@@ -250,6 +250,11 @@ module Sequel
|
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
253
|
+
# Use GROUP BY instead of DISTINCT ON if arguments are provided.
|
254
|
+
def distinct(*args)
|
255
|
+
args.empty? ? super : group(*args)
|
256
|
+
end
|
257
|
+
|
253
258
|
# Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.
|
254
259
|
def for_share
|
255
260
|
lock_style(:share)
|
@@ -288,23 +293,19 @@ module Sequel
|
|
288
293
|
end
|
289
294
|
end
|
290
295
|
|
291
|
-
# Sets up
|
296
|
+
# Sets up the insert methods to use INSERT IGNORE.
|
292
297
|
# Useful if you have a unique key and want to just skip
|
293
298
|
# inserting rows that violate the unique key restriction.
|
294
299
|
#
|
295
|
-
#
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
# )
|
300
|
-
#
|
301
|
-
# INSERT IGNORE INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
302
|
-
#
|
300
|
+
# dataset.insert_ignore.multi_insert(
|
301
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
302
|
+
# )
|
303
|
+
# # INSERT IGNORE INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
303
304
|
def insert_ignore
|
304
305
|
clone(:insert_ignore=>true)
|
305
306
|
end
|
306
307
|
|
307
|
-
# Sets up
|
308
|
+
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
308
309
|
# If you pass no arguments, ALL fields will be
|
309
310
|
# updated with the new values. If you pass the fields you
|
310
311
|
# want then ONLY those field will be updated.
|
@@ -312,22 +313,17 @@ module Sequel
|
|
312
313
|
# Useful if you have a unique key and want to update
|
313
314
|
# inserting rows that violate the unique key restriction.
|
314
315
|
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
# )
|
320
|
-
#
|
321
|
-
# INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
322
|
-
# ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)
|
323
|
-
#
|
324
|
-
# dataset.on_duplicate_key_update(:value).multi_insert(
|
325
|
-
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
326
|
-
# )
|
327
|
-
#
|
328
|
-
# INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
329
|
-
# ON DUPLICATE KEY UPDATE value=VALUES(value)
|
316
|
+
# dataset.on_duplicate_key_update.multi_insert(
|
317
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
318
|
+
# )
|
319
|
+
# # INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
320
|
+
# # ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)
|
330
321
|
#
|
322
|
+
# dataset.on_duplicate_key_update(:value).multi_insert(
|
323
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
324
|
+
# )
|
325
|
+
# # INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
326
|
+
# # ON DUPLICATE KEY UPDATE value=VALUES(value)
|
331
327
|
def on_duplicate_key_update(*args)
|
332
328
|
clone(:on_duplicate_key_update => args)
|
333
329
|
end
|
@@ -337,6 +333,12 @@ module Sequel
|
|
337
333
|
[insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
|
338
334
|
end
|
339
335
|
|
336
|
+
# MySQL uses the number of rows actually modified in the update,
|
337
|
+
# instead of the number of matched by the filter.
|
338
|
+
def provides_accurate_rows_matched?
|
339
|
+
false
|
340
|
+
end
|
341
|
+
|
340
342
|
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
|
341
343
|
def quoted_identifier(c)
|
342
344
|
"`#{c}`"
|
@@ -348,9 +350,10 @@ module Sequel
|
|
348
350
|
clone(:replace=>true).insert_sql(*values)
|
349
351
|
end
|
350
352
|
|
351
|
-
#
|
353
|
+
# MySQL can emulate DISTINCT ON with its non-standard GROUP BY implementation,
|
354
|
+
# though the rows returned cannot be made deterministic through ordering.
|
352
355
|
def supports_distinct_on?
|
353
|
-
|
356
|
+
true
|
354
357
|
end
|
355
358
|
|
356
359
|
# MySQL does not support INTERSECT or EXCEPT
|