colincasey-sequel 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +7 -1
  2. data/doc/advanced_associations.rdoc +614 -0
  3. data/doc/cheat_sheet.rdoc +223 -0
  4. data/doc/dataset_filtering.rdoc +158 -0
  5. data/doc/prepared_statements.rdoc +104 -0
  6. data/doc/release_notes/1.0.txt +38 -0
  7. data/doc/release_notes/1.1.txt +143 -0
  8. data/doc/release_notes/1.3.txt +101 -0
  9. data/doc/release_notes/1.4.0.txt +53 -0
  10. data/doc/release_notes/1.5.0.txt +155 -0
  11. data/doc/release_notes/2.0.0.txt +298 -0
  12. data/doc/release_notes/2.1.0.txt +271 -0
  13. data/doc/release_notes/2.10.0.txt +328 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/doc/schema.rdoc +29 -0
  23. data/doc/sharding.rdoc +113 -0
  24. data/lib/sequel.rb +1 -0
  25. data/lib/sequel_core/adapters/ado.rb +89 -0
  26. data/lib/sequel_core/adapters/db2.rb +143 -0
  27. data/lib/sequel_core/adapters/dbi.rb +112 -0
  28. data/lib/sequel_core/adapters/do/mysql.rb +38 -0
  29. data/lib/sequel_core/adapters/do/postgres.rb +92 -0
  30. data/lib/sequel_core/adapters/do/sqlite.rb +31 -0
  31. data/lib/sequel_core/adapters/do.rb +205 -0
  32. data/lib/sequel_core/adapters/firebird.rb +298 -0
  33. data/lib/sequel_core/adapters/informix.rb +85 -0
  34. data/lib/sequel_core/adapters/jdbc/h2.rb +69 -0
  35. data/lib/sequel_core/adapters/jdbc/mysql.rb +66 -0
  36. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  37. data/lib/sequel_core/adapters/jdbc/postgresql.rb +113 -0
  38. data/lib/sequel_core/adapters/jdbc/sqlite.rb +43 -0
  39. data/lib/sequel_core/adapters/jdbc.rb +491 -0
  40. data/lib/sequel_core/adapters/mysql.rb +369 -0
  41. data/lib/sequel_core/adapters/odbc.rb +174 -0
  42. data/lib/sequel_core/adapters/openbase.rb +68 -0
  43. data/lib/sequel_core/adapters/oracle.rb +107 -0
  44. data/lib/sequel_core/adapters/postgres.rb +456 -0
  45. data/lib/sequel_core/adapters/shared/ms_access.rb +110 -0
  46. data/lib/sequel_core/adapters/shared/mssql.rb +102 -0
  47. data/lib/sequel_core/adapters/shared/mysql.rb +325 -0
  48. data/lib/sequel_core/adapters/shared/oracle.rb +61 -0
  49. data/lib/sequel_core/adapters/shared/postgres.rb +715 -0
  50. data/lib/sequel_core/adapters/shared/progress.rb +31 -0
  51. data/lib/sequel_core/adapters/shared/sqlite.rb +265 -0
  52. data/lib/sequel_core/adapters/sqlite.rb +248 -0
  53. data/lib/sequel_core/connection_pool.rb +258 -0
  54. data/lib/sequel_core/core_ext.rb +217 -0
  55. data/lib/sequel_core/core_sql.rb +202 -0
  56. data/lib/sequel_core/database/schema.rb +164 -0
  57. data/lib/sequel_core/database.rb +691 -0
  58. data/lib/sequel_core/dataset/callback.rb +13 -0
  59. data/lib/sequel_core/dataset/convenience.rb +237 -0
  60. data/lib/sequel_core/dataset/pagination.rb +96 -0
  61. data/lib/sequel_core/dataset/prepared_statements.rb +220 -0
  62. data/lib/sequel_core/dataset/query.rb +41 -0
  63. data/lib/sequel_core/dataset/schema.rb +15 -0
  64. data/lib/sequel_core/dataset/sql.rb +1010 -0
  65. data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
  66. data/lib/sequel_core/dataset/unsupported.rb +43 -0
  67. data/lib/sequel_core/dataset.rb +511 -0
  68. data/lib/sequel_core/deprecated.rb +26 -0
  69. data/lib/sequel_core/exceptions.rb +44 -0
  70. data/lib/sequel_core/migration.rb +212 -0
  71. data/lib/sequel_core/object_graph.rb +230 -0
  72. data/lib/sequel_core/pretty_table.rb +71 -0
  73. data/lib/sequel_core/schema/generator.rb +320 -0
  74. data/lib/sequel_core/schema/sql.rb +325 -0
  75. data/lib/sequel_core/schema.rb +2 -0
  76. data/lib/sequel_core/sql.rb +887 -0
  77. data/lib/sequel_core/version.rb +11 -0
  78. data/lib/sequel_core.rb +172 -0
  79. data/lib/sequel_model/association_reflection.rb +267 -0
  80. data/lib/sequel_model/associations.rb +499 -0
  81. data/lib/sequel_model/base.rb +523 -0
  82. data/lib/sequel_model/caching.rb +82 -0
  83. data/lib/sequel_model/dataset_methods.rb +26 -0
  84. data/lib/sequel_model/eager_loading.rb +370 -0
  85. data/lib/sequel_model/exceptions.rb +7 -0
  86. data/lib/sequel_model/hooks.rb +101 -0
  87. data/lib/sequel_model/inflector.rb +281 -0
  88. data/lib/sequel_model/plugins.rb +62 -0
  89. data/lib/sequel_model/record.rb +568 -0
  90. data/lib/sequel_model/schema.rb +49 -0
  91. data/lib/sequel_model/validations.rb +429 -0
  92. data/lib/sequel_model.rb +91 -0
  93. data/spec/adapters/ado_spec.rb +46 -0
  94. data/spec/adapters/firebird_spec.rb +376 -0
  95. data/spec/adapters/informix_spec.rb +96 -0
  96. data/spec/adapters/mysql_spec.rb +881 -0
  97. data/spec/adapters/oracle_spec.rb +244 -0
  98. data/spec/adapters/postgres_spec.rb +687 -0
  99. data/spec/adapters/spec_helper.rb +10 -0
  100. data/spec/adapters/sqlite_spec.rb +555 -0
  101. data/spec/integration/dataset_test.rb +134 -0
  102. data/spec/integration/eager_loader_test.rb +696 -0
  103. data/spec/integration/prepared_statement_test.rb +130 -0
  104. data/spec/integration/schema_test.rb +180 -0
  105. data/spec/integration/spec_helper.rb +58 -0
  106. data/spec/integration/type_test.rb +96 -0
  107. data/spec/rcov.opts +6 -0
  108. data/spec/sequel_core/connection_pool_spec.rb +526 -0
  109. data/spec/sequel_core/core_ext_spec.rb +156 -0
  110. data/spec/sequel_core/core_sql_spec.rb +522 -0
  111. data/spec/sequel_core/database_spec.rb +1188 -0
  112. data/spec/sequel_core/dataset_spec.rb +3481 -0
  113. data/spec/sequel_core/expression_filters_spec.rb +363 -0
  114. data/spec/sequel_core/migration_spec.rb +261 -0
  115. data/spec/sequel_core/object_graph_spec.rb +272 -0
  116. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  117. data/spec/sequel_core/schema_generator_spec.rb +167 -0
  118. data/spec/sequel_core/schema_spec.rb +780 -0
  119. data/spec/sequel_core/spec_helper.rb +55 -0
  120. data/spec/sequel_core/version_spec.rb +7 -0
  121. data/spec/sequel_model/association_reflection_spec.rb +93 -0
  122. data/spec/sequel_model/associations_spec.rb +1767 -0
  123. data/spec/sequel_model/base_spec.rb +419 -0
  124. data/spec/sequel_model/caching_spec.rb +215 -0
  125. data/spec/sequel_model/dataset_methods_spec.rb +78 -0
  126. data/spec/sequel_model/eager_loading_spec.rb +1165 -0
  127. data/spec/sequel_model/hooks_spec.rb +485 -0
  128. data/spec/sequel_model/inflector_spec.rb +119 -0
  129. data/spec/sequel_model/model_spec.rb +588 -0
  130. data/spec/sequel_model/plugins_spec.rb +80 -0
  131. data/spec/sequel_model/record_spec.rb +1184 -0
  132. data/spec/sequel_model/schema_spec.rb +90 -0
  133. data/spec/sequel_model/spec_helper.rb +78 -0
  134. data/spec/sequel_model/validations_spec.rb +1067 -0
  135. data/spec/spec.opts +0 -0
  136. data/spec/spec_config.rb.example +10 -0
  137. metadata +177 -3
@@ -0,0 +1,107 @@
1
+ require 'oci8'
2
+ require 'sequel_core/adapters/shared/oracle'
3
+
4
+ module Sequel
5
+ module Oracle
6
+ class Database < Sequel::Database
7
+ include DatabaseMethods
8
+ set_adapter_scheme :oracle
9
+
10
+ # ORA-00028: your session has been killed
11
+ # ORA-01012: not logged on
12
+ # ORA-03113: end-of-file on communication channel
13
+ # ORA-03114: not connected to ORACLE
14
+ CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
15
+
16
+ def connect(server)
17
+ opts = server_opts(server)
18
+ if opts[:database]
19
+ dbname = opts[:host] ? \
20
+ "//#{opts[:host]}#{":#{opts[:port]}" if opts[:port]}/#{opts[:database]}" : opts[:database]
21
+ else
22
+ dbname = opts[:host]
23
+ end
24
+ conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
25
+ conn.autocommit = true
26
+ conn.non_blocking = true
27
+ conn
28
+ end
29
+
30
+ def dataset(opts = nil)
31
+ Oracle::Dataset.new(self, opts)
32
+ end
33
+
34
+ def execute(sql, opts={})
35
+ log_info(sql)
36
+ synchronize(opts[:server]) do |conn|
37
+ begin
38
+ r = conn.exec(sql)
39
+ yield(r) if block_given?
40
+ r
41
+ rescue OCIException => e
42
+ if CONNECTION_ERROR_CODES.include?(e.code)
43
+ raise(Sequel::DatabaseDisconnectError)
44
+ else
45
+ raise
46
+ end
47
+ end
48
+ end
49
+ end
50
+ alias_method :do, :execute
51
+
52
+ def transaction(server=nil)
53
+ synchronize(server) do |conn|
54
+ return yield(conn) if @transactions.include?(Thread.current)
55
+
56
+ conn.autocommit = false
57
+ begin
58
+ @transactions << Thread.current
59
+ yield(conn)
60
+ rescue => e
61
+ conn.rollback
62
+ raise e unless Error::Rollback === e
63
+ ensure
64
+ conn.commit unless e
65
+ conn.autocommit = true
66
+ @transactions.delete(Thread.current)
67
+ end
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def disconnect_connection(c)
74
+ c.logoff
75
+ end
76
+ end
77
+
78
+ class Dataset < Sequel::Dataset
79
+ include DatasetMethods
80
+
81
+ def literal(v)
82
+ case v
83
+ when OraDate
84
+ literal(Time.local(*v.to_a))
85
+ else
86
+ super
87
+ end
88
+ end
89
+
90
+ def fetch_rows(sql, &block)
91
+ execute(sql) do |cursor|
92
+ begin
93
+ @columns = cursor.get_col_names.map{|c| output_identifier(c)}
94
+ while r = cursor.fetch
95
+ row = {}
96
+ r.each_with_index {|v, i| row[@columns[i]] = v unless @columns[i] == :raw_rnum_}
97
+ yield row
98
+ end
99
+ ensure
100
+ cursor.close
101
+ end
102
+ end
103
+ self
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,456 @@
1
+ require 'sequel_core/adapters/shared/postgres'
2
+
3
+ begin
4
+ require 'pg'
5
+ SEQUEL_POSTGRES_USES_PG = true
6
+ rescue LoadError => e
7
+ SEQUEL_POSTGRES_USES_PG = false
8
+ begin
9
+ require 'postgres'
10
+ # Attempt to get uniform behavior for the PGconn object no matter
11
+ # if pg, postgres, or postgres-pr is used.
12
+ class PGconn
13
+ unless method_defined?(:escape_string)
14
+ if self.respond_to?(:escape)
15
+ # If there is no escape_string instead method, but there is an
16
+ # escape class method, use that instead.
17
+ def escape_string(str)
18
+ Sequel::Postgres.force_standard_strings ? str.gsub("'", "''") : self.class.escape(str)
19
+ end
20
+ else
21
+ # Raise an error if no valid string escaping method can be found.
22
+ def escape_string(obj)
23
+ raise Sequel::Error, "string escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
24
+ end
25
+ end
26
+ end
27
+ unless method_defined?(:escape_bytea)
28
+ if self.respond_to?(:escape_bytea)
29
+ # If there is no escape_bytea instance method, but there is an
30
+ # escape_bytea class method, use that instead.
31
+ def escape_bytea(obj)
32
+ self.class.escape_bytea(obj)
33
+ end
34
+ else
35
+ begin
36
+ require 'postgres-pr/typeconv/conv'
37
+ require 'postgres-pr/typeconv/bytea'
38
+ extend Postgres::Conversion
39
+ # If we are using postgres-pr, use the encode_bytea method from
40
+ # that.
41
+ def escape_bytea(obj)
42
+ self.class.encode_bytea(obj)
43
+ end
44
+ metaalias :unescape_bytea, :decode_bytea
45
+ rescue
46
+ # If no valid bytea escaping method can be found, create one that
47
+ # raises an error
48
+ def escape_bytea(obj)
49
+ raise Sequel::Error, "bytea escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
50
+ end
51
+ # If no valid bytea unescaping method can be found, create one that
52
+ # raises an error
53
+ def self.unescape_bytea(obj)
54
+ raise Sequel::Error, "bytea unescaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
55
+ end
56
+ end
57
+ end
58
+ end
59
+ alias_method :finish, :close unless method_defined?(:finish)
60
+ alias_method :async_exec, :exec unless method_defined?(:async_exec)
61
+ unless method_defined?(:block)
62
+ def block(timeout=nil)
63
+ end
64
+ end
65
+ unless defined?(CONNECTION_OK)
66
+ CONNECTION_OK = -1
67
+ end
68
+ end
69
+ class PGresult
70
+ alias_method :nfields, :num_fields unless method_defined?(:nfields)
71
+ alias_method :ntuples, :num_tuples unless method_defined?(:ntuples)
72
+ alias_method :ftype, :type unless method_defined?(:ftype)
73
+ alias_method :fname, :fieldname unless method_defined?(:fname)
74
+ alias_method :cmd_tuples, :cmdtuples unless method_defined?(:cmd_tuples)
75
+ end
76
+ rescue LoadError
77
+ raise e
78
+ end
79
+ end
80
+
81
+ module Sequel
82
+ module Postgres
83
+ CONVERTED_EXCEPTIONS << PGError
84
+
85
+ # Hash with integer keys and proc values for converting PostgreSQL types.
86
+ PG_TYPES = {
87
+ 16 => lambda{ |s| s == 't' }, # boolean
88
+ 17 => lambda{ |s| ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) }, # bytea
89
+ 20 => lambda{ |s| s.to_i }, # int8
90
+ 21 => lambda{ |s| s.to_i }, # int2
91
+ 22 => lambda{ |s| s.to_i }, # int2vector
92
+ 23 => lambda{ |s| s.to_i }, # int4
93
+ 26 => lambda{ |s| s.to_i }, # oid
94
+ 700 => lambda{ |s| s.to_f }, # float4
95
+ 701 => lambda{ |s| s.to_f }, # float8
96
+ 790 => lambda{ |s| s.to_d }, # money
97
+ 1082 => lambda{ |s| @use_iso_date_format ? Date.new(*s.split("-").map{|x| x.to_i}) : s.to_date }, # date
98
+ 1083 => lambda{ |s| s.to_time }, # time without time zone
99
+ 1114 => lambda{ |s| s.to_sequel_time }, # timestamp without time zone
100
+ 1184 => lambda{ |s| s.to_sequel_time }, # timestamp with time zone
101
+ 1266 => lambda{ |s| s.to_time }, # time with time zone
102
+ 1700 => lambda{ |s| s.to_d }, # numeric
103
+ }
104
+
105
+ @use_iso_date_format = true
106
+
107
+ # As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
108
+ # the date in a known format that Sequel can parse faster. This can be turned off
109
+ # if you require a date style other than ISO.
110
+ metaattr_accessor :use_iso_date_format
111
+
112
+ # PGconn subclass for connection specific methods used with the
113
+ # pg, postgres, or postgres-pr driver.
114
+ class Adapter < ::PGconn
115
+ include Sequel::Postgres::AdapterMethods
116
+ self.translate_results = false if respond_to?(:translate_results=)
117
+
118
+ # Apply connection settings for this connection. Current sets
119
+ # the date style to ISO in order make Date object creation in ruby faster,
120
+ # if Postgres.use_iso_date_format is true.
121
+ def apply_connection_settings
122
+ super
123
+ if Postgres.use_iso_date_format
124
+ sql = "SET DateStyle = 'ISO'"
125
+ @db.log_info(sql)
126
+ execute(sql)
127
+ end
128
+ end
129
+
130
+ # Execute the given SQL with this connection. If a block is given,
131
+ # yield the results, otherwise, return the number of changed rows.
132
+ def execute(sql, args=nil)
133
+ q = nil
134
+ begin
135
+ q = args ? async_exec(sql, args) : async_exec(sql)
136
+ rescue PGError
137
+ begin
138
+ s = status
139
+ rescue PGError
140
+ raise(Sequel::DatabaseDisconnectError)
141
+ end
142
+ status_ok = (s == Adapter::CONNECTION_OK)
143
+ status_ok ? raise : raise(Sequel::DatabaseDisconnectError)
144
+ ensure
145
+ block if status_ok
146
+ end
147
+ begin
148
+ block_given? ? yield(q) : q.cmd_tuples
149
+ ensure
150
+ q.clear
151
+ end
152
+ end
153
+
154
+ # Reapply the connection settings if the connection is reset.
155
+ def reset(*args, &block)
156
+ super(*args, &block)
157
+ apply_connection_settings
158
+ end
159
+
160
+ if SEQUEL_POSTGRES_USES_PG
161
+ # Hash of prepared statements for this connection. Keys are
162
+ # string names of the server side prepared statement, and values
163
+ # are SQL strings.
164
+ def prepared_statements
165
+ @prepared_statements ||= {}
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ # Return the requested values for the given row.
172
+ def single_value(r)
173
+ r.getvalue(0, 0) unless r.nil? || (r.ntuples == 0)
174
+ end
175
+ end
176
+
177
+ # Database class for PostgreSQL databases used with Sequel and the
178
+ # pg, postgres, or postgres-pr driver.
179
+ class Database < Sequel::Database
180
+ include Sequel::Postgres::DatabaseMethods
181
+
182
+ set_adapter_scheme :postgres
183
+
184
+ # Add the primary_keys and primary_key_sequences instance variables,
185
+ # so we can get the correct return values for inserted rows.
186
+ def initialize(*args)
187
+ super
188
+ @primary_keys = {}
189
+ @primary_key_sequences = {}
190
+ end
191
+
192
+ # Connects to the database. In addition to the standard database
193
+ # options, using the :encoding or :charset option changes the
194
+ # client encoding for the connection.
195
+ def connect(server)
196
+ opts = server_opts(server)
197
+ conn = Adapter.connect(
198
+ (opts[:host] unless opts[:host].blank?),
199
+ opts[:port] || 5432,
200
+ nil, '',
201
+ opts[:database],
202
+ opts[:user],
203
+ opts[:password]
204
+ )
205
+ if encoding = opts[:encoding] || opts[:charset]
206
+ if conn.respond_to?(:set_client_encoding)
207
+ conn.set_client_encoding(encoding)
208
+ else
209
+ conn.async_exec("set client_encoding to '#{encoding}'")
210
+ end
211
+ end
212
+ conn.db = self
213
+ conn.apply_connection_settings
214
+ conn
215
+ end
216
+
217
+ # Return instance of Sequel::Postgres::Dataset with the given options.
218
+ def dataset(opts = nil)
219
+ Postgres::Dataset.new(self, opts)
220
+ end
221
+
222
+ # Execute the given SQL with the given args on an available connection.
223
+ def execute(sql, opts={}, &block)
224
+ return execute_prepared_statement(sql, opts, &block) if Symbol === sql
225
+ begin
226
+ log_info(sql, opts[:arguments])
227
+ synchronize(opts[:server]){|conn| conn.execute(sql, opts[:arguments], &block)}
228
+ rescue => e
229
+ log_info(e.message)
230
+ raise_error(e, :classes=>CONVERTED_EXCEPTIONS)
231
+ end
232
+ end
233
+
234
+ # Insert the values into the table and return the primary key (if
235
+ # automatically generated).
236
+ def execute_insert(sql, opts={})
237
+ return execute(sql, opts) if Symbol === sql
238
+ begin
239
+ log_info(sql, opts[:arguments])
240
+ synchronize(opts[:server]) do |conn|
241
+ conn.execute(sql, opts[:arguments])
242
+ insert_result(conn, opts[:table], opts[:values])
243
+ end
244
+ rescue => e
245
+ log_info(e.message)
246
+ raise_error(e, :classes=>CONVERTED_EXCEPTIONS)
247
+ end
248
+ end
249
+
250
+ private
251
+
252
+ # PostgreSQL doesn't need the connection pool to convert exceptions.
253
+ def connection_pool_default_options
254
+ super.merge(:pool_convert_exceptions=>false)
255
+ end
256
+
257
+ # Disconnect given connection
258
+ def disconnect_connection(conn)
259
+ begin
260
+ conn.finish
261
+ rescue PGError
262
+ end
263
+ end
264
+
265
+ # Execute the prepared statement with the given name on an available
266
+ # connection, using the given args. If the connection has not prepared
267
+ # a statement with the given name yet, prepare it. If the connection
268
+ # has prepared a statement with the same name and different SQL,
269
+ # deallocate that statement first and then prepare this statement.
270
+ # If a block is given, yield the result, otherwise, return the number
271
+ # of rows changed. If the :insert option is passed, return the value
272
+ # of the primary key for the last inserted row.
273
+ def execute_prepared_statement(name, opts={})
274
+ ps = prepared_statements[name]
275
+ sql = ps.prepared_sql
276
+ ps_name = name.to_s
277
+ args = opts[:arguments]
278
+ synchronize(opts[:server]) do |conn|
279
+ unless conn.prepared_statements[ps_name] == sql
280
+ if conn.prepared_statements.include?(ps_name)
281
+ s = "DEALLOCATE #{ps_name}"
282
+ log_info(s)
283
+ conn.execute(s) unless conn.prepared_statements[ps_name] == sql
284
+ end
285
+ conn.prepared_statements[ps_name] = sql
286
+ log_info("PREPARE #{ps_name} AS #{sql}")
287
+ conn.prepare(ps_name, sql)
288
+ end
289
+ log_info("EXECUTE #{ps_name}", args)
290
+ q = conn.exec_prepared(ps_name, args)
291
+ if opts[:table] && opts[:values]
292
+ insert_result(conn, opts[:table], opts[:values])
293
+ else
294
+ begin
295
+ block_given? ? yield(q) : q.cmd_tuples
296
+ ensure
297
+ q.clear
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+
304
+ # Dataset class for PostgreSQL datasets that use the pg, postgres, or
305
+ # postgres-pr driver.
306
+ class Dataset < Sequel::Dataset
307
+ include Sequel::Postgres::DatasetMethods
308
+
309
+ # Yield all rows returned by executing the given SQL and converting
310
+ # the types.
311
+ def fetch_rows(sql)
312
+ cols = []
313
+ execute(sql) do |res|
314
+ res.nfields.times do |fieldnum|
315
+ cols << [fieldnum, PG_TYPES[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
316
+ end
317
+ @columns = cols.map{|c| c.at(2)}
318
+ res.ntuples.times do |recnum|
319
+ converted_rec = {}
320
+ cols.each do |fieldnum, type_proc, fieldsym|
321
+ value = res.getvalue(recnum, fieldnum)
322
+ converted_rec[fieldsym] = (value && type_proc) ? type_proc.call(value) : value
323
+ end
324
+ yield converted_rec
325
+ end
326
+ end
327
+ end
328
+
329
+ # Literalize strings and blobs using code from the native adapter.
330
+ def literal(v)
331
+ case v
332
+ when LiteralString
333
+ v
334
+ when SQL::Blob
335
+ db.synchronize{|c| "'#{c.escape_bytea(v)}'"}
336
+ when String
337
+ db.synchronize{|c| "'#{c.escape_string(v)}'"}
338
+ else
339
+ super
340
+ end
341
+ end
342
+
343
+ if SEQUEL_POSTGRES_USES_PG
344
+
345
+ PREPARED_ARG_PLACEHOLDER = '$'.lit.freeze
346
+
347
+ # PostgreSQL specific argument mapper used for mapping the named
348
+ # argument hash to a array with numbered arguments. Only used with
349
+ # the pg driver.
350
+ module ArgumentMapper
351
+ include Sequel::Dataset::ArgumentMapper
352
+
353
+ protected
354
+
355
+ # Return an array of strings for each of the hash values, inserting
356
+ # them to the correct position in the array.
357
+ def map_to_prepared_args(hash)
358
+ @prepared_args.map{|k| hash[k.to_sym].to_s}
359
+ end
360
+
361
+ private
362
+
363
+ # PostgreSQL most of the time requires type information for each of
364
+ # arguments to a prepared statement. Handle this by allowing the
365
+ # named argument to have a __* suffix, with the * being the type.
366
+ # In the generated SQL, cast the bound argument to that type to
367
+ # elminate ambiguity (and PostgreSQL from raising an exception).
368
+ def prepared_arg(k)
369
+ y, type = k.to_s.split("__")
370
+ if i = @prepared_args.index(y)
371
+ i += 1
372
+ else
373
+ @prepared_args << y
374
+ i = @prepared_args.length
375
+ end
376
+ "#{prepared_arg_placeholder}#{i}#{"::#{type}" if type}".lit
377
+ end
378
+ end
379
+
380
+ # Allow use of bind arguments for PostgreSQL using the pg driver.
381
+ module BindArgumentMethods
382
+ include ArgumentMapper
383
+
384
+ private
385
+
386
+ # Execute the given SQL with the stored bind arguments.
387
+ def execute(sql, opts={}, &block)
388
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
389
+ end
390
+
391
+ # Same as execute, explicit due to intricacies of alias and super.
392
+ def execute_dui(sql, opts={}, &block)
393
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
394
+ end
395
+
396
+ # Same as execute, explicit due to intricacies of alias and super.
397
+ def execute_insert(sql, opts={}, &block)
398
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
399
+ end
400
+ end
401
+
402
+ # Allow use of server side prepared statements for PostgreSQL using the
403
+ # pg driver.
404
+ module PreparedStatementMethods
405
+ include BindArgumentMethods
406
+ include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
407
+
408
+ private
409
+
410
+ # Execute the stored prepared statement name and the stored bind
411
+ # arguments instead of the SQL given.
412
+ def execute(sql, opts={}, &block)
413
+ super(prepared_statement_name, opts, &block)
414
+ end
415
+
416
+ # Same as execute, explicit due to intricacies of alias and super.
417
+ def execute_dui(sql, opts={}, &block)
418
+ super(prepared_statement_name, opts, &block)
419
+ end
420
+
421
+ # Same as execute, explicit due to intricacies of alias and super.
422
+ def execute_insert(sql, opts={}, &block)
423
+ super(prepared_statement_name, opts, &block)
424
+ end
425
+ end
426
+
427
+ # Execute the given type of statement with the hash of values.
428
+ def call(type, hash, values=nil, &block)
429
+ ps = to_prepared_statement(type, values)
430
+ ps.extend(BindArgumentMethods)
431
+ ps.call(hash, &block)
432
+ end
433
+
434
+ # Prepare the given type of statement with the given name, and store
435
+ # it in the database to be called later.
436
+ def prepare(type, name=nil, values=nil)
437
+ ps = to_prepared_statement(type, values)
438
+ ps.extend(PreparedStatementMethods)
439
+ if name
440
+ ps.prepared_statement_name = name
441
+ db.prepared_statements[name] = ps
442
+ end
443
+ ps
444
+ end
445
+
446
+ private
447
+
448
+ # PostgreSQL uses $N for placeholders instead of ?, so use a $
449
+ # as the placeholder.
450
+ def prepared_arg_placeholder
451
+ PREPARED_ARG_PLACEHOLDER
452
+ end
453
+ end
454
+ end
455
+ end
456
+ end
@@ -0,0 +1,110 @@
1
+ module Sequel
2
+ module MSAccess
3
+ module DatabaseMethods
4
+ AUTO_INCREMENT = 'COUNTER(1,1)'.freeze
5
+ SQL_BEGIN = "BEGIN TRANSACTION".freeze
6
+ SQL_COMMIT = "COMMIT TRANSACTION".freeze
7
+ SQL_ROLLBACK = "ROLLBACK TRANSACTION".freeze
8
+
9
+ def auto_increment_sql
10
+ AUTO_INCREMENT
11
+ end
12
+
13
+ def dataset(opts = nil)
14
+ ds = super
15
+ ds.extend(DatasetMethods)
16
+ ds
17
+ end
18
+
19
+ def tables
20
+ self[:MSysObjects].select(:Name).filter({:Type => 1, :Flags => 0} | {:Type => 6}).collect { |result| result[:Name] }
21
+ end
22
+
23
+ def upcase_identifiers?
24
+ false
25
+ end
26
+
27
+ private
28
+
29
+ # SQL to BEGIN a transaction.
30
+ def begin_transaction_sql
31
+ SQL_BEGIN
32
+ end
33
+
34
+ # SQL to COMMIT a transaction.
35
+ def commit_transaction_sql
36
+ SQL_COMMIT
37
+ end
38
+
39
+ # SQL to ROLLBACK a transaction.
40
+ def rollback_transaction_sql
41
+ SQL_ROLLBACK
42
+ end
43
+ end
44
+
45
+ module DatasetMethods
46
+ include Dataset::UnsupportedIntersectExcept
47
+
48
+ SELECT_CLAUSE_ORDER = %w'limit distinct columns from with join where group order having union'.freeze
49
+
50
+ def complex_expression_sql(op, args)
51
+ case op
52
+ when :'||'
53
+ super(:+, args)
54
+ else
55
+ super(op, args)
56
+ end
57
+ end
58
+
59
+ def full_text_search(cols, terms, opts = {})
60
+ filter("CONTAINS (#{literal(cols)}, #{literal(terms)})")
61
+ end
62
+
63
+ def literal(v)
64
+ case v
65
+ when Time
66
+ literal(v.iso8601)
67
+ when Date, DateTime
68
+ literal(v.to_s)
69
+ when TrueClass
70
+ 'TRUE'
71
+ when FalseClass
72
+ 'FALSE'
73
+ else
74
+ super
75
+ end
76
+ end
77
+
78
+ def multi_insert_sql(columns, values)
79
+ values = values.map {|r| "SELECT #{expression_list(r)}" }.join(" UNION ALL ")
80
+ ["INSERT INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) #{values}"]
81
+ end
82
+
83
+ # Allows you to do .nolock on a query
84
+ def nolock
85
+ clone(:with => "(NOLOCK)")
86
+ end
87
+
88
+ def quoted_identifier(name)
89
+ "[#{name}]"
90
+ end
91
+
92
+ private
93
+
94
+ def select_clause_order
95
+ SELECT_CLAUSE_ORDER
96
+ end
97
+
98
+ # ACCESS uses TOP for limit, with no offset support
99
+ def select_limit_sql(sql, opts)
100
+ raise(Error, "OFFSET not supported") if opts[:offset]
101
+ sql << " TOP #{opts[:limit]}" if opts[:limit]
102
+ end
103
+
104
+ # MSSQL uses the WITH statement to lock tables
105
+ def select_with_sql(sql, opts)
106
+ sql << " WITH #{opts[:with]}" if opts[:with]
107
+ end
108
+ end
109
+ end
110
+ end