sequel 3.21.0 → 3.28.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 (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. metadata +85 -34
@@ -1,6 +1,5 @@
1
1
  require 'mysql2' unless defined? Mysql2
2
-
3
- Sequel.require %w'shared/mysql', 'adapters'
2
+ Sequel.require %w'shared/mysql_prepared_statements', 'adapters'
4
3
 
5
4
  module Sequel
6
5
  # Module for holding all Mysql2-related classes and modules for Sequel.
@@ -8,6 +7,7 @@ module Sequel
8
7
  # Database class for MySQL databases used with Sequel.
9
8
  class Database < Sequel::Database
10
9
  include Sequel::MySQL::DatabaseMethods
10
+ include Sequel::MySQL::PreparedStatements::DatabaseMethods
11
11
 
12
12
  # Mysql::Error messages that indicate the current connection should be disconnected
13
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)/
@@ -21,7 +21,6 @@ module Sequel
21
21
  # a filter for an autoincrement column equals NULL to return the last
22
22
  # inserted row.
23
23
  # * :charset - Same as :encoding (:encoding takes precendence)
24
- # * :compress - Set to false to not compress results from the server
25
24
  # * :config_default_group - The default group to read from the in
26
25
  # the MySQL config file.
27
26
  # * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
@@ -57,6 +56,7 @@ module Sequel
57
56
 
58
57
  sqls.each{|sql| log_yield(sql){conn.query(sql)}}
59
58
 
59
+ add_prepared_statements_cache(conn)
60
60
  conn
61
61
  end
62
62
 
@@ -65,16 +65,6 @@ module Sequel
65
65
  Mysql2::Dataset.new(self, opts)
66
66
  end
67
67
 
68
- # Executes the given SQL using an available connection, yielding the
69
- # connection if the block is given.
70
- def execute(sql, opts={}, &block)
71
- if opts[:sproc]
72
- call_sproc(sql, opts, &block)
73
- else
74
- synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
75
- end
76
- end
77
-
78
68
  # Return the version of the MySQL server two which we are connecting.
79
69
  def server_version(server=nil)
80
70
  @server_version ||= (synchronize(server){|conn| conn.server_info[:id]} || super)
@@ -94,7 +84,7 @@ module Sequel
94
84
  yield conn
95
85
  end
96
86
  rescue ::Mysql2::Error => e
97
- raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
87
+ raise_error(e)
98
88
  end
99
89
  end
100
90
 
@@ -108,6 +98,10 @@ module Sequel
108
98
  [::Mysql2::Error]
109
99
  end
110
100
 
101
+ def disconnect_error?(e, opts)
102
+ super || (e.is_a?(::Mysql2::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
103
+ end
104
+
111
105
  # The database name when using the native adapter is always stored in
112
106
  # the :database option.
113
107
  def database_name
@@ -128,6 +122,7 @@ module Sequel
128
122
  # Dataset class for MySQL datasets accessed via the native driver.
129
123
  class Dataset < Sequel::Dataset
130
124
  include Sequel::MySQL::DatasetMethods
125
+ include Sequel::MySQL::PreparedStatements::DatasetMethods
131
126
 
132
127
  # Delete rows matching this dataset
133
128
  def delete
@@ -0,0 +1,21 @@
1
+ Sequel.require 'adapters/shared/db2'
2
+
3
+ module Sequel
4
+ module ODBC
5
+ # Database and Dataset instance methods for DB2 specific
6
+ # support via ODBC.
7
+ module DB2
8
+ module DatabaseMethods
9
+ include ::Sequel::DB2::DatabaseMethods
10
+
11
+ def dataset(opts=nil)
12
+ Sequel::ODBC::DB2::Dataset.new(self, opts)
13
+ end
14
+ end
15
+
16
+ class Dataset < ODBC::Dataset
17
+ include ::Sequel::DB2::DatasetMethods
18
+ end
19
+ end
20
+ end
21
+ end
@@ -15,9 +15,13 @@ module Sequel
15
15
  when 'mssql'
16
16
  Sequel.ts_require 'adapters/odbc/mssql'
17
17
  extend Sequel::ODBC::MSSQL::DatabaseMethods
18
+ set_mssql_unicode_strings
18
19
  when 'progress'
19
20
  Sequel.ts_require 'adapters/shared/progress'
20
21
  extend Sequel::Progress::DatabaseMethods
22
+ when 'db2'
23
+ Sequel.ts_require 'adapters/odbc/db2'
24
+ extend Sequel::ODBC::DB2::DatabaseMethods
21
25
  end
22
26
  end
23
27
 
@@ -51,7 +55,7 @@ module Sequel
51
55
  r = log_yield(sql){conn.run(sql)}
52
56
  yield(r) if block_given?
53
57
  rescue ::ODBC::Error, ArgumentError => e
54
- raise_error(e, :disconnect=>DISCONNECT_ERRORS.match(e.message))
58
+ raise_error(e)
55
59
  ensure
56
60
  r.drop if r
57
61
  end
@@ -64,7 +68,7 @@ module Sequel
64
68
  begin
65
69
  log_yield(sql){conn.do(sql)}
66
70
  rescue ::ODBC::Error, ArgumentError => e
67
- raise_error(e, :disconnect=>DISCONNECT_ERRORS.match(e.message))
71
+ raise_error(e)
68
72
  end
69
73
  end
70
74
  end
@@ -76,9 +80,17 @@ module Sequel
76
80
  :do
77
81
  end
78
82
 
83
+ def database_error_classes
84
+ [::ODBC::Error]
85
+ end
86
+
79
87
  def disconnect_connection(c)
80
88
  c.disconnect
81
89
  end
90
+
91
+ def disconnect_error?(e, opts)
92
+ super || (e.is_a?(::ODBC::Error) && DISCONNECT_ERRORS.match(e.message))
93
+ end
82
94
  end
83
95
 
84
96
  class Dataset < Sequel::Dataset
@@ -116,7 +128,7 @@ module Sequel
116
128
  when ::ODBC::TimeStamp
117
129
  Sequel.database_to_application_timestamp([v.year, v.month, v.day, v.hour, v.minute, v.second])
118
130
  when ::ODBC::Time
119
- Sequel.database_to_application_timestamp([now.year, now.month, now.day, v.hour, v.minute, v.second])
131
+ Sequel::SQLTime.create(v.hour, v.minute, v.second)
120
132
  when ::ODBC::Date
121
133
  Date.new(v.year, v.month, v.day)
122
134
  else
@@ -24,6 +24,18 @@ module Sequel
24
24
  conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
25
25
  conn.autocommit = true
26
26
  conn.non_blocking = true
27
+
28
+ # The ruby-oci8 gem which retrieves oracle columns with a type of
29
+ # DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE is complex based on the
30
+ # ruby version (1.9.2 or later) and Oracle version (9 or later)
31
+ # In the now standard case of 1.9.2 and Oracle 9 or later, the timezone
32
+ # is determined by the Oracle session timezone. Thus if the user
33
+ # requests Sequel provide UTC timezone to the application,
34
+ # we need to alter the session timezone to be UTC
35
+ if Sequel.application_timezone == :utc
36
+ conn.exec("ALTER SESSION SET TIME_ZONE='-00:00'")
37
+ end
38
+
27
39
  conn
28
40
  end
29
41
 
@@ -66,7 +78,7 @@ module Sequel
66
78
  yield(r) if block_given?
67
79
  r
68
80
  rescue OCIException => e
69
- raise_error(e, :disconnect=>CONNECTION_ERROR_CODES.include?(e.code))
81
+ raise_error(e)
70
82
  end
71
83
  end
72
84
  end
@@ -88,6 +100,10 @@ module Sequel
88
100
  rescue OCIInvalidHandle
89
101
  nil
90
102
  end
103
+
104
+ def disconnect_error?(e, opts)
105
+ super || (e.is_a?(::OCIException) && CONNECTION_ERROR_CODES.include?(e.code))
106
+ end
91
107
 
92
108
  def remove_transaction(conn)
93
109
  conn.autocommit = true if conn
@@ -93,13 +93,14 @@ module Sequel
93
93
  def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
94
94
  def integer(s) s.to_i end
95
95
  def float(s) s.to_f end
96
- def numeric(s) ::BigDecimal.new(s) end
97
- def date(s) ::Sequel.string_to_date(s) end
98
- def date_iso(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
99
- def time(s) ::Sequel.string_to_time(s) end
100
- def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
96
+ def date(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
101
97
  end.new
102
98
 
99
+ # Hash with type name symbols and callable values for converting PostgreSQL types.
100
+ # Non-builtin types that don't have fixed numbers should use this to register
101
+ # conversion procs.
102
+ PG_NAMED_TYPES = {}
103
+
103
104
  # Hash with integer keys and callable values for converting PostgreSQL types.
104
105
  PG_TYPES = {}
105
106
  {
@@ -107,9 +108,9 @@ module Sequel
107
108
  [17] => tt.method(:bytea),
108
109
  [20, 21, 22, 23, 26] => tt.method(:integer),
109
110
  [700, 701] => tt.method(:float),
110
- [790, 1700] => tt.method(:numeric),
111
- [1083, 1266] => tt.method(:time),
112
- [1114, 1184] => tt.method(:timestamp)
111
+ [790, 1700] => ::BigDecimal.method(:new),
112
+ [1083, 1266] => ::Sequel.method(:string_to_time),
113
+ [1114, 1184] => ::Sequel.method(:database_to_application_timestamp)
113
114
  }.each do |k,v|
114
115
  k.each{|n| PG_TYPES[n] = v}
115
116
  end
@@ -123,7 +124,7 @@ module Sequel
123
124
 
124
125
  # Modify the type translator for the date type depending on the value given.
125
126
  def self.use_iso_date_format=(v)
126
- PG_TYPES[1082] = TYPE_TRANSLATOR.method(v ? :date_iso : :date)
127
+ PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
127
128
  @use_iso_date_format = v
128
129
  end
129
130
  self.use_iso_date_format = true
@@ -131,7 +132,10 @@ module Sequel
131
132
  # PGconn subclass for connection specific methods used with the
132
133
  # pg, postgres, or postgres-pr driver.
133
134
  class Adapter < ::PGconn
135
+ DISCONNECT_ERROR_RE = /\Acould not receive data from server: Software caused connection abort/
136
+
134
137
  include Sequel::Postgres::AdapterMethods
138
+
135
139
  self.translate_results = false if respond_to?(:translate_results=)
136
140
 
137
141
  # Hash of prepared statements for this connection. Keys are
@@ -150,20 +154,23 @@ module Sequel
150
154
  end
151
155
  @prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
152
156
  end
153
-
157
+
154
158
  # Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
155
159
  # the connection status cannot be determined or it is not OK.
156
160
  def check_disconnect_errors
157
161
  begin
158
162
  yield
159
163
  rescue PGError =>e
164
+ disconnect = false
160
165
  begin
161
166
  s = status
162
167
  rescue PGError
163
- raise Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)
168
+ disconnect = true
164
169
  end
165
170
  status_ok = (s == Adapter::CONNECTION_OK)
166
- status_ok ? raise : raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
171
+ disconnect ||= !status_ok
172
+ disconnect ||= e.message =~ DISCONNECT_ERROR_RE
173
+ disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
167
174
  ensure
168
175
  block if status_ok
169
176
  end
@@ -176,7 +183,7 @@ module Sequel
176
183
  begin
177
184
  block_given? ? yield(q) : q.cmd_tuples
178
185
  ensure
179
- q.clear
186
+ q.clear if q
180
187
  end
181
188
  end
182
189
 
@@ -194,6 +201,10 @@ module Sequel
194
201
  include Sequel::Postgres::DatabaseMethods
195
202
 
196
203
  set_adapter_scheme :postgres
204
+
205
+ # A hash of conversion procs, keyed by type integer (oid) and
206
+ # having callable values for the conversion proc for that type.
207
+ attr_reader :conversion_procs
197
208
 
198
209
  # Add the primary_keys and primary_key_sequences instance variables,
199
210
  # so we can get the correct return values for inserted rows.
@@ -225,9 +236,74 @@ module Sequel
225
236
  end
226
237
  conn.db = self
227
238
  conn.apply_connection_settings
239
+ @conversion_procs ||= get_conversion_procs(conn)
228
240
  conn
229
241
  end
230
242
 
243
+ if SEQUEL_POSTGRES_USES_PG
244
+ # +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
245
+ # results directly to the caller. This method is only support if pg is the
246
+ # underlying ruby driver. This method should only be called if you want
247
+ # results returned to the client. If you are using +COPY FROM+ or +COPY TO+
248
+ # with a filename, you should just use +run+ instead of this method. This
249
+ # method does not currently support +COPY FROM STDIN+, but that may be supported
250
+ # in the future.
251
+ #
252
+ # The table argument supports the following types:
253
+ #
254
+ # String :: Uses the first argument directly as literal SQL. If you are using
255
+ # a version of PostgreSQL before 9.0, you will probably want to
256
+ # use a string if you are using any options at all, as the syntax
257
+ # Sequel uses for options is only compatible with PostgreSQL 9.0+.
258
+ # Dataset :: Uses a query instead of a table name when copying.
259
+ # other :: Uses a table name (usually a symbol) when copying.
260
+ #
261
+ # The following options are respected:
262
+ #
263
+ # :format :: The format to use. text is the default, so this should be :csv or :binary.
264
+ # :options :: An options SQL string to use, which should contain comma separated options.
265
+ # :server :: The server on which to run the query.
266
+ #
267
+ # If a block is provided, the method continually yields to the block, one yield
268
+ # per row. If a block is not provided, a single string is returned with all
269
+ # of the data.
270
+ def copy_table(table, opts={})
271
+ sql = if table.is_a?(String)
272
+ sql = table
273
+ else
274
+ if opts[:options] || opts[:format]
275
+ options = " ("
276
+ options << "FORMAT #{opts[:format]}" if opts[:format]
277
+ options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
278
+ options << ')'
279
+ end
280
+ table = if table.is_a?(::Sequel::Dataset)
281
+ "(#{table.sql})"
282
+ else
283
+ literal(table)
284
+ end
285
+ sql = "COPY #{table} TO STDOUT#{options}"
286
+ end
287
+ synchronize(opts[:server]) do |conn|
288
+ conn.execute(sql)
289
+ begin
290
+ if block_given?
291
+ while buf = conn.get_copy_data
292
+ yield buf
293
+ end
294
+ nil
295
+ else
296
+ b = ''
297
+ b << buf while buf = conn.get_copy_data
298
+ b
299
+ end
300
+ ensure
301
+ raise DatabaseDisconnectError, "disconnecting as a partial COPY may leave the connection in an unusable state" if buf
302
+ end
303
+ end
304
+ end
305
+ end
306
+
231
307
  # Return instance of Sequel::Postgres::Dataset with the given options.
232
308
  def dataset(opts = nil)
233
309
  Postgres::Dataset.new(self, opts)
@@ -305,6 +381,19 @@ module Sequel
305
381
  end
306
382
  end
307
383
  end
384
+
385
+ # Return the conversion procs hash to use for this database
386
+ def get_conversion_procs(conn)
387
+ procs = PG_TYPES.dup
388
+ conn.execute("SELECT oid, typname FROM pg_type where typtype = 'b'") do |res|
389
+ res.ntuples.times do |recnum|
390
+ if pr = PG_NAMED_TYPES[res.getvalue(recnum, 1).to_sym]
391
+ procs[res.getvalue(recnum, 0).to_i] ||= pr
392
+ end
393
+ end
394
+ end
395
+ procs
396
+ end
308
397
  end
309
398
 
310
399
  # Dataset class for PostgreSQL datasets that use the pg, postgres, or
@@ -372,11 +461,17 @@ module Sequel
372
461
  end
373
462
  LiteralString.new("#{prepared_arg_placeholder}#{i}#{"::#{type}" if type}")
374
463
  end
464
+
465
+ # Always assume a prepared argument.
466
+ def prepared_arg?(k)
467
+ true
468
+ end
375
469
  end
376
470
 
377
471
  # Allow use of bind arguments for PostgreSQL using the pg driver.
378
472
  module BindArgumentMethods
379
473
  include ArgumentMapper
474
+ include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
380
475
 
381
476
  private
382
477
 
@@ -400,7 +495,6 @@ module Sequel
400
495
  # pg driver.
401
496
  module PreparedStatementMethods
402
497
  include BindArgumentMethods
403
- include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
404
498
 
405
499
  private
406
500
 
@@ -427,7 +521,7 @@ module Sequel
427
521
  ps.extend(BindArgumentMethods)
428
522
  ps.call(bind_vars, &block)
429
523
  end
430
-
524
+
431
525
  # Prepare the given type of statement with the given name, and store
432
526
  # it in the database to be called later.
433
527
  def prepare(type, name=nil, *values)
@@ -483,8 +577,9 @@ module Sequel
483
577
  # field numers, type conversion procs, and name symbol arrays.
484
578
  def fetch_rows_set_cols(res)
485
579
  cols = []
580
+ procs = db.conversion_procs
486
581
  res.nfields.times do |fieldnum|
487
- cols << [fieldnum, PG_TYPES[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
582
+ cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
488
583
  end
489
584
  @columns = cols.map{|c| c.at(2)}
490
585
  cols
@@ -1,6 +1,7 @@
1
1
  module Sequel
2
2
  module Access
3
3
  module DatabaseMethods
4
+ # Access uses type :access as the database_type
4
5
  def database_type
5
6
  :access
6
7
  end
@@ -16,6 +17,7 @@ module Sequel
16
17
  from(:MSysObjects).filter(:Type=>1, :Flags=>0).select_map(:Name).map{|x| x.to_sym}
17
18
  end
18
19
 
20
+ # Access uses type Counter for an autoincrementing keys
19
21
  def serial_primary_key_options
20
22
  {:primary_key => true, :type=>:Counter}
21
23
  end
@@ -34,16 +36,35 @@ module Sequel
34
36
  module DatasetMethods
35
37
  SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
36
38
 
39
+ # Access doesn't support INTERSECT or EXCEPT
37
40
  def supports_intersect_except?
38
41
  false
39
42
  end
40
43
 
41
44
  private
42
45
 
46
+ # Access uses # to quote dates
47
+ def literal_date(d)
48
+ d.strftime('#%Y-%m-%d#')
49
+ end
50
+
51
+ # Access uses # to quote datetimes
52
+ def literal_datetime(t)
53
+ t.strftime('#%Y-%m-%d %H:%M:%S#')
54
+ end
55
+ alias literal_time literal_datetime
56
+
57
+ # Access uses TOP for limits
58
+ def select_limit_sql(sql)
59
+ sql << " TOP #{@opts[:limit]}" if @opts[:limit]
60
+ end
61
+
62
+ # Access uses [] for quoting identifiers
43
63
  def quoted_identifier(v)
44
64
  "[#{v}]"
45
65
  end
46
66
 
67
+ # Access requires the limit clause come before other clauses
47
68
  def select_clause_methods
48
69
  SELECT_CLAUSE_METHODS
49
70
  end