sequel_core 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG +116 -0
  2. data/COPYING +19 -19
  3. data/README +83 -32
  4. data/Rakefile +9 -20
  5. data/bin/sequel +43 -112
  6. data/doc/cheat_sheet.rdoc +225 -0
  7. data/doc/dataset_filtering.rdoc +257 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
  9. data/lib/sequel_core/adapters/ado.rb +3 -1
  10. data/lib/sequel_core/adapters/db2.rb +4 -2
  11. data/lib/sequel_core/adapters/dbi.rb +127 -113
  12. data/lib/sequel_core/adapters/informix.rb +4 -2
  13. data/lib/sequel_core/adapters/jdbc.rb +5 -3
  14. data/lib/sequel_core/adapters/mysql.rb +112 -46
  15. data/lib/sequel_core/adapters/odbc.rb +5 -7
  16. data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
  17. data/lib/sequel_core/adapters/openbase.rb +3 -1
  18. data/lib/sequel_core/adapters/oracle.rb +11 -9
  19. data/lib/sequel_core/adapters/postgres.rb +261 -262
  20. data/lib/sequel_core/adapters/sqlite.rb +72 -22
  21. data/lib/sequel_core/connection_pool.rb +140 -73
  22. data/lib/sequel_core/core_ext.rb +201 -66
  23. data/lib/sequel_core/core_sql.rb +123 -153
  24. data/lib/sequel_core/database/schema.rb +156 -0
  25. data/lib/sequel_core/database.rb +321 -338
  26. data/lib/sequel_core/dataset/callback.rb +11 -12
  27. data/lib/sequel_core/dataset/convenience.rb +213 -240
  28. data/lib/sequel_core/dataset/pagination.rb +58 -43
  29. data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
  30. data/lib/sequel_core/dataset/query.rb +41 -0
  31. data/lib/sequel_core/dataset/schema.rb +15 -0
  32. data/lib/sequel_core/dataset/sequelizer.rb +41 -373
  33. data/lib/sequel_core/dataset/sql.rb +741 -632
  34. data/lib/sequel_core/dataset.rb +183 -168
  35. data/lib/sequel_core/deprecated.rb +1 -169
  36. data/lib/sequel_core/exceptions.rb +24 -19
  37. data/lib/sequel_core/migration.rb +44 -52
  38. data/lib/sequel_core/object_graph.rb +43 -42
  39. data/lib/sequel_core/pretty_table.rb +71 -76
  40. data/lib/sequel_core/schema/generator.rb +163 -105
  41. data/lib/sequel_core/schema/sql.rb +250 -93
  42. data/lib/sequel_core/schema.rb +2 -8
  43. data/lib/sequel_core/sql.rb +394 -0
  44. data/lib/sequel_core/worker.rb +37 -27
  45. data/lib/sequel_core.rb +99 -45
  46. data/spec/adapters/informix_spec.rb +0 -1
  47. data/spec/adapters/mysql_spec.rb +177 -124
  48. data/spec/adapters/oracle_spec.rb +0 -1
  49. data/spec/adapters/postgres_spec.rb +98 -58
  50. data/spec/adapters/sqlite_spec.rb +45 -4
  51. data/spec/blockless_filters_spec.rb +269 -0
  52. data/spec/connection_pool_spec.rb +21 -18
  53. data/spec/core_ext_spec.rb +169 -19
  54. data/spec/core_sql_spec.rb +56 -49
  55. data/spec/database_spec.rb +78 -17
  56. data/spec/dataset_spec.rb +300 -428
  57. data/spec/migration_spec.rb +1 -1
  58. data/spec/object_graph_spec.rb +5 -11
  59. data/spec/rcov.opts +1 -1
  60. data/spec/schema_generator_spec.rb +16 -4
  61. data/spec/schema_spec.rb +89 -10
  62. data/spec/sequelizer_spec.rb +56 -56
  63. data/spec/spec.opts +0 -5
  64. data/spec/spec_config.rb +7 -0
  65. data/spec/spec_config.rb.example +5 -5
  66. data/spec/spec_helper.rb +6 -0
  67. data/spec/worker_spec.rb +1 -1
  68. metadata +78 -63
@@ -1,198 +1,191 @@
1
- require 'postgres'
2
-
3
- class PGconn
4
- # the pure-ruby postgres adapter does not have a quote method.
5
- TRUE = 'true'.freeze
6
- FALSE = 'false'.freeze
7
- NULL = 'NULL'.freeze
8
-
9
- unless methods.include?('quote')
10
- def self.quote(obj)
11
- case obj
12
- when true
13
- TRUE
14
- when false
15
- FALSE
16
- when nil
17
- NULL
18
- when String
19
- "'#{obj}'"
20
- else
21
- obj.to_s
22
- end
1
+ begin
2
+ require 'pg'
3
+ rescue LoadError => e
4
+ begin
5
+ require 'postgres'
6
+ class PGconn
7
+ metaalias :escape_string, :escape unless self.respond_to?(:escape_string)
8
+ alias_method :finish, :close unless method_defined?(:finish)
23
9
  end
24
- end
25
-
26
- class << self
27
- # The postgres gem's string quoting doesn't render string literals properly, which this fixes.
28
- #
29
- # "a basic string" #=> 'a basic string'
30
- # "this\or that" #=> E'this\\or that'
31
- #
32
- # See <http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html> for details.
33
- def quote_with_proper_escaping(s)
34
- value = quote_without_proper_escaping(s)
35
- value = "E#{value}" if value =~ /\\/
36
- return value
37
- end
38
- alias_method :quote_without_proper_escaping, :quote
39
- alias_method :quote, :quote_with_proper_escaping
40
- end
10
+ class PGresult
11
+ alias_method :nfields, :num_fields unless method_defined?(:nfields)
12
+ alias_method :ntuples, :num_tuples unless method_defined?(:ntuples)
13
+ alias_method :ftype, :type unless method_defined?(:ftype)
14
+ alias_method :fname, :fieldname unless method_defined?(:fname)
15
+ alias_method :cmd_tuples, :cmdtuples unless method_defined?(:cmd_tuples)
16
+ end
17
+ rescue LoadError
18
+ raise e
19
+ end
20
+ end
41
21
 
42
- def connected?
43
- status == PGconn::CONNECTION_OK
44
- end
45
-
46
- unless instance_methods.include?('async_exec')
47
- alias_method :async_exec, :exec
48
- end
49
-
50
- unless instance_methods.include?('async_query')
51
- alias_method :async_query, :query
52
- end
53
-
54
- def execute(sql, &block)
55
- q = nil
56
- begin
57
- q = async_exec(sql)
58
- rescue PGError => e
59
- unless connected?
60
- reset
61
- q = async_exec(sql)
62
- else
63
- raise e
22
+ module Sequel
23
+ module Postgres
24
+ class Adapter < ::PGconn
25
+ # the pure-ruby postgres adapter does not have a quote method.
26
+ TRUE = 'true'.freeze
27
+ FALSE = 'false'.freeze
28
+ NULL = 'NULL'.freeze
29
+
30
+ def self.quote(obj)
31
+ case obj
32
+ when TrueClass
33
+ TRUE
34
+ when FalseClass
35
+ FALSE
36
+ when NilClass
37
+ NULL
38
+ else
39
+ "'#{escape_string(obj.to_s)}'"
40
+ end
64
41
  end
65
- end
66
- begin
67
- block ? block[q] : q.cmdtuples
68
- ensure
69
- q.clear
70
- end
71
- end
72
-
73
- attr_accessor :transaction_in_progress
74
-
75
- SELECT_CURRVAL = "SELECT currval('%s')".freeze
76
42
 
77
- def last_insert_id(table)
78
- @table_sequences ||= {}
79
- if !@table_sequences.include?(table)
80
- pkey_and_seq = pkey_and_sequence(table)
81
- if pkey_and_seq
82
- @table_sequences[table] = pkey_and_seq[1]
43
+ def connected?
44
+ status == Adapter::CONNECTION_OK
83
45
  end
84
- end
85
- if seq = @table_sequences[table]
86
- r = async_query(SELECT_CURRVAL % seq)
87
- return r[0][0].to_i unless r.nil? || (r.respond_to?(:empty?) && r.empty?)
88
- end
89
- nil # primary key sequence not found
90
- end
91
46
 
92
- # Shamelessly appropriated from ActiveRecord's Postgresql adapter.
93
-
94
- SELECT_PK_AND_SERIAL_SEQUENCE = <<-end_sql
95
- SELECT attr.attname, name.nspname, seq.relname
96
- FROM pg_class seq, pg_attribute attr, pg_depend dep,
97
- pg_namespace name, pg_constraint cons
98
- WHERE seq.oid = dep.objid
99
- AND seq.relnamespace = name.oid
100
- AND seq.relkind = 'S'
101
- AND attr.attrelid = dep.refobjid
102
- AND attr.attnum = dep.refobjsubid
103
- AND attr.attrelid = cons.conrelid
104
- AND attr.attnum = cons.conkey[1]
105
- AND cons.contype = 'p'
106
- AND dep.refobjid = '%s'::regclass
107
- end_sql
108
-
109
- SELECT_PK_AND_CUSTOM_SEQUENCE = <<-end_sql
110
- SELECT attr.attname, name.nspname, split_part(def.adsrc, '''', 2)
111
- FROM pg_class t
112
- JOIN pg_namespace name ON (t.relnamespace = name.oid)
113
- JOIN pg_attribute attr ON (t.oid = attrelid)
114
- JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
115
- JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
116
- WHERE t.oid = '%s'::regclass
117
- AND cons.contype = 'p'
118
- AND def.adsrc ~* 'nextval'
119
- end_sql
120
-
121
- SELECT_PK = <<-end_sql
122
- SELECT pg_attribute.attname
123
- FROM pg_class, pg_attribute, pg_index
124
- WHERE pg_class.oid = pg_attribute.attrelid AND
125
- pg_class.oid = pg_index.indrelid AND
126
- pg_index.indkey[0] = pg_attribute.attnum AND
127
- pg_index.indisprimary = 't' AND
128
- pg_class.relname = '%s'
129
- end_sql
130
-
131
- def pkey_and_sequence(table)
132
- r = async_query(SELECT_PK_AND_SERIAL_SEQUENCE % table)
133
- return [r[0].first, r[0].last] unless r.nil? || (r.respond_to?(:empty?) && r.empty?)
134
-
135
- r = async_query(SELECT_PK_AND_CUSTOM_SEQUENCE % table)
136
- return [r[0].first, r[0].last] unless r.nil? || (r.respond_to?(:empty?) && r.empty?)
137
- rescue
138
- nil
139
- end
140
-
141
- def primary_key(table)
142
- r = async_query(SELECT_PK % table)
143
- pkey = r[0].first unless r.nil? || (r.respond_to?(:empty?) && r.empty?)
144
- return pkey.to_sym if pkey
145
- rescue
146
- nil
147
- end
148
- end
149
-
150
- class String
151
- POSTGRES_BOOL_TRUE = 't'.freeze
152
- POSTGRES_BOOL_FALSE = 'f'.freeze
153
-
154
- def postgres_to_bool
155
- if self == POSTGRES_BOOL_TRUE
156
- true
157
- elsif self == POSTGRES_BOOL_FALSE
158
- false
159
- else
160
- nil
47
+ def execute(sql, &block)
48
+ q = nil
49
+ begin
50
+ q = exec(sql)
51
+ rescue PGError => e
52
+ unless connected?
53
+ reset
54
+ q = exec(sql)
55
+ else
56
+ raise e
57
+ end
58
+ end
59
+ begin
60
+ block ? block[q] : q.cmd_tuples
61
+ ensure
62
+ q.clear
63
+ end
64
+ end
65
+
66
+ attr_accessor :transaction_in_progress
67
+
68
+ SELECT_CURRVAL = "SELECT currval('%s')".freeze
69
+
70
+ def last_insert_id(table)
71
+ @table_sequences ||= {}
72
+ if !@table_sequences.include?(table)
73
+ pkey_and_seq = pkey_and_sequence(table)
74
+ if pkey_and_seq
75
+ @table_sequences[table] = pkey_and_seq[1]
76
+ end
77
+ end
78
+ if seq = @table_sequences[table]
79
+ execute(SELECT_CURRVAL % seq) do |r|
80
+ return r.getvalue(0,0).to_i unless r.nil? || (r.ntuples == 0)
81
+ end
82
+ end
83
+ nil # primary key sequence not found
84
+ end
85
+
86
+ # Shamelessly appropriated from ActiveRecord's Postgresql adapter.
87
+ SELECT_PK_AND_SERIAL_SEQUENCE = <<-end_sql
88
+ SELECT attr.attname, name.nspname, seq.relname
89
+ FROM pg_class seq, pg_attribute attr, pg_depend dep,
90
+ pg_namespace name, pg_constraint cons
91
+ WHERE seq.oid = dep.objid
92
+ AND seq.relnamespace = name.oid
93
+ AND seq.relkind = 'S'
94
+ AND attr.attrelid = dep.refobjid
95
+ AND attr.attnum = dep.refobjsubid
96
+ AND attr.attrelid = cons.conrelid
97
+ AND attr.attnum = cons.conkey[1]
98
+ AND cons.contype = 'p'
99
+ AND dep.refobjid = '%s'::regclass
100
+ end_sql
101
+
102
+ SELECT_PK_AND_CUSTOM_SEQUENCE = <<-end_sql
103
+ SELECT attr.attname,
104
+ CASE
105
+ WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
106
+ substr(split_part(def.adsrc, '''', 2),
107
+ strpos(split_part(def.adsrc, '''', 2), '.')+1)
108
+ ELSE split_part(def.adsrc, '''', 2)
109
+ END
110
+ FROM pg_class t
111
+ JOIN pg_namespace name ON (t.relnamespace = name.oid)
112
+ JOIN pg_attribute attr ON (t.oid = attrelid)
113
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
114
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
115
+ WHERE t.oid = '%s'::regclass
116
+ AND cons.contype = 'p'
117
+ AND def.adsrc ~* 'nextval'
118
+ end_sql
119
+
120
+ SELECT_PK = <<-end_sql
121
+ SELECT pg_attribute.attname
122
+ FROM pg_class, pg_attribute, pg_index
123
+ WHERE pg_class.oid = pg_attribute.attrelid AND
124
+ pg_class.oid = pg_index.indrelid AND
125
+ pg_index.indkey[0] = pg_attribute.attnum AND
126
+ pg_index.indisprimary = 't' AND
127
+ pg_class.relname = '%s'
128
+ end_sql
129
+
130
+ def pkey_and_sequence(table)
131
+ execute(SELECT_PK_AND_SERIAL_SEQUENCE % table) do |r|
132
+ return [r.getvalue(0,2), r.getvalue(0,2)] unless r.nil? || (r.ntuples == 0)
133
+ end
134
+
135
+ execute(SELECT_PK_AND_CUSTOM_SEQUENCE % table) do |r|
136
+ return [r.getvalue(0,0), r.getvalue(0,1)] unless r.nil? || (r.ntuples == 0)
137
+ end
138
+ end
139
+
140
+ def primary_key(table)
141
+ execute(SELECT_PK % table) do |r|
142
+ if (r.nil? || (r.ntuples == 0)) then
143
+ return nil
144
+ else
145
+ r.getvalue(0,0)
146
+ end
147
+ end
148
+ end
149
+
150
+ def self.string_to_bool(s)
151
+ if(s.blank?)
152
+ nil
153
+ elsif(s.downcase == 't' || s.downcase == 'true')
154
+ true
155
+ else
156
+ false
157
+ end
158
+ end
161
159
  end
162
- end
163
- end
164
160
 
165
- module Sequel
166
- module Postgres
167
161
  PG_TYPES = {
168
- 16 => :postgres_to_bool,
169
- 20 => :to_i,
170
- 21 => :to_i,
171
- 22 => :to_i,
172
- 23 => :to_i,
173
- 26 => :to_i,
174
- 700 => :to_f,
175
- 701 => :to_f,
176
- 790 => :to_f,
177
- 1082 => :to_date,
178
- 1083 => :to_time,
179
- 1114 => :to_time,
180
- 1184 => :to_time,
181
- 1186 => :to_i
162
+ 16 => lambda{ |s| Adapter.string_to_bool(s) },
163
+ 17 => lambda{ |s| Adapter.unescape_bytea(s) },
164
+ 20 => lambda{ |s| s.to_i },
165
+ 21 => lambda{ |s| s.to_i },
166
+ 22 => lambda{ |s| s.to_i },
167
+ 23 => lambda{ |s| s.to_i },
168
+ 26 => lambda{ |s| s.to_i },
169
+ 700 => lambda{ |s| s.to_f },
170
+ 701 => lambda{ |s| s.to_f },
171
+ 790 => lambda{ |s| s.to_f },
172
+ 1082 => lambda{ |s| s.to_date },
173
+ 1083 => lambda{ |s| s.to_time },
174
+ 1114 => lambda{ |s| s.to_time },
175
+ 1184 => lambda{ |s| s.to_time },
176
+ 1186 => lambda{ |s| s.to_i }
182
177
  }
183
178
 
184
- if PGconn.respond_to?(:translate_results=)
185
- PGconn.translate_results = true
186
- AUTO_TRANSLATE = true
187
- else
188
- AUTO_TRANSLATE = false
179
+ if Adapter.respond_to?(:translate_results=)
180
+ Adapter.translate_results = false
189
181
  end
182
+ AUTO_TRANSLATE = false
190
183
 
191
184
  class Database < Sequel::Database
192
185
  set_adapter_scheme :postgres
193
186
 
194
187
  def connect
195
- conn = PGconn.connect(
188
+ conn = Adapter.connect(
196
189
  @opts[:host] || 'localhost',
197
190
  @opts[:port] || 5432,
198
191
  '', '',
@@ -207,7 +200,7 @@ module Sequel
207
200
  end
208
201
 
209
202
  def disconnect
210
- @pool.disconnect {|c| c.close}
203
+ @pool.disconnect {|c| c.finish}
211
204
  end
212
205
 
213
206
  def dataset(opts = nil)
@@ -229,11 +222,13 @@ module Sequel
229
222
  end
230
223
 
231
224
  def execute(sql, &block)
232
- @logger.info(sql) if @logger
233
- @pool.hold {|conn| conn.execute(sql, &block)}
234
- rescue => e
235
- @logger.error(e.message) if @logger
236
- raise e
225
+ begin
226
+ log_info(sql)
227
+ @pool.hold {|conn| conn.execute(sql, &block)}
228
+ rescue => e
229
+ log_info(e.message)
230
+ raise convert_pgerror(e)
231
+ end
237
232
  end
238
233
 
239
234
  def primary_key_for_table(conn, table)
@@ -250,11 +245,7 @@ module Sequel
250
245
  rescue PGError => e
251
246
  # An error could occur if the inserted values include a primary key
252
247
  # value, while the primary key is serial.
253
- if e.message =~ RE_CURRVAL_ERROR
254
- raise Error, "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?"
255
- else
256
- raise e
257
- end
248
+ raise Error.new(e.message =~ RE_CURRVAL_ERROR ? "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?" : e.message)
258
249
  end
259
250
 
260
251
  case values
@@ -268,25 +259,34 @@ module Sequel
268
259
  end
269
260
 
270
261
  def server_version
271
- @server_version ||= pool.hold do |conn|
262
+ return @server_version if @server_version
263
+ @server_version = pool.hold do |conn|
272
264
  if conn.respond_to?(:server_version)
273
- pool.hold {|c| c.server_version}
274
- else
275
- get(:version[]) =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
276
- ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
265
+ begin
266
+ conn.server_version
267
+ rescue StandardError
268
+ nil
269
+ end
277
270
  end
278
271
  end
272
+ unless @server_version
273
+ m = /PostgreSQL (\d+)\.(\d+)\.(\d+)/.match(get(:version[]))
274
+ @server_version = (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
275
+ end
276
+ @server_version
279
277
  end
280
278
 
281
279
  def execute_insert(sql, table, values)
282
- @logger.info(sql) if @logger
283
- @pool.hold do |conn|
284
- conn.execute(sql)
285
- insert_result(conn, table, values)
280
+ begin
281
+ log_info(sql)
282
+ @pool.hold do |conn|
283
+ conn.execute(sql)
284
+ insert_result(conn, table, values)
285
+ end
286
+ rescue => e
287
+ log_info(e.message)
288
+ raise convert_pgerror(e)
286
289
  end
287
- rescue => e
288
- @logger.error(e.message) if @logger
289
- raise e
290
290
  end
291
291
 
292
292
  SQL_BEGIN = 'BEGIN'.freeze
@@ -298,24 +298,25 @@ module Sequel
298
298
  if conn.transaction_in_progress
299
299
  yield conn
300
300
  else
301
- @logger.info(SQL_BEGIN) if @logger
302
- conn.async_exec(SQL_BEGIN)
301
+ log_info(SQL_BEGIN)
302
+ conn.execute(SQL_BEGIN)
303
303
  begin
304
304
  conn.transaction_in_progress = true
305
- result = yield
306
- begin
307
- @logger.info(SQL_COMMIT) if @logger
308
- conn.async_exec(SQL_COMMIT)
309
- rescue => e
310
- @logger.error(e.message) if @logger
311
- raise e
312
- end
313
- result
314
- rescue => e
315
- @logger.info(SQL_ROLLBACK) if @logger
316
- conn.async_exec(SQL_ROLLBACK) rescue nil
317
- raise e unless Error::Rollback === e
305
+ yield
306
+ rescue ::Exception => e
307
+ log_info(SQL_ROLLBACK)
308
+ conn.execute(SQL_ROLLBACK) rescue nil
309
+ raise convert_pgerror(e) unless Error::Rollback === e
318
310
  ensure
311
+ unless e
312
+ begin
313
+ log_info(SQL_COMMIT)
314
+ conn.execute(SQL_COMMIT)
315
+ rescue => e
316
+ log_info(e.message)
317
+ raise convert_pgerror(e)
318
+ end
319
+ end
319
320
  conn.transaction_in_progress = nil
320
321
  end
321
322
  end
@@ -328,11 +329,11 @@ module Sequel
328
329
 
329
330
  def index_definition_sql(table_name, index)
330
331
  index_name = index[:name] || default_index_name(table_name, index[:columns])
331
- expr = "(#{literal(index[:columns])})"
332
+ expr = literal(Array(index[:columns]))
332
333
  unique = "UNIQUE " if index[:unique]
333
334
  index_type = index[:type]
334
335
  filter = index[:where] || index[:filter]
335
- filter = " WHERE #{expression_list(filter)}" if filter
336
+ filter = " WHERE #{filter_expr(filter)}" if filter
336
337
  case index_type
337
338
  when :full_text
338
339
  lang = index[:language] ? "#{literal(index[:language])}, " : ""
@@ -348,22 +349,41 @@ module Sequel
348
349
  def drop_table_sql(name)
349
350
  "DROP TABLE #{name} CASCADE"
350
351
  end
352
+
353
+ private
354
+ # If the given exception is a PGError, return a Sequel::Error with the same message, otherwise
355
+ # just return the given exception
356
+ def convert_pgerror(e)
357
+ PGError === e ? Error.new(e.message) : e
358
+ end
359
+
360
+ # PostgreSQL currently can always reuse connections. It doesn't need the pool to convert exceptions, either.
361
+ def connection_pool_default_options
362
+ super.merge(:pool_reuse_connections=>:always, :pool_convert_exceptions=>false)
363
+ end
364
+
365
+ def schema_ds_filter(table_name, opts)
366
+ filt = super
367
+ # Restrict it to the given or public schema, unless specifically requesting :schema = nil
368
+ filt = SQL::ComplexExpression.new(:AND, filt, {:c__table_schema=>opts[:schema] || 'public'}) if opts[:schema] || !opts.include?(:schema)
369
+ filt
370
+ end
351
371
  end
352
372
 
353
373
  class Dataset < Sequel::Dataset
354
374
 
355
375
  PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
356
376
 
357
- def quote_column_ref(c); "\"#{c}\""; end
358
-
359
377
  def literal(v)
360
378
  case v
361
379
  when LiteralString
362
380
  v
363
- when String, Fixnum, Float, TrueClass, FalseClass
364
- PGconn.quote(v)
381
+ when String, TrueClass, FalseClass
382
+ Adapter.quote(v)
365
383
  when Time
366
384
  "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
385
+ when DateTime
386
+ "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
367
387
  else
368
388
  super
369
389
  end
@@ -457,9 +477,9 @@ module Sequel
457
477
  def multi_insert_sql(columns, values)
458
478
  return super if @db.server_version < 80200
459
479
 
460
- # postgresql 8.2 introduces support for insert
461
- columns = literal(columns)
462
- values = values.map {|r| "(#{literal(r)})"}.join(COMMA_SEPARATOR)
480
+ # postgresql 8.2 introduces support for multi-row insert
481
+ columns = column_list(columns)
482
+ values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
463
483
  ["INSERT INTO #{source_list(@opts[:from])} (#{columns}) VALUES #{values}"]
464
484
  end
465
485
 
@@ -475,47 +495,26 @@ module Sequel
475
495
  def delete(opts = nil)
476
496
  @db.execute(delete_sql(opts))
477
497
  end
478
-
479
- def fetch_rows(sql, &block)
480
- @db.execute(sql) do |q|
481
- conv = row_converter(q)
482
- q.each {|r| yield conv[r]}
483
- end
484
- end
485
-
486
- @@converters_mutex = Mutex.new
487
- @@converters = {}
488
498
 
489
- def row_converter(result)
490
- @columns = []; translators = []
491
- result.fields.each_with_index do |f, idx|
492
- @columns << f.to_sym
493
- translators << PG_TYPES[result.type(idx)]
494
- end
495
-
496
- # create result signature and memoize the converter
497
- sig = [@columns, translators].hash
498
- @@converters_mutex.synchronize do
499
- @@converters[sig] ||= compile_converter(@columns, translators)
500
- end
501
- end
502
-
503
- def compile_converter(columns, translators)
504
- used_columns = []
505
- kvs = []
506
- columns.each_with_index do |column, idx|
507
- next if used_columns.include?(column)
508
- used_columns << column
509
-
510
- if !AUTO_TRANSLATE and translator = translators[idx]
511
- kvs << ":\"#{column}\" => ((t = r[#{idx}]) ? t.#{translator} : nil)"
512
- else
513
- kvs << ":\"#{column}\" => r[#{idx}]"
499
+ def fetch_rows(sql, &block)
500
+ @columns = []
501
+ @db.execute(sql) do |res|
502
+ (0...res.ntuples).each do |recnum|
503
+ converted_rec = {}
504
+ (0...res.nfields).each do |fieldnum|
505
+ fieldsym = res.fname(fieldnum).to_sym
506
+ @columns << fieldsym
507
+ converted_rec[fieldsym] = if value = res.getvalue(recnum,fieldnum)
508
+ (PG_TYPES[res.ftype(fieldnum)] || lambda{|s| s.to_s}).call(value)
509
+ else
510
+ value
511
+ end
512
+ end
513
+ yield converted_rec
514
514
  end
515
515
  end
516
- eval("lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}")
517
516
  end
518
-
519
517
  end
520
518
  end
521
519
  end
520
+