sequel_core 1.5.1 → 2.0.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 (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
+