sequel 2.6.0 → 2.7.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.
Files changed (50) hide show
  1. data/CHANGELOG +64 -0
  2. data/Rakefile +1 -1
  3. data/lib/sequel_core/adapters/jdbc.rb +6 -2
  4. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  5. data/lib/sequel_core/adapters/oracle.rb +4 -77
  6. data/lib/sequel_core/adapters/postgres.rb +39 -26
  7. data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
  8. data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
  9. data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
  10. data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
  11. data/lib/sequel_core/core_ext.rb +10 -0
  12. data/lib/sequel_core/core_sql.rb +7 -0
  13. data/lib/sequel_core/database.rb +22 -0
  14. data/lib/sequel_core/database/schema.rb +1 -1
  15. data/lib/sequel_core/dataset.rb +29 -11
  16. data/lib/sequel_core/dataset/sql.rb +27 -7
  17. data/lib/sequel_core/migration.rb +20 -2
  18. data/lib/sequel_core/object_graph.rb +24 -10
  19. data/lib/sequel_core/schema/generator.rb +22 -9
  20. data/lib/sequel_core/schema/sql.rb +13 -9
  21. data/lib/sequel_core/sql.rb +27 -2
  22. data/lib/sequel_model/association_reflection.rb +251 -141
  23. data/lib/sequel_model/associations.rb +114 -61
  24. data/lib/sequel_model/base.rb +25 -21
  25. data/lib/sequel_model/eager_loading.rb +17 -40
  26. data/lib/sequel_model/hooks.rb +25 -24
  27. data/lib/sequel_model/record.rb +29 -51
  28. data/lib/sequel_model/schema.rb +1 -1
  29. data/lib/sequel_model/validations.rb +13 -3
  30. data/spec/adapters/postgres_spec.rb +104 -18
  31. data/spec/adapters/spec_helper.rb +4 -1
  32. data/spec/integration/eager_loader_test.rb +5 -4
  33. data/spec/integration/spec_helper.rb +4 -1
  34. data/spec/sequel_core/connection_pool_spec.rb +24 -24
  35. data/spec/sequel_core/core_sql_spec.rb +12 -0
  36. data/spec/sequel_core/dataset_spec.rb +77 -2
  37. data/spec/sequel_core/expression_filters_spec.rb +6 -0
  38. data/spec/sequel_core/object_graph_spec.rb +40 -2
  39. data/spec/sequel_core/schema_spec.rb +13 -0
  40. data/spec/sequel_model/association_reflection_spec.rb +8 -8
  41. data/spec/sequel_model/associations_spec.rb +164 -3
  42. data/spec/sequel_model/caching_spec.rb +2 -1
  43. data/spec/sequel_model/eager_loading_spec.rb +107 -3
  44. data/spec/sequel_model/hooks_spec.rb +38 -22
  45. data/spec/sequel_model/model_spec.rb +11 -35
  46. data/spec/sequel_model/plugins_spec.rb +4 -2
  47. data/spec/sequel_model/record_spec.rb +8 -5
  48. data/spec/sequel_model/validations_spec.rb +25 -0
  49. data/spec/spec_config.rb +4 -3
  50. metadata +21 -19
data/CHANGELOG CHANGED
@@ -1,3 +1,67 @@
1
+ === 2.7.0 (2008-11-03)
2
+
3
+ * Transform AssociationReflection from a single class to a class hierarchy (jeremyevans)
4
+
5
+ * Optimize Date object creation in PostgreSQL adapter (jeremyevans)
6
+
7
+ * Allow easier creation of custom association types, though support for them may still be suboptimal (jeremyevans)
8
+
9
+ * Add :eager_grapher option to associations, which the user can use to override the default eager_graph code (jeremyevans)
10
+
11
+ * Associations are now inherited when a model class is subclassed (jeremyevans)
12
+
13
+ * Instance methods added by associations are now added to an anonymous module the class includes, allowing you to override them and use super (jeremyevans)
14
+
15
+ * Add #add_graph_aliases (select_more for graphs), and allow use of arbitrary expressions when graphing (jeremyevans)
16
+
17
+ * Fix a corner case where the wrong table name is used in eager_graph (jeremyevans)
18
+
19
+ * Make Dataset#join_table take an option hash instead of a table_alias argument, add support for :implicit_qualifier option (jeremyevans)
20
+
21
+ * Add :left_primary_key and :right_primary_key options to many_to_many associations (jeremyevans)
22
+
23
+ * Add :primary_key option to one_to_many and many_to_one associations (jeremyevans)
24
+
25
+ * Make after_load association callbacks take effect when eager loading via eager (jeremyevans)
26
+
27
+ * Add a :uniq association option to many_to_many associations (jeremyevans)
28
+
29
+ * Support using any expression as the argument to Symbol#like (jeremyevans)
30
+
31
+ * Much better support for multiple schemas in PostgreSQL (jeremyevans) (#243)
32
+
33
+ * The first argument to Model#initalize can no longer be nil, it must be a hash if it is given (jeremyevans)
34
+
35
+ * Remove Sequel::Model.lazy_load_schema= setting (jeremyevans)
36
+
37
+ * Lazily load model instance options such as raise_on_save_failure, for better performance (jeremyevans)
38
+
39
+ * Make Model::Validiation::Errors more Rails-compatible (jeremyevans)
40
+
41
+ * Refactor model hooks for performance (jeremyevans)
42
+
43
+ * Major performance enhancement when fetching rows using PostgreSQL (jeremyevans)
44
+
45
+ * Don't typecast serialized columns in models (jeremyevans)
46
+
47
+ * Add Array#sql_array to handle ruby arrays of all two pairs as SQL arrays (jeremyevans) (#245)
48
+
49
+ * Add ComplexExpression#== and #eql?, for checking equality (rubymage) (#244)
50
+
51
+ * Allow full text search on PostgreSQL to include rows where a search column is NULL (jeremyevans)
52
+
53
+ * PostgreSQL full text search queries with multiple columns are joined with space to prevent joining border words to one (michalbugno)
54
+
55
+ * Don't modify a dataset's cached column information if calling #each with an option that modifies the columns (jeremyevans)
56
+
57
+ * The PostgreSQL adapter will now generally default to using a unix socket in /tmp if no host is specified, instead of a tcp socket to localhost (jeremyevans)
58
+
59
+ * Make Dataset#sql call Dataset#select_sql instead of being an alias, to allow for easier subclassing (jeremyevans)
60
+
61
+ * Split Oracle adapter into shared and unshared parts, so Oracle is better supported when using JDBC (jeremyevans)
62
+
63
+ * Fix automatic loading of Oracle driver when using JDBC adapter (bburton333) (#242)
64
+
1
65
  === 2.6.0 (2008-10-11)
2
66
 
3
67
  * Make the sqlite adapter respect the Sequel.datetime_class setting, for timestamp and datetime types (jeremyevans)
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ require "fileutils"
12
12
  include FileUtils
13
13
 
14
14
  NAME = 'sequel'
15
- VERS = '2.6.0'
15
+ VERS = '2.7.0'
16
16
  CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage", "www/public/*.html"]
17
17
  RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
18
18
  'Sequel: The Database Toolkit for Ruby', '--main', 'README']
@@ -3,7 +3,7 @@ require 'java'
3
3
  module Sequel
4
4
  # Houses Sequel's JDBC support when running on JRuby.
5
5
  # Support for individual database types is done using sub adapters.
6
- # PostgreSQL, MySQL, SQLite, and MSSQL all have relatively good support,
6
+ # PostgreSQL, MySQL, SQLite, Oracle, and MSSQL all have relatively good support,
7
7
  # close the the level supported by the native adapter.
8
8
  # PostgreSQL, MySQL, SQLite can load necessary support using
9
9
  # the jdbc-* gem, if it is installed, though they will work if you
@@ -51,7 +51,11 @@ module Sequel
51
51
  JDBC.load_gem('sqlite3')
52
52
  org.sqlite.JDBC
53
53
  end,
54
- :oracle=>proc{oracle.jdbc.driver.OracleDriver},
54
+ :oracle=>proc do |db|
55
+ require 'sequel_core/adapters/jdbc/oracle'
56
+ db.extend(Sequel::JDBC::Oracle::DatabaseMethods)
57
+ Java::oracle.jdbc.driver.OracleDriver
58
+ end,
55
59
  :sqlserver=>proc do |db|
56
60
  require 'sequel_core/adapters/shared/mssql'
57
61
  db.extend(Sequel::MSSQL::DatabaseMethods)
@@ -0,0 +1,23 @@
1
+ require 'sequel_core/adapters/shared/oracle'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ # Database and Dataset support for Oracle databases accessed via JDBC.
6
+ module Oracle
7
+ # Instance methods for Oracle Database objects accessed via JDBC.
8
+ module DatabaseMethods
9
+ include Sequel::Oracle::DatabaseMethods
10
+
11
+ # Return Sequel::JDBC::Oracle::Dataset object with the given opts.
12
+ def dataset(opts=nil)
13
+ Sequel::JDBC::Oracle::Dataset.new(self, opts)
14
+ end
15
+ end
16
+
17
+ # Dataset class for Oracle datasets accessed via JDBC.
18
+ class Dataset < JDBC::Dataset
19
+ include Sequel::Oracle::DatasetMethods
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,8 +1,10 @@
1
1
  require 'oci8'
2
+ require 'sequel_core/adapters/shared/oracle'
2
3
 
3
4
  module Sequel
4
5
  module Oracle
5
6
  class Database < Sequel::Database
7
+ include DatabaseMethods
6
8
  set_adapter_scheme :oracle
7
9
 
8
10
  def connect(server)
@@ -37,16 +39,6 @@ module Sequel
37
39
  end
38
40
  alias_method :do, :execute
39
41
 
40
- def tables
41
- from(:tab).select(:tname).filter(:tabtype => 'TABLE').map do |r|
42
- r[:tname].downcase.to_sym
43
- end
44
- end
45
-
46
- def table_exists?(name)
47
- from(:tab).filter(:tname => name.to_s.upcase, :tabtype => 'TABLE').count > 0
48
- end
49
-
50
42
  def transaction(server=nil)
51
43
  synchronize(server) do |conn|
52
44
  return yield(conn) if @transactions.include?(Thread.current)
@@ -68,6 +60,8 @@ module Sequel
68
60
  end
69
61
 
70
62
  class Dataset < Sequel::Dataset
63
+ include DatasetMethods
64
+
71
65
  def literal(v)
72
66
  case v
73
67
  when OraDate
@@ -92,73 +86,6 @@ module Sequel
92
86
  end
93
87
  self
94
88
  end
95
-
96
- def empty?
97
- db[:dual].where(exists).get(1) == nil
98
- end
99
-
100
- # Formats a SELECT statement using the given options and the dataset
101
- # options.
102
- def select_sql(opts = nil)
103
- opts = opts ? @opts.merge(opts) : @opts
104
-
105
- if sql = opts[:sql]
106
- return sql
107
- end
108
-
109
- columns = opts[:select]
110
- select_columns = columns ? column_list(columns) : WILDCARD
111
- sql = opts[:distinct] ? \
112
- "SELECT DISTINCT #{select_columns}" : \
113
- "SELECT #{select_columns}"
114
-
115
- if opts[:from]
116
- sql << " FROM #{source_list(opts[:from])}"
117
- end
118
-
119
- if join = opts[:join]
120
- join.each{|j| sql << literal(j)}
121
- end
122
-
123
- if where = opts[:where]
124
- sql << " WHERE #{literal(where)}"
125
- end
126
-
127
- if group = opts[:group]
128
- sql << " GROUP BY #{expression_list(group)}"
129
- end
130
-
131
- if having = opts[:having]
132
- sql << " HAVING #{literal(having)}"
133
- end
134
-
135
- if union = opts[:union]
136
- sql << (opts[:union_all] ? \
137
- " UNION ALL #{union.sql}" : " UNION #{union.sql}")
138
- elsif intersect = opts[:intersect]
139
- sql << (opts[:intersect_all] ? \
140
- " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
141
- elsif except = opts[:except]
142
- sql << (opts[:except_all] ? \
143
- " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
144
- end
145
-
146
- if order = opts[:order]
147
- sql << " ORDER BY #{expression_list(order)}"
148
- end
149
-
150
- if limit = opts[:limit]
151
- if (offset = opts[:offset]) && (offset > 0)
152
- sql = "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}"
153
- else
154
- sql = "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
155
- end
156
- end
157
-
158
- sql
159
- end
160
-
161
- alias sql select_sql
162
89
  end
163
90
  end
164
91
  end
@@ -83,7 +83,7 @@ module Sequel
83
83
 
84
84
  # Hash with integer keys and proc values for converting PostgreSQL types.
85
85
  PG_TYPES = {
86
- 16 => lambda{ |s| Postgres.string_to_bool(s) }, # boolean
86
+ 16 => lambda{ |s| s == 't' }, # boolean
87
87
  17 => lambda{ |s| Adapter.unescape_bytea(s).to_blob }, # bytea
88
88
  20 => lambda{ |s| s.to_i }, # int8
89
89
  21 => lambda{ |s| s.to_i }, # int2
@@ -93,7 +93,7 @@ module Sequel
93
93
  700 => lambda{ |s| s.to_f }, # float4
94
94
  701 => lambda{ |s| s.to_f }, # float8
95
95
  790 => lambda{ |s| s.to_d }, # money
96
- 1082 => lambda{ |s| s.to_date }, # date
96
+ 1082 => lambda{ |s| @use_iso_date_format ? Date.new(*s.split("-").map{|x| x.to_i}) : s.to_date }, # date
97
97
  1083 => lambda{ |s| s.to_time }, # time without time zone
98
98
  1114 => lambda{ |s| s.to_sequel_time }, # timestamp without time zone
99
99
  1184 => lambda{ |s| s.to_sequel_time }, # timestamp with time zone
@@ -102,16 +102,12 @@ module Sequel
102
102
  1700 => lambda{ |s| s.to_d }, # numeric
103
103
  }
104
104
 
105
- # Module method for converting a PostgreSQL string to a boolean value.
106
- def self.string_to_bool(s)
107
- if(s.blank?)
108
- nil
109
- elsif(s.downcase == 't' || s.downcase == 'true')
110
- true
111
- else
112
- false
113
- end
114
- end
105
+ @use_iso_date_format = true
106
+
107
+ # As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
108
+ # the date in a known format that Sequel can parse faster. This can be turned off
109
+ # if you require a date style other than ISO.
110
+ metaattr_accessor :use_iso_date_format
115
111
 
116
112
  # PGconn subclass for connection specific methods used with the
117
113
  # pg, postgres, or postgres-pr driver.
@@ -119,6 +115,13 @@ module Sequel
119
115
  include Sequel::Postgres::AdapterMethods
120
116
  self.translate_results = false if respond_to?(:translate_results=)
121
117
 
118
+ # Apply connection settings for this connection. Current sets
119
+ # the date style to ISO in order make Date object creation in ruby faster,
120
+ # if Postgres.use_iso_date_format is true.
121
+ def apply_connection_settings
122
+ async_exec("SET DateStyle = 'ISO, YMD'") if Postgres.use_iso_date_format
123
+ end
124
+
122
125
  # Execute the given SQL with this connection. If a block is given,
123
126
  # yield the results, otherwise, return the number of changed rows.
124
127
  def execute(sql, args=nil)
@@ -138,6 +141,12 @@ module Sequel
138
141
  q.clear
139
142
  end
140
143
  end
144
+
145
+ # Reapply the connection settings if the connection is reset.
146
+ def reset(*args, &block)
147
+ super(*args, &block)
148
+ apply_connection_settings
149
+ end
141
150
 
142
151
  if SEQUEL_POSTGRES_USES_PG
143
152
  # Hash of prepared statements for this connection. Keys are
@@ -170,14 +179,14 @@ module Sequel
170
179
  @primary_keys = {}
171
180
  @primary_key_sequences = {}
172
181
  end
173
-
182
+
174
183
  # Connects to the database. In addition to the standard database
175
184
  # options, using the :encoding or :charset option changes the
176
185
  # client encoding for the connection.
177
186
  def connect(server)
178
187
  opts = server_opts(server)
179
188
  conn = Adapter.connect(
180
- opts[:host] || 'localhost',
189
+ (opts[:host] unless opts[:host].blank?),
181
190
  opts[:port] || 5432,
182
191
  nil, '',
183
192
  opts[:database],
@@ -185,8 +194,13 @@ module Sequel
185
194
  opts[:password]
186
195
  )
187
196
  if encoding = opts[:encoding] || opts[:charset]
188
- conn.set_client_encoding(encoding)
197
+ if conn.respond_to?(:set_client_encoding)
198
+ conn.set_client_encoding(encoding)
199
+ else
200
+ conn.async_exec("set client_encoding to '#{encoding}'")
201
+ end
189
202
  end
203
+ conn.apply_connection_settings
190
204
  conn.db = self
191
205
  conn
192
206
  end
@@ -280,21 +294,20 @@ module Sequel
280
294
  class Dataset < Sequel::Dataset
281
295
  include Sequel::Postgres::DatasetMethods
282
296
 
283
- # yield all rows returned by executing the given SQL and converting
297
+ # Yield all rows returned by executing the given SQL and converting
284
298
  # the types.
285
299
  def fetch_rows(sql)
286
- @columns = []
300
+ cols = []
287
301
  execute(sql) do |res|
288
- (0...res.ntuples).each do |recnum|
302
+ res.nfields.times do |fieldnum|
303
+ cols << [fieldnum, PG_TYPES[res.ftype(fieldnum)], res.fname(fieldnum).to_sym]
304
+ end
305
+ @columns = cols.map{|c| c.at(2)}
306
+ res.ntuples.times do |recnum|
289
307
  converted_rec = {}
290
- (0...res.nfields).each do |fieldnum|
291
- fieldsym = res.fname(fieldnum).to_sym
292
- @columns << fieldsym
293
- converted_rec[fieldsym] = if value = res.getvalue(recnum,fieldnum)
294
- (PG_TYPES[res.ftype(fieldnum)] || lambda{|s| s.to_s}).call(value)
295
- else
296
- value
297
- end
308
+ cols.each do |fieldnum, type_proc, fieldsym|
309
+ value = res.getvalue(recnum, fieldnum)
310
+ converted_rec[fieldsym] = (value && type_proc) ? type_proc.call(value) : value
298
311
  end
299
312
  yield converted_rec
300
313
  end
@@ -120,7 +120,6 @@ module Sequel
120
120
 
121
121
  sql
122
122
  end
123
- alias_method :sql, :select_sql
124
123
  end
125
124
  end
126
125
  end
@@ -156,7 +156,7 @@ module Sequel
156
156
 
157
157
  # Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.
158
158
  # Raises an error on use of :full_outer type, since MySQL doesn't support it.
159
- def join_table(type, table, expr=nil, table_alias=nil)
159
+ def join_table(type, table, expr=nil, table_alias={})
160
160
  type = :inner if (type == :cross) && !expr.nil?
161
161
  raise(Sequel::Error, "MySQL doesn't support FULL OUTER JOIN") if type == :full_outer
162
162
  super(type, table, expr, table_alias)
@@ -0,0 +1,82 @@
1
+ module Sequel
2
+ module Oracle
3
+ module DatabaseMethods
4
+ def tables
5
+ from(:tab).select(:tname).filter(:tabtype => 'TABLE').map do |r|
6
+ r[:tname].downcase.to_sym
7
+ end
8
+ end
9
+
10
+ def table_exists?(name)
11
+ from(:tab).filter(:tname => name.to_s.upcase, :tabtype => 'TABLE').count > 0
12
+ end
13
+ end
14
+
15
+ module DatasetMethods
16
+ def empty?
17
+ db[:dual].where(exists).get(1) == nil
18
+ end
19
+
20
+ # Formats a SELECT statement using the given options and the dataset
21
+ # options.
22
+ def select_sql(opts = nil)
23
+ opts = opts ? @opts.merge(opts) : @opts
24
+
25
+ if sql = opts[:sql]
26
+ return sql
27
+ end
28
+
29
+ columns = opts[:select]
30
+ select_columns = columns ? column_list(columns) : '*'
31
+ sql = opts[:distinct] ? \
32
+ "SELECT DISTINCT #{select_columns}" : \
33
+ "SELECT #{select_columns}"
34
+
35
+ if opts[:from]
36
+ sql << " FROM #{source_list(opts[:from])}"
37
+ end
38
+
39
+ if join = opts[:join]
40
+ join.each{|j| sql << literal(j)}
41
+ end
42
+
43
+ if where = opts[:where]
44
+ sql << " WHERE #{literal(where)}"
45
+ end
46
+
47
+ if group = opts[:group]
48
+ sql << " GROUP BY #{expression_list(group)}"
49
+ end
50
+
51
+ if having = opts[:having]
52
+ sql << " HAVING #{literal(having)}"
53
+ end
54
+
55
+ if union = opts[:union]
56
+ sql << (opts[:union_all] ? \
57
+ " UNION ALL #{union.sql}" : " UNION #{union.sql}")
58
+ elsif intersect = opts[:intersect]
59
+ sql << (opts[:intersect_all] ? \
60
+ " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
61
+ elsif except = opts[:except]
62
+ sql << (opts[:except_all] ? \
63
+ " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
64
+ end
65
+
66
+ if order = opts[:order]
67
+ sql << " ORDER BY #{expression_list(order)}"
68
+ end
69
+
70
+ if limit = opts[:limit]
71
+ if (offset = opts[:offset]) && (offset > 0)
72
+ sql = "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}"
73
+ else
74
+ sql = "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
75
+ end
76
+ end
77
+
78
+ sql
79
+ end
80
+ end
81
+ end
82
+ end