sequel_core 1.5.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +116 -0
- data/COPYING +19 -19
- data/README +83 -32
- data/Rakefile +9 -20
- data/bin/sequel +43 -112
- data/doc/cheat_sheet.rdoc +225 -0
- data/doc/dataset_filtering.rdoc +257 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
- data/lib/sequel_core/adapters/ado.rb +3 -1
- data/lib/sequel_core/adapters/db2.rb +4 -2
- data/lib/sequel_core/adapters/dbi.rb +127 -113
- data/lib/sequel_core/adapters/informix.rb +4 -2
- data/lib/sequel_core/adapters/jdbc.rb +5 -3
- data/lib/sequel_core/adapters/mysql.rb +112 -46
- data/lib/sequel_core/adapters/odbc.rb +5 -7
- data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
- data/lib/sequel_core/adapters/openbase.rb +3 -1
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +261 -262
- data/lib/sequel_core/adapters/sqlite.rb +72 -22
- data/lib/sequel_core/connection_pool.rb +140 -73
- data/lib/sequel_core/core_ext.rb +201 -66
- data/lib/sequel_core/core_sql.rb +123 -153
- data/lib/sequel_core/database/schema.rb +156 -0
- data/lib/sequel_core/database.rb +321 -338
- data/lib/sequel_core/dataset/callback.rb +11 -12
- data/lib/sequel_core/dataset/convenience.rb +213 -240
- data/lib/sequel_core/dataset/pagination.rb +58 -43
- data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
- data/lib/sequel_core/dataset/query.rb +41 -0
- data/lib/sequel_core/dataset/schema.rb +15 -0
- data/lib/sequel_core/dataset/sequelizer.rb +41 -373
- data/lib/sequel_core/dataset/sql.rb +741 -632
- data/lib/sequel_core/dataset.rb +183 -168
- data/lib/sequel_core/deprecated.rb +1 -169
- data/lib/sequel_core/exceptions.rb +24 -19
- data/lib/sequel_core/migration.rb +44 -52
- data/lib/sequel_core/object_graph.rb +43 -42
- data/lib/sequel_core/pretty_table.rb +71 -76
- data/lib/sequel_core/schema/generator.rb +163 -105
- data/lib/sequel_core/schema/sql.rb +250 -93
- data/lib/sequel_core/schema.rb +2 -8
- data/lib/sequel_core/sql.rb +394 -0
- data/lib/sequel_core/worker.rb +37 -27
- data/lib/sequel_core.rb +99 -45
- data/spec/adapters/informix_spec.rb +0 -1
- data/spec/adapters/mysql_spec.rb +177 -124
- data/spec/adapters/oracle_spec.rb +0 -1
- data/spec/adapters/postgres_spec.rb +98 -58
- data/spec/adapters/sqlite_spec.rb +45 -4
- data/spec/blockless_filters_spec.rb +269 -0
- data/spec/connection_pool_spec.rb +21 -18
- data/spec/core_ext_spec.rb +169 -19
- data/spec/core_sql_spec.rb +56 -49
- data/spec/database_spec.rb +78 -17
- data/spec/dataset_spec.rb +300 -428
- data/spec/migration_spec.rb +1 -1
- data/spec/object_graph_spec.rb +5 -11
- data/spec/rcov.opts +1 -1
- data/spec/schema_generator_spec.rb +16 -4
- data/spec/schema_spec.rb +89 -10
- data/spec/sequelizer_spec.rb +56 -56
- data/spec/spec.opts +0 -5
- data/spec/spec_config.rb +7 -0
- data/spec/spec_config.rb.example +5 -5
- data/spec/spec_helper.rb +6 -0
- data/spec/worker_spec.rb +1 -1
- metadata +78 -63
@@ -1,37 +1,42 @@
|
|
1
1
|
module Sequel
|
2
2
|
# Represents an error raised in Sequel code.
|
3
|
-
class Error < StandardError
|
3
|
+
class Error < ::StandardError
|
4
4
|
|
5
|
-
#
|
6
|
-
class
|
7
|
-
|
8
|
-
# Rollback is a special error used to rollback a transactions.
|
9
|
-
# A transaction block will catch this error and wont pass further up the stack.
|
10
|
-
class Rollback < Error ; end
|
5
|
+
# Raised when Sequel is unable to load a specified adapter.
|
6
|
+
class AdapterNotFound < Error ; end
|
11
7
|
|
12
|
-
#
|
13
|
-
class
|
8
|
+
# Raise when an invalid expression is encountered inside a block filter.
|
9
|
+
class InvalidExpression < Error; end
|
14
10
|
|
15
|
-
# Represents an Invalid
|
16
|
-
class
|
11
|
+
# Represents an Invalid filter.
|
12
|
+
class InvalidFilter < Error ; end
|
17
13
|
|
14
|
+
# Represents an invalid join type.
|
15
|
+
class InvalidJoinType < Error ; end
|
16
|
+
|
18
17
|
# Raised on an invalid operation.
|
19
18
|
class InvalidOperation < Error; end
|
20
19
|
|
21
|
-
#
|
22
|
-
class
|
20
|
+
# Error raised when an invalid statement is executed.
|
21
|
+
class InvalidStatement < Error; end
|
22
|
+
|
23
|
+
# Represents an Invalid transform.
|
24
|
+
class InvalidTransform < Error ; end
|
23
25
|
|
24
|
-
|
26
|
+
# Represents an invalid value stored in the database.
|
27
|
+
class InvalidValue < Error ; end
|
25
28
|
|
26
29
|
# Represents an attempt to performing filter operations when no filter has been specified yet.
|
27
30
|
class NoExistingFilter < Error ; end
|
28
31
|
|
29
|
-
#
|
30
|
-
class
|
32
|
+
# There was an error while waiting on a connection from the connection pool
|
33
|
+
class PoolTimeoutError < Error ; end
|
31
34
|
|
32
|
-
|
35
|
+
# Rollback is a special error used to rollback a transactions.
|
36
|
+
# A transaction block will catch this error and won't pass further up the stack.
|
37
|
+
class Rollback < Error ; end
|
33
38
|
|
34
|
-
#
|
35
|
-
class
|
39
|
+
# Should be raised inside a worker loop to tell it to stop working.
|
40
|
+
class WorkerStop < RuntimeError ; end
|
36
41
|
end
|
37
42
|
end
|
@@ -1,6 +1,3 @@
|
|
1
|
-
# The migration code is based on work by Florian Aßmann:
|
2
|
-
# http://code.google.com/p/ruby-sequel/issues/detail?id=23
|
3
|
-
|
4
1
|
module Sequel
|
5
2
|
# The Migration class describes a database migration that can be reversed.
|
6
3
|
# The migration looks very similar to ActiveRecord (Rails) migrations, e.g.:
|
@@ -23,28 +20,14 @@ module Sequel
|
|
23
20
|
# To apply a migration to a database, you can invoke the #apply with
|
24
21
|
# the target database instance and the direction :up or :down, e.g.:
|
25
22
|
#
|
26
|
-
# DB = Sequel.open ('sqlite
|
23
|
+
# DB = Sequel.open ('sqlite://mydb')
|
27
24
|
# CreateSessions.apply(DB, :up)
|
28
|
-
#
|
29
25
|
class Migration
|
30
26
|
# Creates a new instance of the migration and sets the @db attribute.
|
31
27
|
def initialize(db)
|
32
28
|
@db = db
|
33
29
|
end
|
34
30
|
|
35
|
-
# Adds the new migration class to the list of Migration descendants.
|
36
|
-
def self.inherited(base)
|
37
|
-
descendants << base
|
38
|
-
end
|
39
|
-
|
40
|
-
# Returns the list of Migration descendants.
|
41
|
-
def self.descendants
|
42
|
-
@descendants ||= []
|
43
|
-
end
|
44
|
-
|
45
|
-
def up; end #:nodoc:
|
46
|
-
def down; end #:nodoc:
|
47
|
-
|
48
31
|
# Applies the migration to the supplied database in the specified
|
49
32
|
# direction.
|
50
33
|
def self.apply(db, direction)
|
@@ -59,9 +42,27 @@ module Sequel
|
|
59
42
|
end
|
60
43
|
end
|
61
44
|
|
45
|
+
# Returns the list of Migration descendants.
|
46
|
+
def self.descendants
|
47
|
+
@descendants ||= []
|
48
|
+
end
|
49
|
+
|
50
|
+
# Adds the new migration class to the list of Migration descendants.
|
51
|
+
def self.inherited(base)
|
52
|
+
descendants << base
|
53
|
+
end
|
54
|
+
|
55
|
+
# The default down action does nothing
|
56
|
+
def down
|
57
|
+
end
|
58
|
+
|
62
59
|
# Intercepts method calls intended for the database and sends them along.
|
63
60
|
def method_missing(method_sym, *args, &block)
|
64
|
-
@db.send
|
61
|
+
@db.send(method_sym, *args, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
# The default up action does nothing
|
65
|
+
def up
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
@@ -96,8 +97,9 @@ module Sequel
|
|
96
97
|
# To migrate the database from version 1 to version 5:
|
97
98
|
#
|
98
99
|
# Sequel::Migrator.apply(DB, '.', 5, 1)
|
99
|
-
#
|
100
100
|
module Migrator
|
101
|
+
MIGRATION_FILE_PATTERN = /\A\d+_.+\.rb\z/.freeze
|
102
|
+
|
101
103
|
# Migrates the supplied database in the specified directory from the
|
102
104
|
# current version to the target version. If no current version is
|
103
105
|
# supplied, it is extracted from a schema_info table. The schema_info
|
@@ -121,6 +123,19 @@ module Sequel
|
|
121
123
|
target
|
122
124
|
end
|
123
125
|
|
126
|
+
# Gets the current migration version stored in the database. If no version
|
127
|
+
# number is stored, 0 is returned.
|
128
|
+
def self.get_current_migration_version(db)
|
129
|
+
r = schema_info_dataset(db).first
|
130
|
+
r ? r[:version] : 0
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns the latest version available in the specified directory.
|
134
|
+
def self.latest_migration_version(directory)
|
135
|
+
l = migration_files(directory).last
|
136
|
+
l ? File.basename(l).to_i : nil
|
137
|
+
end
|
138
|
+
|
124
139
|
# Returns a list of migration classes filtered for the migration range and
|
125
140
|
# ordered according to the migration direction.
|
126
141
|
def self.migration_classes(directory, target, current, direction)
|
@@ -142,50 +157,27 @@ module Sequel
|
|
142
157
|
classes
|
143
158
|
end
|
144
159
|
|
145
|
-
MIGRATION_FILE_PATTERN = '[0-9][0-9][0-9]_*.rb'.freeze
|
146
|
-
|
147
160
|
# Returns any found migration files in the supplied directory.
|
148
161
|
def self.migration_files(directory, range = nil)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
m
|
162
|
+
files = []
|
163
|
+
Dir.new(directory).each do |file|
|
164
|
+
files[file.to_i] = File.join(directory, file) if MIGRATION_FILE_PATTERN.match(file)
|
153
165
|
end
|
154
166
|
filtered = range ? files[range] : files
|
155
167
|
filtered ? filtered.compact : []
|
156
168
|
end
|
157
169
|
|
158
|
-
# Returns the
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
# Gets the current migration version stored in the database. If no version
|
165
|
-
# number is stored, 0 is returned.
|
166
|
-
def self.get_current_migration_version(db)
|
167
|
-
r = schema_info_dataset(db).first
|
168
|
-
r ? r[:version] : 0
|
170
|
+
# Returns the dataset for the schema_info table. If no such table
|
171
|
+
# exists, it is automatically created.
|
172
|
+
def self.schema_info_dataset(db)
|
173
|
+
db.create_table(:schema_info) {integer :version} unless db.table_exists?(:schema_info)
|
174
|
+
db[:schema_info]
|
169
175
|
end
|
170
176
|
|
171
177
|
# Sets the current migration version stored in the database.
|
172
178
|
def self.set_current_migration_version(db, version)
|
173
179
|
dataset = schema_info_dataset(db)
|
174
|
-
|
175
|
-
dataset.update(:version => version)
|
176
|
-
else
|
177
|
-
dataset << {:version => version}
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
# Returns the dataset for the schema_info table. If no such table
|
182
|
-
# exists, it is automatically created.
|
183
|
-
def self.schema_info_dataset(db)
|
184
|
-
unless db.table_exists?(:schema_info)
|
185
|
-
db.create_table(:schema_info) {integer :version}
|
186
|
-
end
|
187
|
-
|
188
|
-
db[:schema_info]
|
180
|
+
dataset.send(dataset.first ? :update : :<<, :version => version)
|
189
181
|
end
|
190
182
|
end
|
191
183
|
end
|
@@ -68,7 +68,7 @@ module Sequel
|
|
68
68
|
raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
|
69
69
|
|
70
70
|
# Join the table early in order to avoid cloning the dataset twice
|
71
|
-
ds = join_table(options[:join_type] || :left_outer, table
|
71
|
+
ds = join_table(options[:join_type] || :left_outer, table, join_conditions, table_alias)
|
72
72
|
opts = ds.opts
|
73
73
|
|
74
74
|
# Whether to include the table in the result set
|
@@ -162,50 +162,51 @@ module Sequel
|
|
162
162
|
end
|
163
163
|
|
164
164
|
private
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
165
|
+
|
166
|
+
# Fetch the rows, split them into component table parts,
|
167
|
+
# tranform and run the row_proc on each part (if applicable),
|
168
|
+
# and yield a hash of the parts.
|
169
|
+
def graph_each(opts, &block)
|
170
|
+
# Reject tables with nil datasets, as they are excluded from
|
171
|
+
# the result set
|
172
|
+
datasets = @opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
|
173
|
+
# Get just the list of table aliases into a local variable, for speed
|
174
|
+
table_aliases = datasets.collect{|ta,ds| ta}
|
175
|
+
# Get an array of arrays, one for each dataset, with
|
176
|
+
# the necessary information about each dataset, for speed
|
177
|
+
datasets = datasets.collect do |ta, ds|
|
178
|
+
[ta, ds, ds.instance_variable_get(:@transform), ds.row_proc]
|
179
|
+
end
|
180
|
+
# Use the manually set graph aliases, if any, otherwise
|
181
|
+
# use the ones automatically created by .graph
|
182
|
+
column_aliases = @opts[:graph_aliases] || @opts[:graph][:column_aliases]
|
183
|
+
fetch_rows(select_sql(opts)) do |r|
|
184
|
+
graph = {}
|
185
|
+
# Create the sub hashes, one per table
|
186
|
+
table_aliases.each{|ta| graph[ta]={}}
|
187
|
+
# Split the result set based on the column aliases
|
188
|
+
# If there are columns in the result set that are
|
189
|
+
# not in column_aliases, they are ignored
|
190
|
+
column_aliases.each do |col_alias, tc|
|
191
|
+
ta, column = tc
|
192
|
+
graph[ta][column] = r[col_alias]
|
178
193
|
end
|
179
|
-
#
|
180
|
-
#
|
181
|
-
|
182
|
-
|
183
|
-
graph =
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
column_aliases.each do |col_alias, tc|
|
190
|
-
ta, column = tc
|
191
|
-
graph[ta][column] = r[col_alias]
|
192
|
-
end
|
193
|
-
# For each dataset, transform and run the row
|
194
|
-
# row_proc if applicable
|
195
|
-
datasets.each do |ta,ds,tr,rp|
|
196
|
-
g = graph[ta]
|
197
|
-
graph[ta] = if g.values.any?
|
198
|
-
g = ds.transform_load(g) if tr
|
199
|
-
g = rp[g] if rp
|
200
|
-
g
|
201
|
-
else
|
202
|
-
nil
|
203
|
-
end
|
194
|
+
# For each dataset, transform and run the row
|
195
|
+
# row_proc if applicable
|
196
|
+
datasets.each do |ta,ds,tr,rp|
|
197
|
+
g = graph[ta]
|
198
|
+
graph[ta] = if g.values.any?
|
199
|
+
g = ds.transform_load(g) if tr
|
200
|
+
g = rp[g] if rp
|
201
|
+
g
|
202
|
+
else
|
203
|
+
nil
|
204
204
|
end
|
205
|
-
|
206
|
-
yield graph
|
207
205
|
end
|
208
|
-
|
206
|
+
|
207
|
+
yield graph
|
209
208
|
end
|
209
|
+
self
|
210
|
+
end
|
210
211
|
end
|
211
212
|
end
|
@@ -1,76 +1,71 @@
|
|
1
|
-
module Sequel
|
2
|
-
module PrettyTable
|
3
|
-
# Prints nice-looking plain-text tables
|
4
|
-
#
|
5
|
-
# +--+-------+
|
6
|
-
# |id|name |
|
7
|
-
# |--+-------|
|
8
|
-
# |1 |fasdfas|
|
9
|
-
# |2 |test |
|
10
|
-
# +--+-------+
|
11
|
-
def self.print(records, columns = nil) # records is an array of hashes
|
12
|
-
columns ||=
|
13
|
-
sizes = column_sizes(records, columns)
|
14
|
-
|
15
|
-
|
16
|
-
puts
|
17
|
-
puts
|
18
|
-
|
19
|
-
puts
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
'|' + columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') + '|'
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
1
|
+
module Sequel
|
2
|
+
module PrettyTable
|
3
|
+
# Prints nice-looking plain-text tables via puts
|
4
|
+
#
|
5
|
+
# +--+-------+
|
6
|
+
# |id|name |
|
7
|
+
# |--+-------|
|
8
|
+
# |1 |fasdfas|
|
9
|
+
# |2 |test |
|
10
|
+
# +--+-------+
|
11
|
+
def self.print(records, columns = nil) # records is an array of hashes
|
12
|
+
columns ||= records.first.keys.sort_by{|x|x.to_s}
|
13
|
+
sizes = column_sizes(records, columns)
|
14
|
+
sep_line = separator_line(columns, sizes)
|
15
|
+
|
16
|
+
puts sep_line
|
17
|
+
puts header_line(columns, sizes)
|
18
|
+
puts sep_line
|
19
|
+
records.each {|r| puts data_line(columns, sizes, r)}
|
20
|
+
puts sep_line
|
21
|
+
end
|
22
|
+
|
23
|
+
### Private Module Methods ###
|
24
|
+
|
25
|
+
# Hash of the maximum size of the value for each column
|
26
|
+
def self.column_sizes(records, columns) # :nodoc:
|
27
|
+
sizes = Hash.new {0}
|
28
|
+
columns.each do |c|
|
29
|
+
s = c.to_s.size
|
30
|
+
sizes[c.to_sym] = s if s > sizes[c.to_sym]
|
31
|
+
end
|
32
|
+
records.each do |r|
|
33
|
+
columns.each do |c|
|
34
|
+
s = r[c].to_s.size
|
35
|
+
sizes[c.to_sym] = s if s > sizes[c.to_sym]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
sizes
|
39
|
+
end
|
40
|
+
|
41
|
+
# String for each data line
|
42
|
+
def self.data_line(columns, sizes, record) # :nodoc:
|
43
|
+
'|' << columns.map {|c| format_cell(sizes[c], record[c])}.join('|') << '|'
|
44
|
+
end
|
45
|
+
|
46
|
+
# Format the value so it takes up exactly size characters
|
47
|
+
def self.format_cell(size, v) # :nodoc:
|
48
|
+
case v
|
49
|
+
when Bignum, Fixnum
|
50
|
+
"%#{size}d" % v
|
51
|
+
when Float
|
52
|
+
"%#{size}g" % v
|
53
|
+
else
|
54
|
+
"%-#{size}s" % v.to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# String for header line
|
59
|
+
def self.header_line(columns, sizes) # :nodoc:
|
60
|
+
'|' << columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') << '|'
|
61
|
+
end
|
62
|
+
|
63
|
+
# String for separtor line
|
64
|
+
def self.separator_line(columns, sizes) # :nodoc:
|
65
|
+
'+' << columns.map {|c| '-' * sizes[c]}.join('+') << '+'
|
66
|
+
end
|
67
|
+
|
68
|
+
metaprivate :column_sizes, :data_line, :format_cell, :header_line, :separator_line
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|