sequel 3.28.0 → 3.29.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 (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