sequel 3.14.0 → 3.15.0

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