sequel 4.3.0 → 4.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG +34 -0
- data/README.rdoc +7 -7
- data/Rakefile +2 -2
- data/doc/active_record.rdoc +2 -2
- data/doc/association_basics.rdoc +21 -7
- data/doc/bin_sequel.rdoc +2 -2
- data/doc/cheat_sheet.rdoc +2 -1
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/migration.rdoc +2 -2
- data/doc/object_model.rdoc +2 -2
- data/doc/opening_databases.rdoc +13 -1
- data/doc/querying.rdoc +9 -4
- data/doc/release_notes/4.4.0.txt +92 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +2 -2
- data/doc/sql.rdoc +3 -3
- data/doc/thread_safety.rdoc +1 -1
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/jdbc.rb +85 -19
- data/lib/sequel/adapters/jdbc/db2.rb +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +34 -3
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +57 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/oracle.rb +41 -4
- data/lib/sequel/adapters/shared/sqlanywhere.rb +458 -0
- data/lib/sequel/adapters/sqlanywhere.rb +177 -0
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +11 -3
- data/lib/sequel/core.rb +4 -4
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +2 -2
- data/lib/sequel/dataset.rb +1 -1
- data/lib/sequel/dataset/actions.rb +2 -0
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +37 -16
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +2 -2
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +5 -4
- data/lib/sequel/extensions/pg_array.rb +2 -2
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +2 -2
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
- data/lib/sequel/extensions/pg_json.rb +2 -2
- data/lib/sequel/extensions/pg_json_ops.rb +2 -2
- data/lib/sequel/extensions/pg_range.rb +2 -2
- data/lib/sequel/extensions/pg_range_ops.rb +2 -2
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/pg_row_ops.rb +3 -3
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/associations.rb +106 -17
- data/lib/sequel/model/base.rb +23 -19
- data/lib/sequel/plugins/json_serializer.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +14 -6
- data/lib/sequel/plugins/pg_array_associations.rb +28 -0
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +11 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/table_select.rb +41 -0
- data/lib/sequel/plugins/tree.rb +1 -1
- data/lib/sequel/sql.rb +2 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/oracle_spec.rb +22 -1
- data/spec/adapters/postgres_spec.rb +31 -48
- data/spec/adapters/sqlanywhere_spec.rb +170 -0
- data/spec/core/dataset_spec.rb +109 -0
- data/spec/core/object_graph_spec.rb +7 -0
- data/spec/extensions/constraint_validations_spec.rb +7 -0
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +65 -0
- data/spec/extensions/pg_array_associations_spec.rb +44 -0
- data/spec/extensions/rcte_tree_spec.rb +3 -3
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/extensions/table_select_spec.rb +71 -0
- data/spec/integration/associations_test.rb +279 -7
- data/spec/integration/dataset_test.rb +13 -4
- data/spec/integration/schema_test.rb +12 -14
- data/spec/model/associations_spec.rb +472 -3
- data/spec/model/class_dataset_methods_spec.rb +1 -0
- data/spec/model/model_spec.rb +10 -0
- metadata +10 -2
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
require 'sqlanywhere'
|
|
2
|
+
|
|
3
|
+
Sequel.require %w'shared/sqlanywhere', 'adapters'
|
|
4
|
+
|
|
5
|
+
module Sequel
|
|
6
|
+
# Module for holding all SqlAnywhere-related classes and modules for Sequel.
|
|
7
|
+
module SqlAnywhere
|
|
8
|
+
|
|
9
|
+
class SQLAnywhereException < StandardError
|
|
10
|
+
attr_reader :errno
|
|
11
|
+
attr_reader :sql
|
|
12
|
+
|
|
13
|
+
def initialize(message, errno, sql)
|
|
14
|
+
super(message)
|
|
15
|
+
@errno = errno
|
|
16
|
+
@sql = sql
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
TYPE_TRANSLATOR = tt = Class.new do
|
|
21
|
+
def blob(s) ::Sequel::SQL::Blob.new(s) end
|
|
22
|
+
def boolean(s) s.to_i != 0 end
|
|
23
|
+
def date(s) ::Date.strptime(s) end
|
|
24
|
+
def decimal(s) ::BigDecimal.new(s) end
|
|
25
|
+
def time(s) ::Sequel.string_to_time(s) end
|
|
26
|
+
end.new
|
|
27
|
+
|
|
28
|
+
SQLANYWHERE_TYPES = {}
|
|
29
|
+
{
|
|
30
|
+
[0, 484] => tt.method(:decimal),
|
|
31
|
+
[384] => tt.method(:date),
|
|
32
|
+
[388] => tt.method(:time),
|
|
33
|
+
[500] => tt.method(:boolean),
|
|
34
|
+
[524, 528] => tt.method(:blob)
|
|
35
|
+
}.each do |k,v|
|
|
36
|
+
k.each{|n| SQLANYWHERE_TYPES[n] = v}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Database class for SQLAnywhere databases used with Sequel.
|
|
40
|
+
class Database < Sequel::Database
|
|
41
|
+
include Sequel::SqlAnywhere::DatabaseMethods
|
|
42
|
+
|
|
43
|
+
DEFAULT_CONFIG = { :user => 'dba', :password => 'sql' }
|
|
44
|
+
|
|
45
|
+
attr_accessor :api
|
|
46
|
+
|
|
47
|
+
set_adapter_scheme :sqlanywhere
|
|
48
|
+
|
|
49
|
+
def connect(server)
|
|
50
|
+
opts = server_opts(server)
|
|
51
|
+
unless conn_string = opts[:conn_string]
|
|
52
|
+
conn_string = []
|
|
53
|
+
conn_string << "Host=#{opts[:host]}#{":#{opts[:port]}" if opts[:port]}" if opts[:host]
|
|
54
|
+
conn_string << "DBN=#{opts[:database]}" if opts[:database]
|
|
55
|
+
conn_string << "UID=#{opts[:user]}" if opts[:user]
|
|
56
|
+
conn_string << "Password=#{opts[:password]}" if opts[:password]
|
|
57
|
+
conn_string << "CommLinks=#{opts[:commlinks]}" if opts[:commlinks]
|
|
58
|
+
conn_string << "ConnectionName=#{opts[:connection_name]}" if opts[:connection_name]
|
|
59
|
+
conn_string << "CharSet=#{opts[:encoding]}" if opts[:encoding]
|
|
60
|
+
conn_string << "Idle=0" # Prevent the server from disconnecting us if we're idle for >240mins (by default)
|
|
61
|
+
conn_string << nil
|
|
62
|
+
conn_string = conn_string.join(';')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
conn = @api.sqlany_new_connection
|
|
66
|
+
raise LoadError, "Could not connect" unless conn && @api.sqlany_connect(conn, conn_string) == 1
|
|
67
|
+
|
|
68
|
+
if Sequel.application_timezone == :utc
|
|
69
|
+
@api.sqlany_execute_immediate(conn, "SET TEMPORARY OPTION time_zone_adjustment=0")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
conn
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Closes given database connection.
|
|
76
|
+
def disconnect_connection(c)
|
|
77
|
+
@api.sqlany_disconnect(c)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns number of rows affected
|
|
81
|
+
def execute_dui(sql, opts=OPTS)
|
|
82
|
+
synchronize do |conn|
|
|
83
|
+
_execute(conn, :rows, sql, opts)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def execute(sql, opts=OPTS, &block)
|
|
88
|
+
synchronize do |conn|
|
|
89
|
+
_execute(conn, :select, sql, opts, &block)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def execute_insert(sql, opts=OPTS)
|
|
94
|
+
synchronize do |conn|
|
|
95
|
+
_execute(conn, :insert, sql, opts)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
LAST_INSERT_ID = 'SELECT @@IDENTITY'.freeze
|
|
102
|
+
def _execute(conn, type, sql, opts)
|
|
103
|
+
unless rs = log_yield(sql){@api.sqlany_execute_direct(conn, sql)}
|
|
104
|
+
result, errstr = @api.sqlany_error(conn)
|
|
105
|
+
raise_error(SQLAnywhereException.new(errstr, result, sql))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
case type
|
|
109
|
+
when :select
|
|
110
|
+
yield rs if block_given?
|
|
111
|
+
when :rows
|
|
112
|
+
return @api.sqlany_affected_rows(rs)
|
|
113
|
+
when :insert
|
|
114
|
+
_execute(conn, :select, LAST_INSERT_ID, opts){|r| return @api.sqlany_get_column(r, 0)[1] if r && @api.sqlany_fetch_next(r) == 1}
|
|
115
|
+
end
|
|
116
|
+
ensure
|
|
117
|
+
@api.sqlany_commit(conn) unless in_transaction?
|
|
118
|
+
@api.sqlany_free_stmt(rs) if rs
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def adapter_initialize
|
|
122
|
+
@conversion_procs = SQLANYWHERE_TYPES.dup
|
|
123
|
+
@conversion_procs[392] = method(:to_application_timestamp_sa)
|
|
124
|
+
@api = SQLAnywhere::SQLAnywhereInterface.new
|
|
125
|
+
raise LoadError, "Could not load SQLAnywhere DBCAPI library" if SQLAnywhere::API.sqlany_initialize_interface(@api) == 0
|
|
126
|
+
raise LoadError, "Could not initialize SQLAnywhere DBCAPI library" if @api.sqlany_init == 0
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def log_connection_execute(conn, sql)
|
|
130
|
+
_execute(conn, nil, sql, OPTS)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Dataset class for SqlAnywhere datasets accessed via the native driver.
|
|
135
|
+
class Dataset < Sequel::Dataset
|
|
136
|
+
include Sequel::SqlAnywhere::DatasetMethods
|
|
137
|
+
|
|
138
|
+
Database::DatasetClass = self
|
|
139
|
+
|
|
140
|
+
# Yield all rows matching this dataset. If the dataset is set to
|
|
141
|
+
# split multiple statements, yield arrays of hashes one per statement
|
|
142
|
+
# instead of yielding results for all statements as hashes.
|
|
143
|
+
def fetch_rows(sql)
|
|
144
|
+
db = @db
|
|
145
|
+
cps = db.conversion_procs
|
|
146
|
+
api = db.api
|
|
147
|
+
execute(sql) do |rs|
|
|
148
|
+
convert = (convert_smallint_to_bool and db.convert_smallint_to_bool)
|
|
149
|
+
col_infos = []
|
|
150
|
+
api.sqlany_num_cols(rs).times do |i|
|
|
151
|
+
_, _, name, _, type = api.sqlany_get_column_info(rs, i)
|
|
152
|
+
cp = if type == 500
|
|
153
|
+
cps[500] if convert
|
|
154
|
+
else
|
|
155
|
+
cps[type]
|
|
156
|
+
end
|
|
157
|
+
col_infos << [i, output_identifier(name), cp]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
@columns = col_infos.map{|a| a[1]}
|
|
161
|
+
|
|
162
|
+
if rs
|
|
163
|
+
while api.sqlany_fetch_next(rs) == 1
|
|
164
|
+
h = {}
|
|
165
|
+
col_infos.each do |i, name, cp|
|
|
166
|
+
_, v = api.sqlany_get_column(rs, i)
|
|
167
|
+
h[name] = cp && v ? cp[v] : v
|
|
168
|
+
end
|
|
169
|
+
yield h
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
self
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -11,9 +11,12 @@ module Sequel
|
|
|
11
11
|
def select_sql
|
|
12
12
|
return super unless o = @opts[:offset]
|
|
13
13
|
|
|
14
|
-
order = @opts[:order]
|
|
15
|
-
if
|
|
16
|
-
|
|
14
|
+
order = @opts[:order]
|
|
15
|
+
if require_offset_order?
|
|
16
|
+
order ||= default_offset_order
|
|
17
|
+
if order.nil? || order.empty?
|
|
18
|
+
raise(Error, "#{db.database_type} requires an order be provided if using an offset")
|
|
19
|
+
end
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
columns = clone(:append_sql=>'').columns
|
|
@@ -38,5 +41,10 @@ module Sequel
|
|
|
38
41
|
def default_offset_order
|
|
39
42
|
clone(:append_sql=>'').columns
|
|
40
43
|
end
|
|
44
|
+
|
|
45
|
+
# Whether an order is required when using offset emulation via ROW_NUMBER, true by default.
|
|
46
|
+
def require_offset_order?
|
|
47
|
+
true
|
|
48
|
+
end
|
|
41
49
|
end
|
|
42
50
|
end
|
data/lib/sequel/core.rb
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
#
|
|
18
18
|
# Sequel.sqlite('blog.db'){|db| puts db[:users].count}
|
|
19
19
|
#
|
|
20
|
-
# For a more expanded introduction, see the {README}[
|
|
21
|
-
# For a quicker introduction, see the {cheat sheet}[
|
|
20
|
+
# For a more expanded introduction, see the {README}[rdoc-ref:README.rdoc].
|
|
21
|
+
# For a quicker introduction, see the {cheat sheet}[rdoc-ref:doc/cheat_sheet.rdoc].
|
|
22
22
|
module Sequel
|
|
23
23
|
@convert_two_digit_years = true
|
|
24
24
|
@datetime_class = Time
|
|
@@ -89,8 +89,8 @@ module Sequel
|
|
|
89
89
|
#
|
|
90
90
|
# Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
|
|
91
91
|
#
|
|
92
|
-
# For details, see the {"Connecting to a Database" guide}[
|
|
93
|
-
# To set up a master/slave or sharded database connection, see the {"Master/Slave Databases and Sharding" guide}[
|
|
92
|
+
# For details, see the {"Connecting to a Database" guide}[rdoc-ref:doc/opening_databases.rdoc].
|
|
93
|
+
# To set up a master/slave or sharded database connection, see the {"Master/Slave Databases and Sharding" guide}[rdoc-ref:doc/sharding.rdoc].
|
|
94
94
|
def self.connect(*args, &block)
|
|
95
95
|
Database.connect(*args, &block)
|
|
96
96
|
end
|
|
@@ -6,7 +6,7 @@ module Sequel
|
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
8
8
|
# Array of supported database adapters
|
|
9
|
-
ADAPTERS = %w'ado amalgalite cubrid db2 dbi do firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
|
|
9
|
+
ADAPTERS = %w'ado amalgalite cubrid db2 dbi do firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlanywhere sqlite swift tinytds'.collect{|x| x.to_sym}
|
|
10
10
|
|
|
11
11
|
@single_threaded = false
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ module Sequel
|
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
8
8
|
STRING_DEFAULT_RE = /\A'(.*)'\z/
|
|
9
|
-
CURRENT_TIMESTAMP_RE = /now|CURRENT|getdate|\ADate\(\)\z/io
|
|
9
|
+
CURRENT_TIMESTAMP_RE = /now|today|CURRENT|getdate|\ADate\(\)\z/io
|
|
10
10
|
COLUMN_SCHEMA_DATETIME_TYPES = [:date, :datetime]
|
|
11
11
|
COLUMN_SCHEMA_STRING_TYPES = [:string, :blob, :date, :datetime, :time, :enum, :set, :interval]
|
|
12
12
|
|
|
@@ -13,7 +13,7 @@ module Sequel
|
|
|
13
13
|
# the column method, which makes for a nicer DSL.
|
|
14
14
|
#
|
|
15
15
|
# For more information on Sequel's support for schema modification, see
|
|
16
|
-
# the {"Schema Modification" guide}[
|
|
16
|
+
# the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
|
|
17
17
|
class CreateTableGenerator
|
|
18
18
|
# Classes specifying generic types that Sequel will convert to database-specific types.
|
|
19
19
|
GENERIC_TYPES=[String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
|
|
@@ -71,7 +71,7 @@ module Sequel
|
|
|
71
71
|
# definitions using <tt>create_table</tt>, and +add_index+ accepts all the options
|
|
72
72
|
# available for index definition.
|
|
73
73
|
#
|
|
74
|
-
# See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[
|
|
74
|
+
# See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[rdoc-ref:doc/migration.rdoc].
|
|
75
75
|
def alter_table(name, generator=nil, &block)
|
|
76
76
|
generator ||= alter_table_generator(&block)
|
|
77
77
|
remove_cached_schema(name)
|
|
@@ -159,7 +159,7 @@ module Sequel
|
|
|
159
159
|
# :inherits :: Inherit from a different tables. An array can be
|
|
160
160
|
# specified to inherit from multiple tables.
|
|
161
161
|
#
|
|
162
|
-
# See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[
|
|
162
|
+
# See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
|
|
163
163
|
def create_table(name, options=OPTS, &block)
|
|
164
164
|
remove_cached_schema(name)
|
|
165
165
|
options = {:generator=>options} if options.is_a?(Schema::CreateTableGenerator)
|
data/lib/sequel/dataset.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Sequel
|
|
|
21
21
|
# Datasets are Enumerable objects, so they can be manipulated using any
|
|
22
22
|
# of the Enumerable methods, such as map, inject, etc.
|
|
23
23
|
#
|
|
24
|
-
# For more information, see the {"Dataset Basics" guide}[
|
|
24
|
+
# For more information, see the {"Dataset Basics" guide}[rdoc-ref:doc/dataset_basics.rdoc].
|
|
25
25
|
class Dataset
|
|
26
26
|
OPTS = Sequel::OPTS
|
|
27
27
|
|
|
@@ -271,6 +271,8 @@ module Sequel
|
|
|
271
271
|
# :commit_every :: Open a new transaction for every given number of records.
|
|
272
272
|
# For example, if you provide a value of 50, will commit
|
|
273
273
|
# after every 50 records.
|
|
274
|
+
# :return :: When the :value is :primary_key, returns an array of
|
|
275
|
+
# autoincremented primary key values for the rows inserted.
|
|
274
276
|
# :server :: Set the server/shard to use for the transaction and insert
|
|
275
277
|
# queries.
|
|
276
278
|
# :slice :: Same as :commit_every, :commit_every takes precedence.
|
data/lib/sequel/dataset/graph.rb
CHANGED
|
@@ -44,7 +44,7 @@ module Sequel
|
|
|
44
44
|
# some metadata about the join that makes it important to use +graph+ instead
|
|
45
45
|
# of +join_table+.
|
|
46
46
|
# :table_alias :: The alias to use for the table. If not specified, doesn't
|
|
47
|
-
# alias the table. You will get an error if the
|
|
47
|
+
# alias the table. You will get an error if the alias (or table) name is
|
|
48
48
|
# used more than once.
|
|
49
49
|
def graph(dataset, join_conditions = nil, options = OPTS, &block)
|
|
50
50
|
# Allow the use of a dataset or symbol as the first argument
|
|
@@ -3,7 +3,7 @@ module Sequel
|
|
|
3
3
|
# ---------------------
|
|
4
4
|
# :section: 8 - Methods related to prepared statements or bound variables
|
|
5
5
|
# On some adapters, these use native prepared statements and bound variables, on others
|
|
6
|
-
# support is emulated. For details, see the {"Prepared Statements/Bound Variables" guide}[
|
|
6
|
+
# support is emulated. For details, see the {"Prepared Statements/Bound Variables" guide}[rdoc-ref:doc/prepared_statements.rdoc].
|
|
7
7
|
# ---------------------
|
|
8
8
|
|
|
9
9
|
PREPARED_ARG_PLACEHOLDER = LiteralString.new('?').freeze
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Sequel
|
|
|
36
36
|
QUERY_METHODS = (<<-METHS).split.map{|x| x.to_sym} + JOIN_METHODS
|
|
37
37
|
add_graph_aliases and distinct except exclude exclude_having exclude_where
|
|
38
38
|
filter for_update from from_self graph grep group group_and_count group_by having intersect invert
|
|
39
|
-
limit lock_style naked or order order_append order_by order_more order_prepend qualify
|
|
39
|
+
limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
|
|
40
40
|
reverse reverse_order select select_all select_append select_group select_more server
|
|
41
41
|
set_graph_aliases unfiltered ungraphed ungrouped union
|
|
42
42
|
unlimited unordered where with with_recursive with_sql
|
|
@@ -524,6 +524,7 @@ module Sequel
|
|
|
524
524
|
return from_self.limit(l, o) if @opts[:sql]
|
|
525
525
|
|
|
526
526
|
if l.is_a?(Range)
|
|
527
|
+
no_offset = false
|
|
527
528
|
o = l.first
|
|
528
529
|
l = l.last - l.first + (l.exclude_end? ? 0 : 1)
|
|
529
530
|
end
|
|
@@ -531,17 +532,10 @@ module Sequel
|
|
|
531
532
|
if l.is_a?(Integer)
|
|
532
533
|
raise(Error, 'Limits must be greater than or equal to 1') unless l >= 1
|
|
533
534
|
end
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
raise(Error, 'Offsets must be greater than or equal to 0') unless o >= 0
|
|
539
|
-
end
|
|
540
|
-
opts[:offset] = o
|
|
541
|
-
elsif !no_offset
|
|
542
|
-
opts[:offset] = nil
|
|
543
|
-
end
|
|
544
|
-
clone(opts)
|
|
535
|
+
|
|
536
|
+
ds = clone(:limit=>l)
|
|
537
|
+
ds = ds.offset(o) unless no_offset
|
|
538
|
+
ds
|
|
545
539
|
end
|
|
546
540
|
|
|
547
541
|
# Returns a cloned dataset with the given lock style. If style is a
|
|
@@ -568,6 +562,19 @@ module Sequel
|
|
|
568
562
|
ds.row_proc = nil
|
|
569
563
|
ds
|
|
570
564
|
end
|
|
565
|
+
|
|
566
|
+
# Returns a copy of the dataset with a specified order. Can be safely combined with limit.
|
|
567
|
+
# If you call limit with an offset, it will override override the offset if you've called
|
|
568
|
+
# offset first.
|
|
569
|
+
#
|
|
570
|
+
# DB[:items].offset(10) # SELECT * FROM items OFFSET 10
|
|
571
|
+
def offset(o)
|
|
572
|
+
o = o.to_i if o.is_a?(String) && !o.is_a?(LiteralString)
|
|
573
|
+
if o.is_a?(Integer)
|
|
574
|
+
raise(Error, 'Offsets must be greater than or equal to 0') unless o >= 0
|
|
575
|
+
end
|
|
576
|
+
clone(:offset => o)
|
|
577
|
+
end
|
|
571
578
|
|
|
572
579
|
# Adds an alternate filter to an existing filter using OR. If no filter
|
|
573
580
|
# exists an +Error+ is raised.
|
|
@@ -842,7 +849,7 @@ module Sequel
|
|
|
842
849
|
# where also accepts a block, which should return one of the above argument
|
|
843
850
|
# types, and is treated the same way. This block yields a virtual row object,
|
|
844
851
|
# which is easy to use to create identifiers and functions. For more details
|
|
845
|
-
# on the virtual row support, see the {"Virtual Rows" guide}[
|
|
852
|
+
# on the virtual row support, see the {"Virtual Rows" guide}[rdoc-ref:doc/virtual_rows.rdoc]
|
|
846
853
|
#
|
|
847
854
|
# If both a block and regular argument are provided, they get ANDed together.
|
|
848
855
|
#
|
|
@@ -871,7 +878,7 @@ module Sequel
|
|
|
871
878
|
# software = dataset.where(:category => 'software').where{price < 100}
|
|
872
879
|
# # SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
|
|
873
880
|
#
|
|
874
|
-
# See the
|
|
881
|
+
# See the {"Dataset Filtering" guide}[rdoc-ref:doc/dataset_filtering.rdoc] for more examples and details.
|
|
875
882
|
def where(*cond, &block)
|
|
876
883
|
_filter(:where, *cond, &block)
|
|
877
884
|
end
|
|
@@ -961,10 +968,24 @@ module Sequel
|
|
|
961
968
|
!(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
|
|
962
969
|
end
|
|
963
970
|
|
|
964
|
-
# Whether this dataset is a simple
|
|
971
|
+
# Whether this dataset is a simple select from an underlying table, such as:
|
|
972
|
+
#
|
|
973
|
+
# SELECT * FROM table
|
|
974
|
+
# SELECT table.* FROM table
|
|
965
975
|
def simple_select_all?
|
|
966
976
|
o = @opts.reject{|k,v| v.nil? || NON_SQL_OPTIONS.include?(k)}
|
|
967
|
-
|
|
977
|
+
if (f = o[:from]) && f.length == 1 && (f.first.is_a?(Symbol) || f.first.is_a?(SQL::AliasedExpression))
|
|
978
|
+
case o.length
|
|
979
|
+
when 1
|
|
980
|
+
true
|
|
981
|
+
when 2
|
|
982
|
+
(s = o[:select]) && s.length == 1 && s.first.is_a?(SQL::ColumnAll)
|
|
983
|
+
else
|
|
984
|
+
false
|
|
985
|
+
end
|
|
986
|
+
else
|
|
987
|
+
false
|
|
988
|
+
end
|
|
968
989
|
end
|
|
969
990
|
|
|
970
991
|
private
|
|
@@ -401,10 +401,10 @@ module Sequel
|
|
|
401
401
|
end
|
|
402
402
|
|
|
403
403
|
ds = from(:sequel_constraint_validations)
|
|
404
|
-
ds.multi_insert(rows.flatten)
|
|
405
404
|
unless drop_rows.empty?
|
|
406
405
|
ds.where([:table, :constraint_name]=>drop_rows).delete
|
|
407
406
|
end
|
|
407
|
+
ds.multi_insert(rows.flatten)
|
|
408
408
|
end
|
|
409
409
|
|
|
410
410
|
# Add the constraint to the generator, including a NOT NULL constraint
|
|
@@ -91,9 +91,9 @@ module Sequel
|
|
|
91
91
|
each_valid_interval_unit(h, MYSQL_DURATION_UNITS) do |value, sql_unit|
|
|
92
92
|
expr = Sequel.function(:DATE_ADD, expr, Sequel.lit(["INTERVAL ", " "], value, sql_unit))
|
|
93
93
|
end
|
|
94
|
-
when :mssql, :h2, :access
|
|
94
|
+
when :mssql, :h2, :access, :sqlanywhere
|
|
95
95
|
units = case db_type
|
|
96
|
-
when :mssql
|
|
96
|
+
when :mssql, :sqlanywhere
|
|
97
97
|
MSSQL_DURATION_UNITS
|
|
98
98
|
when :h2
|
|
99
99
|
H2_DURATION_UNITS
|
|
@@ -373,7 +373,7 @@ module Sequel
|
|
|
373
373
|
migrator_class(directory).new(db, directory, opts).is_current?
|
|
374
374
|
end
|
|
375
375
|
|
|
376
|
-
# Migrates the supplied database using the migration files in the
|
|
376
|
+
# Migrates the supplied database using the migration files in the specified directory. Options:
|
|
377
377
|
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
|
378
378
|
# :column :: The column in the :table argument storing the migration version (default: :version).
|
|
379
379
|
# :current :: The current version of the database. If not given, it is retrieved from the database
|