sequel 3.14.0 → 3.15.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 (45) hide show
  1. data/CHANGELOG +30 -0
  2. data/README.rdoc +2 -2
  3. data/doc/cheat_sheet.rdoc +1 -1
  4. data/doc/release_notes/3.15.0.txt +78 -0
  5. data/lib/sequel/adapters/do/postgres.rb +1 -1
  6. data/lib/sequel/adapters/jdbc.rb +15 -7
  7. data/lib/sequel/adapters/jdbc/mssql.rb +22 -0
  8. data/lib/sequel/adapters/mysql.rb +2 -6
  9. data/lib/sequel/adapters/mysql2.rb +176 -0
  10. data/lib/sequel/adapters/odbc.rb +1 -1
  11. data/lib/sequel/adapters/postgres.rb +13 -0
  12. data/lib/sequel/adapters/shared/mysql.rb +9 -1
  13. data/lib/sequel/adapters/shared/postgres.rb +1 -1
  14. data/lib/sequel/adapters/shared/sqlite.rb +41 -13
  15. data/lib/sequel/core.rb +8 -0
  16. data/lib/sequel/database/connecting.rb +1 -1
  17. data/lib/sequel/dataset/actions.rb +1 -1
  18. data/lib/sequel/dataset/misc.rb +32 -0
  19. data/lib/sequel/dataset/query.rb +3 -1
  20. data/lib/sequel/extensions/named_timezones.rb +1 -2
  21. data/lib/sequel/model.rb +2 -1
  22. data/lib/sequel/model/associations.rb +3 -1
  23. data/lib/sequel/model/base.rb +44 -4
  24. data/lib/sequel/plugins/active_model.rb +5 -3
  25. data/lib/sequel/plugins/class_table_inheritance.rb +3 -3
  26. data/lib/sequel/version.rb +1 -1
  27. data/spec/adapters/mysql_spec.rb +12 -2
  28. data/spec/adapters/sqlite_spec.rb +31 -1
  29. data/spec/core/dataset_spec.rb +61 -1
  30. data/spec/core/spec_helper.rb +1 -0
  31. data/spec/extensions/active_model_spec.rb +10 -0
  32. data/spec/extensions/lazy_attributes_spec.rb +19 -11
  33. data/spec/extensions/named_timezones_spec.rb +13 -11
  34. data/spec/extensions/spec_helper.rb +1 -5
  35. data/spec/integration/associations_test.rb +23 -0
  36. data/spec/integration/dataset_test.rb +3 -3
  37. data/spec/integration/plugin_test.rb +6 -0
  38. data/spec/integration/schema_test.rb +7 -0
  39. data/spec/integration/timezone_test.rb +2 -2
  40. data/spec/integration/type_test.rb +1 -1
  41. data/spec/model/associations_spec.rb +7 -0
  42. data/spec/model/base_spec.rb +6 -6
  43. data/spec/model/model_spec.rb +38 -3
  44. data/spec/model/record_spec.rb +24 -0
  45. metadata +7 -4
data/CHANGELOG CHANGED
@@ -1,3 +1,33 @@
1
+ === 3.15.0 (2010-09-01)
2
+
3
+ * Make emulated alter_table tasks on SQLite correctly preserve foreign keys (DirtYiCE, jeremyevans)
4
+
5
+ * Add support for sequel_pg to the native postgres adapter when pg is used (jeremyevans)
6
+
7
+ * Make class MyModel < Sequel::Model(DB[:table]) reload safe (jeremyevans)
8
+
9
+ * Fix a possible error when using the do (DataObjects) adapter with postgres (jeremyevans)
10
+
11
+ * Handle a many_to_many :join_table option that uses an implicit alias (mluu, jeremyevans)
12
+
13
+ * Work around bug in Microsoft's SQL Server JDBC Adapter version 3.0 (jfirebaugh)
14
+
15
+ * Make eager graphing a model that uses an aliased table name work correctly (jeremyevans)
16
+
17
+ * Make class_table_inheritance plugin work with non integer primary keys on SQLite (jeremyevans, russm)
18
+
19
+ * Add :auto_increment field to column schema values on MySQL if the column is auto incrementing (dbd)
20
+
21
+ * Handle DSN-less ODBC connections better (Ricardo Ramalho)
22
+
23
+ * Exclude temporary tables when parsing the schema on PostgreSQL (jeremyevans) (#306)
24
+
25
+ * Add Mysql2 adapter (brianmario)
26
+
27
+ * Handle Mysql::Error exceptions when disconnecting in the MySQL adapter (jeremyevans)
28
+
29
+ * Make typecasting work correctly for attributes loaded lazily when using the lazy attributes plugin (jeremyevans)
30
+
1
31
  === 3.14.0 (2010-08-02)
2
32
 
3
33
  * Handle OCIInvalidHandle errors when disconnecting in the Oracle adapter (jeremyevans)
data/README.rdoc CHANGED
@@ -12,8 +12,8 @@ toolkit for Ruby.
12
12
  two-phase commit, transaction isolation, master/slave
13
13
  configurations, and database sharding.
14
14
  * Sequel currently has adapters for ADO, Amalgalite, DataObjects,
15
- DB2, DBI, Firebird, Informix, JDBC, MySQL, ODBC, OpenBase, Oracle,
16
- PostgreSQL and SQLite3.
15
+ DB2, DBI, Firebird, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase,
16
+ Oracle, PostgreSQL and SQLite3.
17
17
 
18
18
  == Resources
19
19
 
data/doc/cheat_sheet.rdoc CHANGED
@@ -29,7 +29,7 @@ Without a filename argument, the sqlite adapter will setup a new sqlite database
29
29
  dataset = DB["SELECT age FROM users WHERE name = ?", name]
30
30
  dataset.map(:age)
31
31
  DB.fetch("SELECT name FROM users") do |row|
32
- p r[:name]
32
+ p row[:name]
33
33
  end
34
34
 
35
35
  == Create a dataset
@@ -0,0 +1,78 @@
1
+ = Performance Enhancements
2
+
3
+ * A mysql2 adapter was added to Sequel. It offers a large (2-6x)
4
+ performance increase over the standard mysql adapter. In order to
5
+ use it, you need to install mysql2, and change your connection
6
+ strings to use mysql2:// instead of mysql://.
7
+
8
+ * Support for sequel_pg was added to the native postgres adapter,
9
+ when pg is being used as the backend. sequel_pg also offers a
10
+ large (2-6x) performance increase over the default row fetching
11
+ code that the Sequel postgres adapter uses. In order to use it,
12
+ you just need to install sequel_pg, and the postgres adapter will
13
+ pick it up automatically.
14
+
15
+ * Mass assignment has been made about 10x faster by caching the
16
+ allowed setter methods in the model.
17
+
18
+ = Other Improvements
19
+
20
+ * The following construct is now safe to use in environments that
21
+ reload code without unloading existing constants:
22
+
23
+ class MyModel < Sequel::Model(DB[:table])
24
+ end
25
+
26
+ Previously, this would raise a superclass mismatch TypeError.
27
+
28
+ * Sequel now handles the case where both an implicit and an explicit
29
+ table alias are given to join_table, preferring the explicit alias.
30
+ This can happen if you are using models with aliased table names
31
+ and eager graphing them. Previously, this would result in invalid
32
+ SQL, with both aliases being used.
33
+
34
+ * You can use use an aliased table for the :join_table option
35
+ of a many_to_many association.
36
+
37
+ * The active_model plugin now supports the final release of
38
+ ActiveModel 3.0.0.
39
+
40
+ * Typecasting now works correctly for attributes loaded lazily
41
+ when using the lazy_attributes plugin.
42
+
43
+ * The class_table_inheritance plugin now works with non-integer
44
+ primary keys on SQLite.
45
+
46
+ * Temporary tables are now ignored when parsing the schema on
47
+ PostgreSQL.
48
+
49
+ * On MySQL, an :auto_increment key with a true value is added to
50
+ the Database#schema output hash if the related column is
51
+ auto incrementing.
52
+
53
+ * The mysql adapter now handles Mysql::Error exceptions raised when
54
+ disconnecting.
55
+
56
+ * On SQLite, emulated alter_table commands that require dropping
57
+ the table now preserve the foreign key information, if SQLite
58
+ foreign key support is enabled (it is by default).
59
+
60
+ * DSN-less connections now work correctly in more cases in the
61
+ ODBC adapter.
62
+
63
+ * A workaround has been added for a bug in the Microsoft SQL
64
+ Server JDBC Driver 3.0, involving it incorrectly returning a
65
+ smallint instead of a char type for the IS_AUTOINCREMENT
66
+ metadata value.
67
+
68
+ * An bug in the error handling when connecting to PostgreSQL using
69
+ the do (DataObjects) adapter has been fixed.
70
+
71
+ = Backwards Compatibility
72
+
73
+ * The caching of allowed mass assignment methods can result in the
74
+ incorrect exception class being raised if you manually undefine
75
+ instance setter methods in the model class. If you do this, you
76
+ need to clear the setter methods cache manually:
77
+
78
+ MyModel.clear_setter_methods_cache
@@ -27,7 +27,7 @@ module Sequel
27
27
  @db.log_yield(sql){command.execute_non_query}
28
28
  end
29
29
  rescue ::DataObjects::Error => e
30
- raise_error(e)
30
+ @db.send(:raise_error, e)
31
31
  end
32
32
  end
33
33
 
@@ -572,19 +572,27 @@ module Sequel
572
572
  i = 0
573
573
  meta.getColumnCount.times{cols << [output_identifier(meta.getColumnLabel(i+=1)), i]}
574
574
  @columns = cols.map{|c| c.at(0)}
575
- row = {}
576
- blk = if @convert_types
577
- lambda{|n, i| row[n] = convert_type(result.getObject(i))}
578
- else
579
- lambda{|n, i| row[n] = result.getObject(i)}
580
- end
575
+ blk = result_set_object_getter
581
576
  # get rows
582
577
  while result.next
583
578
  row = {}
584
- cols.each(&blk)
579
+ cols.each do |n, i|
580
+ row[n] = blk.call(result, n, i)
581
+ end
585
582
  yield row
586
583
  end
587
584
  end
585
+
586
+ # Proc that takes the ResultSet and an integer field number and returns
587
+ # the object for that field. Respects the @convert_types setting to
588
+ # convert Java types to ruby types.
589
+ def result_set_object_getter
590
+ if @convert_types
591
+ lambda {|result, n, i| convert_type(result.getObject(i))}
592
+ else
593
+ lambda {|result, n, i| result.getObject(i)}
594
+ end
595
+ end
588
596
  end
589
597
  end
590
598
  end
@@ -46,6 +46,28 @@ module Sequel
46
46
  def primary_key_index_re
47
47
  PRIMARY_KEY_INDEX_RE
48
48
  end
49
+
50
+ def metadata_dataset
51
+ ds = super
52
+ # Work around a bug in SQL Server JDBC Driver 3.0, where the metadata
53
+ # for the getColumns result set specifies an incorrect type for the
54
+ # IS_AUTOINCREMENT column. The column is a string, but the type is
55
+ # specified as a short. This causes getObject() to throw a
56
+ # com.microsoft.sqlserver.jdbc.SQLServerException: "The conversion
57
+ # from char to SMALLINT is unsupported." Using getString() rather
58
+ # than getObject() for this column avoids the problem.
59
+ # Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
60
+ def ds.result_set_object_getter
61
+ lambda do |result, n, i|
62
+ if n == :is_autoincrement
63
+ @convert_types ? convert_type(result.getString(i)) : result.getString(i)
64
+ else
65
+ @convert_types ? convert_type(result.getObject(i)) : result.getObject(i)
66
+ end
67
+ end
68
+ end
69
+ ds
70
+ end
49
71
  end
50
72
 
51
73
  # Dataset class for MSSQL datasets accessed via JDBC.
@@ -29,7 +29,6 @@ module Sequel
29
29
  end
30
30
 
31
31
  @convert_invalid_date_time = false
32
- @convert_tinyint_to_bool = true
33
32
 
34
33
  class << self
35
34
  # By default, Sequel raises an exception if in invalid date or time is used.
@@ -37,11 +36,6 @@ module Sequel
37
36
  # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
38
37
  # it returns the strings as is.
39
38
  attr_accessor :convert_invalid_date_time
40
-
41
- # Sequel converts the column type tinyint(1) to a boolean by default when
42
- # using the native MySQL adapter. You can turn off the conversion by setting
43
- # this to false.
44
- attr_accessor :convert_tinyint_to_bool
45
39
  end
46
40
 
47
41
  # If convert_invalid_date_time is nil, :nil, or :string and
@@ -227,6 +221,8 @@ module Sequel
227
221
  # Closes given database connection.
228
222
  def disconnect_connection(c)
229
223
  c.close
224
+ rescue Mysql::Error
225
+ nil
230
226
  end
231
227
 
232
228
  # Executes a prepared statement on an available connection. If the
@@ -0,0 +1,176 @@
1
+ require 'mysql2' unless defined? Mysql2
2
+
3
+ Sequel.require %w'shared/mysql', 'adapters'
4
+
5
+ module Sequel
6
+ # Module for holding all Mysql2-related classes and modules for Sequel.
7
+ module Mysql2
8
+ # Database class for MySQL databases used with Sequel.
9
+ class Database < Sequel::Database
10
+ include Sequel::MySQL::DatabaseMethods
11
+
12
+ # Mysql::Error messages that indicate the current connection should be disconnected
13
+ MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away)/
14
+
15
+ set_adapter_scheme :mysql2
16
+
17
+ # Connect to the database. In addition to the usual database options,
18
+ # the following options have effect:
19
+ #
20
+ # * :auto_is_null - Set to true to use MySQL default behavior of having
21
+ # a filter for an autoincrement column equals NULL to return the last
22
+ # inserted row.
23
+ # * :charset - Same as :encoding (:encoding takes precendence)
24
+ # * :compress - Set to false to not compress results from the server
25
+ # * :config_default_group - The default group to read from the in
26
+ # the MySQL config file.
27
+ # * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
28
+ # option on the connection with the given value.
29
+ # * :encoding - Set all the related character sets for this
30
+ # connection (connection, client, database, server, and results).
31
+ # * :socket - Use a unix socket file instead of connecting via TCP/IP.
32
+ # * :timeout - Set the timeout in seconds before the server will
33
+ # disconnect this connection.
34
+ def connect(server)
35
+ opts = server_opts(server)
36
+ opts[:host] ||= 'localhost'
37
+ opts[:username] ||= opts[:user]
38
+ conn = ::Mysql2::Client.new(opts)
39
+
40
+ sqls = []
41
+ # Set encoding a slightly different way after connecting,
42
+ # in case the READ_DEFAULT_GROUP overrode the provided encoding.
43
+ # Doesn't work across implicit reconnects, but Sequel doesn't turn on
44
+ # that feature.
45
+ if encoding = opts[:encoding] || opts[:charset]
46
+ sqls << "SET NAMES #{conn.escape(encoding.to_s)}"
47
+ end
48
+
49
+ # increase timeout so mysql server doesn't disconnect us
50
+ sqls << "SET @@wait_timeout = #{opts[:timeout] || 2592000}"
51
+
52
+ # By default, MySQL 'where id is null' selects the last inserted id
53
+ sqls << "SET SQL_AUTO_IS_NULL=0" unless opts[:auto_is_null]
54
+
55
+ sqls.each{|sql| log_yield(sql){conn.query(sql)}}
56
+
57
+ conn
58
+ end
59
+
60
+ # Returns instance of Sequel::MySQL::Dataset with the given options.
61
+ def dataset(opts = nil)
62
+ Mysql2::Dataset.new(self, opts)
63
+ end
64
+
65
+ # Executes the given SQL using an available connection, yielding the
66
+ # connection if the block is given.
67
+ def execute(sql, opts={}, &block)
68
+ if opts[:sproc]
69
+ call_sproc(sql, opts, &block)
70
+ else
71
+ synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
72
+ end
73
+ end
74
+
75
+ # Return the version of the MySQL server two which we are connecting.
76
+ def server_version(server=nil)
77
+ @server_version ||= (synchronize(server){|conn| conn.server_info[:id]} || super)
78
+ end
79
+
80
+ private
81
+
82
+ # Execute the given SQL on the given connection. If the :type
83
+ # option is :select, yield the result of the query, otherwise
84
+ # yield the connection if a block is given.
85
+ def _execute(conn, sql, opts)
86
+ begin
87
+ r = log_yield(sql){conn.query(sql, :symbolize_keys => true, :database_timezone => Sequel.database_timezone, :application_timezone => Sequel.application_timezone)}
88
+ if opts[:type] == :select
89
+ yield r if r
90
+ elsif block_given?
91
+ yield conn
92
+ end
93
+ rescue ::Mysql2::Error => e
94
+ raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
95
+ end
96
+ end
97
+
98
+ # MySQL connections use the query method to execute SQL without a result
99
+ def connection_execute_method
100
+ :query
101
+ end
102
+
103
+ # The MySQL adapter main error class is Mysql2::Error
104
+ def database_error_classes
105
+ [::Mysql2::Error]
106
+ end
107
+
108
+ # The database name when using the native adapter is always stored in
109
+ # the :database option.
110
+ def database_name
111
+ @opts[:database]
112
+ end
113
+
114
+ # Closes given database connection.
115
+ def disconnect_connection(c)
116
+ c.close
117
+ end
118
+
119
+ # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
120
+ def schema_column_type(db_type)
121
+ Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
122
+ end
123
+ end
124
+
125
+ # Dataset class for MySQL datasets accessed via the native driver.
126
+ class Dataset < Sequel::Dataset
127
+ include Sequel::MySQL::DatasetMethods
128
+
129
+ # Delete rows matching this dataset
130
+ def delete
131
+ execute_dui(delete_sql){|c| return c.affected_rows}
132
+ end
133
+
134
+ # Yield all rows matching this dataset.
135
+ def fetch_rows(sql, &block)
136
+ execute(sql) do |r|
137
+ @columns = r.fields
138
+ r.each(:cast_booleans => Sequel::MySQL.convert_tinyint_to_bool, &block)
139
+ end
140
+ self
141
+ end
142
+
143
+ # Insert a new value into this dataset
144
+ def insert(*values)
145
+ execute_dui(insert_sql(*values)){|c| return c.last_id}
146
+ end
147
+
148
+ # Replace (update or insert) the matching row.
149
+ def replace(*args)
150
+ execute_dui(replace_sql(*args)){|c| return c.last_id}
151
+ end
152
+
153
+ # Update the matching rows.
154
+ def update(values={})
155
+ execute_dui(update_sql(values)){|c| return c.affected_rows}
156
+ end
157
+
158
+ private
159
+
160
+ # Set the :type option to :select if it hasn't been set.
161
+ def execute(sql, opts={}, &block)
162
+ super(sql, {:type=>:select}.merge(opts), &block)
163
+ end
164
+
165
+ # Set the :type option to :dui if it hasn't been set.
166
+ def execute_dui(sql, opts={}, &block)
167
+ super(sql, {:type=>:dui}.merge(opts), &block)
168
+ end
169
+
170
+ # Handle correct quoting of strings using ::Mysql2::Client#escape.
171
+ def literal_string(v)
172
+ db.synchronize{|c| "'#{c.escape(v)}'"}
173
+ end
174
+ end
175
+ end
176
+ end
@@ -30,7 +30,7 @@ module Sequel
30
30
  if :driver == param and not (value =~ GUARDED_DRV_NAME)
31
31
  value = DRV_NAME_GUARDS % value
32
32
  end
33
- drv.attrs[param.to_s.capitalize] = value
33
+ drv.attrs[param.to_s.upcase] = value.to_s
34
34
  end
35
35
  db = ::ODBC::Database.new
36
36
  conn = db.drvconnect(drv)
@@ -502,3 +502,16 @@ module Sequel
502
502
  end
503
503
  end
504
504
  end
505
+
506
+ if SEQUEL_POSTGRES_USES_PG
507
+ begin
508
+ require 'sequel_pg'
509
+ rescue LoadError
510
+ if RUBY_PLATFORM =~ /mingw|mswin/
511
+ begin
512
+ require "#{RUBY_VERSION[0...3]}/sequel_pg"
513
+ rescue LoadError
514
+ end
515
+ end
516
+ end
517
+ end
@@ -3,7 +3,15 @@ module Sequel
3
3
  Dataset::NON_SQL_OPTIONS << :on_duplicate_key_update
4
4
 
5
5
  module MySQL
6
+ @convert_tinyint_to_bool = true
7
+
6
8
  class << self
9
+ # Sequel converts the column type tinyint(1) to a boolean by default when
10
+ # using the native MySQL or Mysql2 adapter. You can turn off the conversion by setting
11
+ # this to false. This setting is ignored when connecting to MySQL via the do or jdbc
12
+ # adapters, both of which automatically do the conversion.
13
+ attr_accessor :convert_tinyint_to_bool
14
+
7
15
  # Set the default charset used for CREATE TABLE. You can pass the
8
16
  # :charset option to create_table to override this setting.
9
17
  attr_accessor :default_charset
@@ -233,7 +241,7 @@ module Sequel
233
241
  m = output_identifier_meth
234
242
  im = input_identifier_meth
235
243
  metadata_dataset.with_sql("DESCRIBE ?", SQL::Identifier.new(im.call(table_name))).map do |row|
236
- row.delete(:Extra)
244
+ row[:auto_increment] = true if row.delete(:Extra).to_s =~ /auto_increment/io
237
245
  row[:allow_null] = row.delete(:Null) == 'YES'
238
246
  row[:default] = row.delete(:Default)
239
247
  row[:primary_key] = row.delete(:Key) == 'PRI'