sequel 3.28.0 → 3.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/CHANGELOG +119 -3
  2. data/Rakefile +5 -3
  3. data/bin/sequel +1 -5
  4. data/doc/model_hooks.rdoc +9 -1
  5. data/doc/opening_databases.rdoc +49 -40
  6. data/doc/prepared_statements.rdoc +27 -6
  7. data/doc/release_notes/3.28.0.txt +2 -2
  8. data/doc/release_notes/3.29.0.txt +459 -0
  9. data/doc/sharding.rdoc +7 -1
  10. data/doc/testing.rdoc +18 -9
  11. data/doc/transactions.rdoc +41 -1
  12. data/lib/sequel/adapters/ado.rb +28 -17
  13. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  14. data/lib/sequel/adapters/amalgalite.rb +11 -7
  15. data/lib/sequel/adapters/db2.rb +122 -70
  16. data/lib/sequel/adapters/dbi.rb +15 -15
  17. data/lib/sequel/adapters/do.rb +5 -36
  18. data/lib/sequel/adapters/do/mysql.rb +0 -5
  19. data/lib/sequel/adapters/do/postgres.rb +0 -5
  20. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  21. data/lib/sequel/adapters/firebird.rb +3 -6
  22. data/lib/sequel/adapters/ibmdb.rb +24 -16
  23. data/lib/sequel/adapters/informix.rb +2 -4
  24. data/lib/sequel/adapters/jdbc.rb +47 -11
  25. data/lib/sequel/adapters/jdbc/as400.rb +5 -24
  26. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  28. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/h2.rb +10 -12
  30. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  31. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  32. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
  34. data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
  36. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  38. data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
  39. data/lib/sequel/adapters/mock.rb +315 -0
  40. data/lib/sequel/adapters/mysql.rb +64 -51
  41. data/lib/sequel/adapters/mysql2.rb +15 -9
  42. data/lib/sequel/adapters/odbc.rb +13 -6
  43. data/lib/sequel/adapters/odbc/db2.rb +0 -4
  44. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  45. data/lib/sequel/adapters/openbase.rb +2 -4
  46. data/lib/sequel/adapters/oracle.rb +333 -51
  47. data/lib/sequel/adapters/postgres.rb +80 -27
  48. data/lib/sequel/adapters/shared/access.rb +0 -6
  49. data/lib/sequel/adapters/shared/db2.rb +13 -15
  50. data/lib/sequel/adapters/shared/firebird.rb +6 -6
  51. data/lib/sequel/adapters/shared/mssql.rb +23 -18
  52. data/lib/sequel/adapters/shared/mysql.rb +6 -6
  53. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  54. data/lib/sequel/adapters/shared/oracle.rb +185 -30
  55. data/lib/sequel/adapters/shared/postgres.rb +35 -18
  56. data/lib/sequel/adapters/shared/progress.rb +0 -6
  57. data/lib/sequel/adapters/shared/sqlite.rb +116 -37
  58. data/lib/sequel/adapters/sqlite.rb +16 -8
  59. data/lib/sequel/adapters/swift.rb +5 -5
  60. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  61. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  62. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  63. data/lib/sequel/adapters/tinytds.rb +13 -10
  64. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
  65. data/lib/sequel/core.rb +40 -0
  66. data/lib/sequel/database/connecting.rb +1 -2
  67. data/lib/sequel/database/dataset.rb +3 -3
  68. data/lib/sequel/database/dataset_defaults.rb +58 -0
  69. data/lib/sequel/database/misc.rb +62 -2
  70. data/lib/sequel/database/query.rb +113 -49
  71. data/lib/sequel/database/schema_methods.rb +7 -2
  72. data/lib/sequel/dataset/actions.rb +37 -19
  73. data/lib/sequel/dataset/features.rb +24 -0
  74. data/lib/sequel/dataset/graph.rb +7 -6
  75. data/lib/sequel/dataset/misc.rb +11 -3
  76. data/lib/sequel/dataset/mutation.rb +2 -3
  77. data/lib/sequel/dataset/prepared_statements.rb +6 -4
  78. data/lib/sequel/dataset/query.rb +46 -15
  79. data/lib/sequel/dataset/sql.rb +28 -4
  80. data/lib/sequel/extensions/named_timezones.rb +5 -0
  81. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  82. data/lib/sequel/model.rb +2 -1
  83. data/lib/sequel/model/associations.rb +115 -33
  84. data/lib/sequel/model/base.rb +91 -31
  85. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  86. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  87. data/lib/sequel/plugins/force_encoding.rb +6 -6
  88. data/lib/sequel/plugins/identity_map.rb +1 -1
  89. data/lib/sequel/plugins/many_through_many.rb +6 -10
  90. data/lib/sequel/plugins/prepared_statements.rb +12 -1
  91. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
  92. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  93. data/lib/sequel/plugins/serialization.rb +6 -1
  94. data/lib/sequel/plugins/sharding.rb +0 -5
  95. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  96. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  97. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  98. data/lib/sequel/timezones.rb +42 -42
  99. data/lib/sequel/version.rb +1 -1
  100. data/spec/adapters/mssql_spec.rb +29 -29
  101. data/spec/adapters/mysql_spec.rb +86 -104
  102. data/spec/adapters/oracle_spec.rb +48 -76
  103. data/spec/adapters/postgres_spec.rb +98 -33
  104. data/spec/adapters/spec_helper.rb +0 -5
  105. data/spec/adapters/sqlite_spec.rb +24 -21
  106. data/spec/core/connection_pool_spec.rb +9 -15
  107. data/spec/core/core_sql_spec.rb +20 -31
  108. data/spec/core/database_spec.rb +491 -227
  109. data/spec/core/dataset_spec.rb +638 -1051
  110. data/spec/core/expression_filters_spec.rb +0 -1
  111. data/spec/core/mock_adapter_spec.rb +378 -0
  112. data/spec/core/object_graph_spec.rb +48 -114
  113. data/spec/core/schema_generator_spec.rb +3 -3
  114. data/spec/core/schema_spec.rb +51 -114
  115. data/spec/core/spec_helper.rb +3 -90
  116. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  117. data/spec/extensions/dataset_associations_spec.rb +199 -0
  118. data/spec/extensions/instance_hooks_spec.rb +71 -0
  119. data/spec/extensions/named_timezones_spec.rb +22 -2
  120. data/spec/extensions/nested_attributes_spec.rb +3 -0
  121. data/spec/extensions/schema_spec.rb +1 -1
  122. data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
  123. data/spec/extensions/serialization_spec.rb +5 -8
  124. data/spec/extensions/spec_helper.rb +4 -0
  125. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  126. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  127. data/spec/integration/associations_test.rb +123 -12
  128. data/spec/integration/dataset_test.rb +140 -47
  129. data/spec/integration/eager_loader_test.rb +19 -21
  130. data/spec/integration/model_test.rb +80 -1
  131. data/spec/integration/plugin_test.rb +179 -128
  132. data/spec/integration/prepared_statement_test.rb +92 -91
  133. data/spec/integration/schema_test.rb +42 -23
  134. data/spec/integration/spec_helper.rb +25 -31
  135. data/spec/integration/timezone_test.rb +38 -12
  136. data/spec/integration/transaction_test.rb +161 -34
  137. data/spec/integration/type_test.rb +3 -3
  138. data/spec/model/association_reflection_spec.rb +83 -7
  139. data/spec/model/associations_spec.rb +393 -676
  140. data/spec/model/base_spec.rb +186 -116
  141. data/spec/model/dataset_methods_spec.rb +7 -27
  142. data/spec/model/eager_loading_spec.rb +343 -867
  143. data/spec/model/hooks_spec.rb +160 -79
  144. data/spec/model/model_spec.rb +118 -165
  145. data/spec/model/plugins_spec.rb +7 -13
  146. data/spec/model/record_spec.rb +138 -207
  147. data/spec/model/spec_helper.rb +10 -73
  148. metadata +14 -8
@@ -9,11 +9,6 @@ module Sequel
9
9
  module DatabaseMethods
10
10
  include Sequel::Informix::DatabaseMethods
11
11
 
12
- # Return instance of Sequel::JDBC::Informix::Dataset with the given opts.
13
- def dataset(opts=nil)
14
- Sequel::JDBC::Informix::Dataset.new(self, opts)
15
- end
16
-
17
12
  private
18
13
 
19
14
  # TODO: implement
@@ -8,11 +8,6 @@ module Sequel
8
8
  # Database instance methods for JTDS databases accessed via JDBC.
9
9
  module DatabaseMethods
10
10
  include Sequel::JDBC::MSSQL::DatabaseMethods
11
-
12
- # Return instance of Sequel::JDBC::JTDS::Dataset with the given opts.
13
- def dataset(opts=nil)
14
- Sequel::JDBC::JTDS::Dataset.new(self, opts)
15
- end
16
11
  end
17
12
 
18
13
  # Dataset class for JTDS datasets accessed via JDBC.
@@ -9,11 +9,6 @@ module Sequel
9
9
  module DatabaseMethods
10
10
  include Sequel::MySQL::DatabaseMethods
11
11
 
12
- # Return instance of Sequel::JDBC::MySQL::Dataset with the given opts.
13
- def dataset(opts=nil)
14
- Sequel::JDBC::MySQL::Dataset.new(self, opts)
15
- end
16
-
17
12
  private
18
13
 
19
14
  # The database name for the given database. Need to parse it out
@@ -71,11 +66,6 @@ module Sequel
71
66
  class Dataset < JDBC::Dataset
72
67
  include Sequel::MySQL::DatasetMethods
73
68
 
74
- # Use execute_insert to execute the insert_sql.
75
- def insert(*values)
76
- execute_insert(insert_sql(*values))
77
- end
78
-
79
69
  # Use execute_insert to execute the replace_sql.
80
70
  def replace(*args)
81
71
  execute_insert(replace_sql(*args))
@@ -9,16 +9,83 @@ module Sequel
9
9
  module DatabaseMethods
10
10
  include Sequel::Oracle::DatabaseMethods
11
11
  include Sequel::JDBC::Transactions
12
+
13
+ def self.extended(db)
14
+ db.instance_eval do
15
+ @autosequence = opts[:autosequence]
16
+ @primary_key_sequences = {}
17
+ end
18
+ end
12
19
 
13
- # Return Sequel::JDBC::Oracle::Dataset object with the given opts.
14
- def dataset(opts=nil)
15
- Sequel::JDBC::Oracle::Dataset.new(self, opts)
20
+ private
21
+
22
+ def last_insert_id(conn, opts)
23
+ unless sequence = opts[:sequence]
24
+ if t = opts[:table]
25
+ sequence = sequence_for_table(t)
26
+ end
27
+ end
28
+ if sequence
29
+ sql = "SELECT #{literal(sequence)}.currval FROM dual"
30
+ statement(conn) do |stmt|
31
+ begin
32
+ rs = log_yield(sql){stmt.executeQuery(sql)}
33
+ rs.next
34
+ rs.getInt(1)
35
+ rescue java.sql.SQLException
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def schema_parse_table(*)
43
+ sch = super
44
+ sch.each do |c, s|
45
+ if s[:type] == :decimal && s[:scale] == -127
46
+ s[:type] = :integer
47
+ elsif s[:db_type] == 'DATE'
48
+ s[:type] = :datetime
49
+ end
50
+ end
51
+ sch
52
+ end
53
+
54
+ def schema_parse_table_skip?(h, schema)
55
+ super || (h[:table_schem] != current_user unless schema)
56
+ end
57
+
58
+ # As of Oracle 9.2, releasing savepoints is no longer supported.
59
+ def supports_releasing_savepoints?
60
+ false
16
61
  end
17
62
  end
18
63
 
19
64
  # Dataset class for Oracle datasets accessed via JDBC.
20
65
  class Dataset < JDBC::Dataset
21
66
  include Sequel::Oracle::DatasetMethods
67
+
68
+ private
69
+
70
+ def convert_type(v)
71
+ case v
72
+ when Java::JavaMath::BigDecimal
73
+ if v.scale == 0
74
+ i = v.long_value
75
+ if v.equals(Java::JavaMath::BigDecimal.new(i))
76
+ i
77
+ else
78
+ super
79
+ end
80
+ else
81
+ super
82
+ end
83
+ when Java::OracleSql::TIMESTAMP
84
+ db.to_application_timestamp(v.to_string)
85
+ else
86
+ super
87
+ end
88
+ end
22
89
  end
23
90
  end
24
91
  end
@@ -47,17 +47,6 @@ module Sequel
47
47
  end
48
48
  end
49
49
 
50
- # Return instance of Sequel::JDBC::Postgres::Dataset with the given opts.
51
- def dataset(opts=nil)
52
- Sequel::JDBC::Postgres::Dataset.new(self, opts)
53
- end
54
-
55
- # Run the INSERT sql on the database and return the primary key
56
- # for the record.
57
- def execute_insert(sql, opts={})
58
- super(sql, {:type=>:insert}.merge(opts))
59
- end
60
-
61
50
  private
62
51
 
63
52
  # Use setNull for nil arguments as the default behavior of setString
@@ -8,11 +8,6 @@ module Sequel
8
8
  module DatabaseMethods
9
9
  include Sequel::SQLite::DatabaseMethods
10
10
 
11
- # Return Sequel::JDBC::SQLite::Dataset object with the given opts.
12
- def dataset(opts=nil)
13
- Sequel::JDBC::SQLite::Dataset.new(self, opts)
14
- end
15
-
16
11
  private
17
12
 
18
13
  # Use last_insert_rowid() to get the last inserted id.
@@ -9,11 +9,6 @@ module Sequel
9
9
  module DatabaseMethods
10
10
  include Sequel::JDBC::MSSQL::DatabaseMethods
11
11
 
12
- # Return instance of Sequel::JDBC::SQLServer::Dataset with the given opts.
13
- def dataset(opts=nil)
14
- Sequel::JDBC::SQLServer::Dataset.new(self, opts)
15
- end
16
-
17
12
  def metadata_dataset
18
13
  ds = super
19
14
  # Work around a bug in SQL Server JDBC Driver 3.0, where the metadata
@@ -3,30 +3,79 @@ module Sequel
3
3
  module Transactions
4
4
  TRANSACTION_BEGIN = 'Transaction.begin'.freeze
5
5
  TRANSACTION_COMMIT = 'Transaction.commit'.freeze
6
+ TRANSACTION_RELEASE_SP = 'Transaction.release_savepoint'.freeze
6
7
  TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
8
+ TRANSACTION_ROLLBACK_SP = 'Transaction.rollback_savepoint'.freeze
9
+ TRANSACTION_SAVEPOINT= 'Transaction.savepoint'.freeze
10
+
11
+ # Check the JDBC DatabaseMetaData for savepoint support
12
+ def supports_savepoints?
13
+ return @supports_savepoints if defined?(@supports_savepoints)
14
+ @supports_savepoints = synchronize{|c| c.get_meta_data.supports_savepoints}
15
+ end
7
16
 
8
17
  private
9
18
 
19
+ # Most JDBC drivers that support savepoints support releasing them.
20
+ def supports_releasing_savepoints?
21
+ true
22
+ end
23
+
10
24
  # Use JDBC connection's setAutoCommit to false to start transactions
11
25
  def begin_transaction(conn, opts={})
12
- log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
13
- conn
26
+ if supports_savepoints?
27
+ th = @transactions[conn]
28
+ if sps = th[:savepoints]
29
+ sps << log_yield(TRANSACTION_SAVEPOINT){conn.set_savepoint}
30
+ else
31
+ log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
32
+ th[:savepoints] = []
33
+ end
34
+ th[:savepoint_level] += 1
35
+ else
36
+ log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
37
+ end
14
38
  end
15
39
 
16
40
  # Use JDBC connection's commit method to commit transactions
17
41
  def commit_transaction(conn, opts={})
18
- log_yield(TRANSACTION_COMMIT){conn.commit}
42
+ if supports_savepoints?
43
+ sps = @transactions[conn][:savepoints]
44
+ if sps.empty?
45
+ log_yield(TRANSACTION_COMMIT){conn.commit}
46
+ elsif supports_releasing_savepoints?
47
+ log_yield(TRANSACTION_RELEASE_SP){supports_releasing_savepoints? ? conn.release_savepoint(sps.last) : sps.last}
48
+ end
49
+ else
50
+ log_yield(TRANSACTION_COMMIT){conn.commit}
51
+ end
19
52
  end
20
53
 
21
54
  # Use JDBC connection's setAutoCommit to true to enable non-transactional behavior
22
- def remove_transaction(conn)
23
- conn.setAutoCommit(true) if conn
24
- @transactions.delete(Thread.current)
55
+ def remove_transaction(conn, committed)
56
+ if supports_savepoints?
57
+ sps = @transactions[conn][:savepoints]
58
+ conn.setAutoCommit(true) if sps.empty?
59
+ sps.pop
60
+ else
61
+ conn.setAutoCommit(true)
62
+ end
63
+ ensure
64
+ super
25
65
  end
26
66
 
27
67
  # Use JDBC connection's rollback method to rollback transactions
28
68
  def rollback_transaction(conn, opts={})
29
- log_yield(TRANSACTION_ROLLBACK){conn.rollback}
69
+ if supports_savepoints?
70
+ sps = @transactions[conn][:savepoints]
71
+ if sps.empty?
72
+ log_yield(TRANSACTION_ROLLBACK){conn.rollback}
73
+ else
74
+ log_yield(TRANSACTION_ROLLBACK_SP){conn.rollback(sps.last)}
75
+ end
76
+ else
77
+ log_yield(TRANSACTION_ROLLBACK){conn.rollback}
78
+ end
30
79
  end
31
80
  end
32
81
  end
@@ -0,0 +1,315 @@
1
+ module Sequel
2
+ module Mock
3
+ # Connection class for Sequel's mock adapter.
4
+ class Connection
5
+ # Sequel::Mock::Database object that created this connection
6
+ attr_reader :db
7
+
8
+ # Shard this connection operates on, when using Sequel's
9
+ # sharding support (always :default for databases not using
10
+ # sharding).
11
+ attr_reader :server
12
+
13
+ # The specific database options for this connection.
14
+ attr_reader :opts
15
+
16
+ # Store the db, server, and opts.
17
+ def initialize(db, server, opts)
18
+ @db = db
19
+ @server = server
20
+ @opts = opts
21
+ end
22
+
23
+ # Delegate to the db's #_execute method.
24
+ def execute(sql)
25
+ @db.send(:_execute, self, sql)
26
+ end
27
+ end
28
+
29
+ # Database class for Sequel's mock adapter.
30
+ class Database < Sequel::Database
31
+ set_adapter_scheme :mock
32
+
33
+ # Set the autogenerated primary key integer
34
+ # to be returned when running an insert query.
35
+ # Argument types supported:
36
+ #
37
+ # nil :: Return nil for all inserts
38
+ # Integer :: Starting integer for next insert, with
39
+ # futher inserts getting an incremented
40
+ # value
41
+ # Array :: First insert gets the first value in the
42
+ # array, second gets the second value, etc.
43
+ # Proc :: Called with the insert SQL query, uses
44
+ # the value returned
45
+ # Class :: Should be an Exception subclass, will create a new
46
+ # instance an raise it wrapped in a DatabaseError.
47
+ attr_writer :autoid
48
+
49
+ # Set the columns to set in the dataset when the dataset fetches
50
+ # rows. Argument types supported:
51
+ # nil :: Set no columns
52
+ # Array of Symbols: Used for all datasets
53
+ # Array (otherwise): First retrieval gets the first value in the
54
+ # array, second gets the second value, etc.
55
+ # Proc :: Called with the select SQL query, uses the value
56
+ # returned, which should be an array of symbols
57
+ attr_writer :columns
58
+
59
+ # Set the hashes to yield by execute when retrieving rows.
60
+ # Argument types supported:
61
+ #
62
+ # nil :: Yield no rows
63
+ # Hash :: Always yield a single row with this hash
64
+ # Array of Hashes :: Yield separately for each hash in this array
65
+ # Array (otherwise) :: First retrieval gets the first value
66
+ # in the array, second gets the second value, etc.
67
+ # Proc :: Called with the select SQL query, uses
68
+ # the value returned, which should be a hash or
69
+ # array of hashes.
70
+ # Class :: Should be an Exception subclass, will create a new
71
+ # instance an raise it wrapped in a DatabaseError.
72
+ attr_writer :fetch
73
+
74
+ # Set the number of rows to return from update or delete.
75
+ # Argument types supported:
76
+ #
77
+ # nil :: Return 0 for all updates and deletes
78
+ # Integer :: Used for all updates and deletes
79
+ # Array :: First update/delete gets the first value in the
80
+ # array, second gets the second value, etc.
81
+ # Proc :: Called with the update/delete SQL query, uses
82
+ # the value returned.
83
+ # Class :: Should be an Exception subclass, will create a new
84
+ # instance an raise it wrapped in a DatabaseError.
85
+ attr_writer :numrows
86
+
87
+ # Additional options supported:
88
+ #
89
+ # :autoid :: Call #autoid= with the value
90
+ # :columns :: Call #columns= with the value
91
+ # :fetch :: Call #fetch= with the value
92
+ # :numrows :: Call #numrows= with the value
93
+ # :extend :: A module the object is extended with.
94
+ # :sqls :: The array to store the SQL queries in.
95
+ def initialize(opts=nil)
96
+ super
97
+ self.autoid = opts[:autoid]
98
+ self.columns = opts[:columns]
99
+ self.fetch = opts[:fetch]
100
+ self.numrows = opts[:numrows]
101
+ extend(opts[:extend]) if opts[:extend]
102
+ @sqls = opts[:sqls] || []
103
+ end
104
+
105
+ # Return a related Connection option connecting to the given shard.
106
+ def connect(server)
107
+ Connection.new(self, server, server_opts(server))
108
+ end
109
+
110
+ # Store the sql used for later retrieval with #sqls, and return
111
+ # the appropriate value using either the #autoid, #fetch, or
112
+ # #numrows methods.
113
+ def execute(sql, opts={}, &block)
114
+ synchronize(opts[:server]){|c| _execute(c, sql, opts, &block)}
115
+ end
116
+ alias execute_ddl execute
117
+
118
+ # Store the sql used, and return the value of the #numrows method.
119
+ def execute_dui(sql, opts={})
120
+ execute(sql, opts.merge(:meth=>:numrows))
121
+ end
122
+
123
+ # Store the sql used, and return the value of the #autoid method.
124
+ def execute_insert(sql, opts={})
125
+ execute(sql, opts.merge(:meth=>:autoid))
126
+ end
127
+
128
+ # Return all stored SQL queries, and clear the cache
129
+ # of SQL queries.
130
+ def sqls
131
+ s = @sqls.dup
132
+ @sqls.clear
133
+ s
134
+ end
135
+
136
+ # Enable use of savepoints.
137
+ def supports_savepoints?
138
+ true
139
+ end
140
+
141
+ private
142
+
143
+ def _autoid(sql, v, ds=nil)
144
+ case v
145
+ when Integer
146
+ if ds
147
+ ds.autoid += 1 if ds.autoid.is_a?(Integer)
148
+ else
149
+ @autoid += 1
150
+ end
151
+ v
152
+ else
153
+ _nextres(v, sql, nil)
154
+ end
155
+ end
156
+
157
+ def _execute(c, sql, opts={}, &block)
158
+ sql += " -- args: #{opts[:arguments].inspect}" if opts[:arguments]
159
+ sql += " -- #{@opts[:append]}" if @opts[:append]
160
+ sql += " -- #{c.server}" if c.server != :default
161
+ log_info(sql)
162
+ @sqls << sql
163
+
164
+ ds = opts[:dataset]
165
+ begin
166
+ if block
167
+ columns(ds, sql) if ds
168
+ _fetch(sql, ds._fetch || @fetch, &block)
169
+ elsif meth = opts[:meth]
170
+ if meth == :numrows
171
+ _numrows(sql, ds.numrows || @numrows)
172
+ else
173
+ v = ds.autoid
174
+ _autoid(sql, v || @autoid, (ds if v))
175
+ end
176
+ end
177
+ rescue => e
178
+ raise_error(e)
179
+ end
180
+ end
181
+
182
+ def _fetch(sql, f, &block)
183
+ case f
184
+ when Hash
185
+ yield f.dup
186
+ when Array
187
+ if f.all?{|h| h.is_a?(Hash)}
188
+ f.each{|h| yield h.dup}
189
+ else
190
+ _fetch(sql, f.shift, &block)
191
+ end
192
+ when Proc
193
+ h = f.call(sql)
194
+ if h.is_a?(Hash)
195
+ yield h.dup
196
+ elsif h
197
+ h.each{|h1| yield h1.dup}
198
+ end
199
+ when Class
200
+ if f < Exception
201
+ raise f
202
+ else
203
+ raise Error, "Invalid @autoid/@numrows attribute: #{v.inspect}"
204
+ end
205
+ when nil
206
+ # nothing
207
+ else
208
+ raise Error, "Invalid @fetch attribute: #{f.inspect}"
209
+ end
210
+ end
211
+
212
+ def _nextres(v, sql, default)
213
+ case v
214
+ when Integer
215
+ v
216
+ when Array
217
+ v.empty? ? default : _nextres(v.shift, sql, default)
218
+ when Proc
219
+ v.call(sql)
220
+ when Class
221
+ if v < Exception
222
+ raise v
223
+ else
224
+ raise Error, "Invalid @autoid/@numrows attribute: #{v.inspect}"
225
+ end
226
+ when nil
227
+ default
228
+ else
229
+ raise Error, "Invalid @autoid/@numrows attribute: #{v.inspect}"
230
+ end
231
+ end
232
+
233
+ def _numrows(sql, v)
234
+ _nextres(v, sql, 0)
235
+ end
236
+
237
+ def columns(ds, sql, cs=@columns)
238
+ case cs
239
+ when Array
240
+ unless cs.empty?
241
+ if cs.all?{|c| c.is_a?(Symbol)}
242
+ ds.columns(*cs)
243
+ else
244
+ columns(ds, sql, cs.shift)
245
+ end
246
+ end
247
+ when Proc
248
+ ds.columns(*cs.call(sql))
249
+ when nil
250
+ # nothing
251
+ else
252
+ raise Error, "Invalid @columns attribute: #{cs.inspect}"
253
+ end
254
+ end
255
+
256
+ def disconnect_connection(c)
257
+ end
258
+
259
+ def quote_identifiers_default
260
+ false
261
+ end
262
+
263
+ def identifier_input_method_default
264
+ nil
265
+ end
266
+
267
+ def identifier_input_method_default
268
+ nil
269
+ end
270
+ end
271
+
272
+ class Dataset < Sequel::Dataset
273
+ Database::DatasetClass = self
274
+
275
+ # Override the databases's autoid setting for this dataset
276
+ attr_accessor :autoid
277
+
278
+ # Override the databases's fetch setting for this dataset
279
+ attr_accessor :_fetch
280
+
281
+ # Override the databases's numrows setting for this dataset
282
+ attr_accessor :numrows
283
+
284
+ # If arguments are provided, use them to set the columns
285
+ # for this dataset and return self. Otherwise, use the
286
+ # default Sequel behavior and return the columns.
287
+ def columns(*cs)
288
+ if cs.empty?
289
+ super
290
+ else
291
+ @columns = cs
292
+ self
293
+ end
294
+ end
295
+
296
+ def fetch_rows(sql, &block)
297
+ execute(sql, &block)
298
+ end
299
+
300
+ private
301
+
302
+ def execute(sql, opts={}, &block)
303
+ super(sql, opts.merge(:dataset=>self), &block)
304
+ end
305
+
306
+ def execute_dui(sql, opts={}, &block)
307
+ super(sql, opts.merge(:dataset=>self), &block)
308
+ end
309
+
310
+ def execute_insert(sql, opts={}, &block)
311
+ super(sql, opts.merge(:dataset=>self), &block)
312
+ end
313
+ end
314
+ end
315
+ end