sequel 3.10.0 → 3.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|