sequel 0.5.0.2 → 1.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 (66) hide show
  1. data/COPYING +18 -18
  2. data/Rakefile +17 -98
  3. data/lib/sequel.rb +2 -71
  4. metadata +10 -108
  5. data/CHANGELOG +0 -989
  6. data/bin/sequel +0 -41
  7. data/lib/sequel/adapters/adapter_skeleton.rb +0 -68
  8. data/lib/sequel/adapters/ado.rb +0 -100
  9. data/lib/sequel/adapters/db2.rb +0 -158
  10. data/lib/sequel/adapters/dbi.rb +0 -126
  11. data/lib/sequel/adapters/informix.rb +0 -87
  12. data/lib/sequel/adapters/jdbc.rb +0 -108
  13. data/lib/sequel/adapters/mysql.rb +0 -269
  14. data/lib/sequel/adapters/odbc.rb +0 -145
  15. data/lib/sequel/adapters/odbc_mssql.rb +0 -93
  16. data/lib/sequel/adapters/openbase.rb +0 -90
  17. data/lib/sequel/adapters/oracle.rb +0 -99
  18. data/lib/sequel/adapters/postgres.rb +0 -519
  19. data/lib/sequel/adapters/sqlite.rb +0 -192
  20. data/lib/sequel/ado.rb +0 -6
  21. data/lib/sequel/array_keys.rb +0 -296
  22. data/lib/sequel/connection_pool.rb +0 -152
  23. data/lib/sequel/core_ext.rb +0 -59
  24. data/lib/sequel/core_sql.rb +0 -191
  25. data/lib/sequel/database.rb +0 -433
  26. data/lib/sequel/dataset.rb +0 -409
  27. data/lib/sequel/dataset/convenience.rb +0 -321
  28. data/lib/sequel/dataset/sequelizer.rb +0 -354
  29. data/lib/sequel/dataset/sql.rb +0 -586
  30. data/lib/sequel/db2.rb +0 -6
  31. data/lib/sequel/dbi.rb +0 -6
  32. data/lib/sequel/exceptions.rb +0 -45
  33. data/lib/sequel/informix.rb +0 -6
  34. data/lib/sequel/migration.rb +0 -191
  35. data/lib/sequel/model.rb +0 -8
  36. data/lib/sequel/mysql.rb +0 -6
  37. data/lib/sequel/odbc.rb +0 -6
  38. data/lib/sequel/oracle.rb +0 -6
  39. data/lib/sequel/postgres.rb +0 -6
  40. data/lib/sequel/pretty_table.rb +0 -73
  41. data/lib/sequel/schema.rb +0 -8
  42. data/lib/sequel/schema/schema_generator.rb +0 -131
  43. data/lib/sequel/schema/schema_sql.rb +0 -131
  44. data/lib/sequel/sqlite.rb +0 -6
  45. data/lib/sequel/worker.rb +0 -58
  46. data/spec/adapters/informix_spec.rb +0 -139
  47. data/spec/adapters/mysql_spec.rb +0 -330
  48. data/spec/adapters/oracle_spec.rb +0 -130
  49. data/spec/adapters/postgres_spec.rb +0 -189
  50. data/spec/adapters/sqlite_spec.rb +0 -345
  51. data/spec/array_keys_spec.rb +0 -679
  52. data/spec/connection_pool_spec.rb +0 -356
  53. data/spec/core_ext_spec.rb +0 -67
  54. data/spec/core_sql_spec.rb +0 -301
  55. data/spec/database_spec.rb +0 -811
  56. data/spec/dataset_spec.rb +0 -2381
  57. data/spec/migration_spec.rb +0 -261
  58. data/spec/pretty_table_spec.rb +0 -66
  59. data/spec/rcov.opts +0 -4
  60. data/spec/schema_generator_spec.rb +0 -86
  61. data/spec/schema_spec.rb +0 -230
  62. data/spec/sequel_spec.rb +0 -10
  63. data/spec/sequelizer_spec.rb +0 -389
  64. data/spec/spec.opts +0 -5
  65. data/spec/spec_helper.rb +0 -44
  66. data/spec/worker_spec.rb +0 -96
@@ -1,145 +0,0 @@
1
- require 'odbc'
2
-
3
- module Sequel
4
- module ODBC
5
- class Database < Sequel::Database
6
- set_adapter_scheme :odbc
7
-
8
- def connect
9
- conn = ::ODBC::connect(@opts[:database], @opts[:user], @opts[:password])
10
- conn.autocommit = true
11
- conn
12
- end
13
-
14
- def disconnect
15
- @pool.disconnect {|c| c.disconnect}
16
- end
17
-
18
- def dataset(opts = nil)
19
- ODBC::Dataset.new(self, opts)
20
- end
21
-
22
- def execute(sql)
23
- @logger.info(sql) if @logger
24
- @pool.hold do |conn|
25
- conn.run(sql)
26
- end
27
- end
28
-
29
- def do(sql)
30
- @logger.info(sql) if @logger
31
- @pool.hold do |conn|
32
- conn.do(sql)
33
- end
34
- end
35
- end
36
-
37
- class Dataset < Sequel::Dataset
38
- BOOL_TRUE = '1'.freeze
39
- BOOL_FALSE = '0'.freeze
40
-
41
- def literal(v)
42
- case v
43
- when true
44
- BOOL_TRUE
45
- when false
46
- BOOL_FALSE
47
- else
48
- super
49
- end
50
- end
51
-
52
- def fetch_rows(sql, &block)
53
- @db.synchronize do
54
- s = @db.execute sql
55
- begin
56
- @columns = s.columns(true).map {|c| c.name.to_sym}
57
- rows = s.fetch_all
58
- rows.each {|row| yield hash_row(row)}
59
- ensure
60
- s.drop unless s.nil? rescue nil
61
- end
62
- end
63
- self
64
- end
65
-
66
- def hash_row(row)
67
- hash = {}
68
- row.each_with_index do |v, idx|
69
- hash[@columns[idx]] = convert_odbc_value(v)
70
- end
71
- hash
72
- end
73
-
74
- def convert_odbc_value(v)
75
- # When fetching a result set, the Ruby ODBC driver converts all ODBC
76
- # SQL types to an equivalent Ruby type; with the exception of
77
- # SQL_TYPE_DATE, SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP.
78
- #
79
- # The conversions below are consistent with the mappings in
80
- # ODBCColumn#mapSqlTypeToGenericType and Column#klass.
81
- case v
82
- when ::ODBC::TimeStamp
83
- DateTime.new(v.year, v.month, v.day, v.hour, v.minute, v.second)
84
- when ::ODBC::Time
85
- DateTime.now
86
- Time.gm(now.year, now.month, now.day, v.hour, v.minute, v.second)
87
- when ::ODBC::Date
88
- Date.new(v.year, v.month, v.day)
89
- else
90
- v
91
- end
92
- end
93
-
94
- def array_tuples_fetch_rows(sql, &block)
95
- @db.synchronize do
96
- s = @db.execute sql
97
- begin
98
- @columns = s.columns(true).map {|c| c.name.to_sym}
99
- rows = s.fetch_all
100
- rows.each {|r| yield array_tuples_make_row(r)}
101
- ensure
102
- s.drop unless s.nil? rescue nil
103
- end
104
- end
105
- self
106
- end
107
-
108
- def array_tuples_make_row(row)
109
- row.keys = @columns
110
- row.each_with_index do |v, idx|
111
- # When fetching a result set, the Ruby ODBC driver converts all ODBC
112
- # SQL types to an equivalent Ruby type; with the exception of
113
- # SQL_TYPE_DATE, SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP.
114
- #
115
- # The conversions below are consistent with the mappings in
116
- # ODBCColumn#mapSqlTypeToGenericType and Column#klass.
117
- case v
118
- when ::ODBC::TimeStamp
119
- row[idx] = DateTime.new(v.year, v.month, v.day, v.hour, v.minute, v.second)
120
- when ::ODBC::Time
121
- now = DateTime.now
122
- row[idx] = Time.gm(now.year, now.month, now.day, v.hour, v.minute, v.second)
123
- when ::ODBC::Date
124
- row[idx] = Date.new(v.year, v.month, v.day)
125
- end
126
- end
127
- row
128
- end
129
-
130
-
131
- def insert(*values)
132
- @db.do insert_sql(*values)
133
- end
134
-
135
- def update(*args, &block)
136
- @db.do update_sql(*args, &block)
137
- self
138
- end
139
-
140
- def delete(opts = nil)
141
- @db.do delete_sql(opts)
142
- end
143
- end
144
- end
145
- end
@@ -1,93 +0,0 @@
1
- if !Sequel.const_defined?('ODBC')
2
- require File.join(File.dirname(__FILE__), 'odbc')
3
- end
4
-
5
- module Sequel
6
- module ODBC
7
- module MSSQL
8
- class Database < ODBC::Database
9
- set_adapter_scheme :odbc_mssql
10
-
11
- def dataset(opts = nil)
12
- MSSQL::Dataset.new(self, opts)
13
- end
14
- end
15
-
16
- class Dataset < ODBC::Dataset
17
- # Allows you to do .nolock on a query
18
- def nolock
19
- clone_merge(:with => "(NOLOCK)")
20
- end
21
-
22
- # Formats a SELECT statement using the given options and the dataset
23
- # options.
24
- def select_sql(opts = nil)
25
- opts = opts ? @opts.merge(opts) : @opts
26
-
27
- if sql = opts[:sql]
28
- return sql
29
- end
30
-
31
- # ADD TOP to SELECT string for LIMITS
32
- if limit = opts[:limit]
33
- top = "TOP #{limit} "
34
- raise Error, "Offset not supported" if opts[:offset]
35
- end
36
-
37
- columns = opts[:select]
38
- select_columns = columns ? column_list(columns) : WILDCARD
39
-
40
- if distinct = opts[:distinct]
41
- distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{column_list(distinct)})"
42
- sql = "SELECT #{top}#{distinct_clause} #{select_columns}"
43
- else
44
- sql = "SELECT #{top}#{select_columns}"
45
- end
46
-
47
- if opts[:from]
48
- sql << " FROM #{source_list(opts[:from])}"
49
- end
50
-
51
- # ADD WITH to SELECT string for NOLOCK
52
- if with = opts[:with]
53
- sql << " WITH #{with}"
54
- end
55
-
56
- if join = opts[:join]
57
- sql << join
58
- end
59
-
60
- if where = opts[:where]
61
- sql << " WHERE #{where}"
62
- end
63
-
64
- if group = opts[:group]
65
- sql << " GROUP BY #{column_list(group)}"
66
- end
67
-
68
- if order = opts[:order]
69
- sql << " ORDER BY #{column_list(order)}"
70
- end
71
-
72
- if having = opts[:having]
73
- sql << " HAVING #{having}"
74
- end
75
-
76
- if union = opts[:union]
77
- sql << (opts[:union_all] ? \
78
- " UNION ALL #{union.sql}" : " UNION #{union.sql}")
79
- elsif intersect = opts[:intersect]
80
- sql << (opts[:intersect_all] ? \
81
- " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
82
- elsif except = opts[:except]
83
- sql << (opts[:except_all] ? \
84
- " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
85
- end
86
-
87
- sql
88
- end
89
- alias_method :sql, :select_sql
90
- end
91
- end
92
- end
93
- end
@@ -1,90 +0,0 @@
1
- require 'openbase'
2
-
3
- module Sequel
4
- module OpenBase
5
- class Database < Sequel::Database
6
- set_adapter_scheme :openbase
7
-
8
- def connect
9
- OpenBase.new(
10
- opts[:database],
11
- opts[:host] || 'localhost',
12
- opts[:user],
13
- opts[:password]
14
- )
15
- end
16
-
17
- def disconnect
18
- # would this work?
19
- @pool.disconnect {|c| c.disconnect}
20
- end
21
-
22
- def dataset(opts = nil)
23
- OpenBase::Dataset.new(self, opts)
24
- end
25
-
26
- def execute(sql)
27
- @logger.info(sql) if @logger
28
- @pool.hold {|conn| conn.execute(sql)}
29
- end
30
-
31
- alias_method :do, :execute
32
- end
33
-
34
- class Dataset < Sequel::Dataset
35
- def literal(v)
36
- case v
37
- when Time
38
- literal(v.iso8601)
39
- else
40
- super
41
- end
42
- end
43
-
44
- def fetch_rows(sql, &block)
45
- @db.synchronize do
46
- result = @db.execute sql
47
- begin
48
- @columns = result.column_infos.map {|c| c.name.to_sym}
49
- result.each do |r|
50
- row = {}
51
- r.each_with_index {|v, i| row[@columns[i]] = v}
52
- yield row
53
- end
54
- ensure
55
- # result.close
56
- end
57
- end
58
- self
59
- end
60
-
61
- def array_tuples_fetch_rows(sql, &block)
62
- @db.synchronize do
63
- result = @db.execute sql
64
- begin
65
- @columns = result.column_infos.map {|c| c.name.to_sym}
66
- result.each do |r|
67
- r.keys = @columns
68
- yield r
69
- end
70
- ensure
71
- # cursor.close
72
- end
73
- end
74
- self
75
- end
76
-
77
- def insert(*values)
78
- @db.do insert_sql(*values)
79
- end
80
-
81
- def update(*args, &block)
82
- @db.do update_sql(*args, &block)
83
- end
84
-
85
- def delete(opts = nil)
86
- @db.do delete_sql(opts)
87
- end
88
- end
89
- end
90
- end
@@ -1,99 +0,0 @@
1
- require 'oci8'
2
-
3
- module Sequel
4
- module Oracle
5
- class Database < Sequel::Database
6
- set_adapter_scheme :oracle
7
-
8
- # AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
9
- #
10
- # def auto_increment_sql
11
- # AUTO_INCREMENT
12
- # end
13
-
14
- def connect
15
- if @opts[:database]
16
- dbname = @opts[:host] ? \
17
- "//#{@opts[:host]}/#{@opts[:database]}" : @opts[:database]
18
- else
19
- dbname = @opts[:host]
20
- end
21
- conn = OCI8.new(@opts[:user], @opts[:password], dbname, @opts[:privilege])
22
- conn.autocommit = true
23
- conn.non_blocking = true
24
- conn
25
- end
26
-
27
- def disconnect
28
- @pool.disconnect {|c| c.logoff}
29
- end
30
-
31
- def dataset(opts = nil)
32
- Oracle::Dataset.new(self, opts)
33
- end
34
-
35
- def execute(sql)
36
- @logger.info(sql) if @logger
37
- @pool.hold {|conn| conn.exec(sql)}
38
- end
39
-
40
- alias_method :do, :execute
41
- end
42
-
43
- class Dataset < Sequel::Dataset
44
- def literal(v)
45
- case v
46
- when Time
47
- literal(v.iso8601)
48
- else
49
- super
50
- end
51
- end
52
-
53
- def fetch_rows(sql, &block)
54
- @db.synchronize do
55
- cursor = @db.execute sql
56
- begin
57
- @columns = cursor.get_col_names.map {|c| c.to_sym}
58
- while r = cursor.fetch
59
- row = {}
60
- r.each_with_index {|v, i| row[@columns[i]] = v}
61
- yield row
62
- end
63
- ensure
64
- cursor.close
65
- end
66
- end
67
- self
68
- end
69
-
70
- def array_tuples_fetch_rows(sql, &block)
71
- @db.synchronize do
72
- cursor = @db.execute sql
73
- begin
74
- @columns = cursor.get_col_names.map {|c| c.to_sym}
75
- while r = cursor.fetch
76
- r.keys = columns
77
- yield r
78
- end
79
- ensure
80
- cursor.close
81
- end
82
- end
83
- self
84
- end
85
-
86
- def insert(*values)
87
- @db.do insert_sql(*values)
88
- end
89
-
90
- def update(*args, &block)
91
- @db.do update_sql(*args, &block)
92
- end
93
-
94
- def delete(opts = nil)
95
- @db.do delete_sql(opts)
96
- end
97
- end
98
- end
99
- end
@@ -1,519 +0,0 @@
1
- require 'postgres'
2
-
3
- class PGconn
4
- # the pure-ruby postgres adapter does not have a quote method.
5
- TRUE = 't'.freeze
6
- FALSE = 'f'.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
23
- 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
41
-
42
- def connected?
43
- status == PGconn::CONNECTION_OK
44
- end
45
-
46
- def execute(sql)
47
- begin
48
- async_exec(sql)
49
- rescue PGError => e
50
- unless connected?
51
- reset
52
- async_exec(sql)
53
- else
54
- raise e
55
- end
56
- end
57
- end
58
-
59
- attr_accessor :transaction_in_progress
60
-
61
- SELECT_CURRVAL = "SELECT currval('%s')".freeze
62
-
63
- def last_insert_id(table)
64
- @table_sequences ||= {}
65
- if !@table_sequences.include?(table)
66
- pkey_and_seq = pkey_and_sequence(table)
67
- if pkey_and_seq
68
- @table_sequences[table] = pkey_and_seq[1]
69
- end
70
- end
71
- if seq = @table_sequences[table]
72
- r = async_query(SELECT_CURRVAL % seq)
73
- return r[0][0].to_i unless r.nil? || r.empty?
74
- end
75
- nil # primary key sequence not found
76
- end
77
-
78
- # Shamelessly appropriated from ActiveRecord's Postgresql adapter.
79
-
80
- SELECT_PK_AND_SERIAL_SEQUENCE = <<-end_sql
81
- SELECT attr.attname, name.nspname, seq.relname
82
- FROM pg_class seq, pg_attribute attr, pg_depend dep,
83
- pg_namespace name, pg_constraint cons
84
- WHERE seq.oid = dep.objid
85
- AND seq.relnamespace = name.oid
86
- AND seq.relkind = 'S'
87
- AND attr.attrelid = dep.refobjid
88
- AND attr.attnum = dep.refobjsubid
89
- AND attr.attrelid = cons.conrelid
90
- AND attr.attnum = cons.conkey[1]
91
- AND cons.contype = 'p'
92
- AND dep.refobjid = '%s'::regclass
93
- end_sql
94
-
95
- SELECT_PK_AND_CUSTOM_SEQUENCE = <<-end_sql
96
- SELECT attr.attname, name.nspname, split_part(def.adsrc, '''', 2)
97
- FROM pg_class t
98
- JOIN pg_namespace name ON (t.relnamespace = name.oid)
99
- JOIN pg_attribute attr ON (t.oid = attrelid)
100
- JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
101
- JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
102
- WHERE t.oid = '%s'::regclass
103
- AND cons.contype = 'p'
104
- AND def.adsrc ~* 'nextval'
105
- end_sql
106
-
107
- SELECT_PK = <<-end_sql
108
- SELECT pg_attribute.attname
109
- FROM pg_class, pg_attribute, pg_index
110
- WHERE pg_class.oid = pg_attribute.attrelid AND
111
- pg_class.oid = pg_index.indrelid AND
112
- pg_index.indkey[0] = pg_attribute.attnum AND
113
- pg_index.indisprimary = 't' AND
114
- pg_class.relname = '%s'
115
- end_sql
116
-
117
- def pkey_and_sequence(table)
118
- r = async_query(SELECT_PK_AND_SERIAL_SEQUENCE % table)
119
- return [r[0].first, r[0].last] unless r.nil? or r.empty?
120
-
121
- r = async_query(SELECT_PK_AND_CUSTOM_SEQUENCE % table)
122
- return [r[0].first, r[0].last] unless r.nil? or r.empty?
123
- rescue
124
- nil
125
- end
126
-
127
- def primary_key(table)
128
- r = async_query(SELECT_PK % table)
129
- pkey = r[0].first unless r.nil? or r.empty?
130
- return pkey.to_sym if pkey
131
- rescue
132
- nil
133
- end
134
- end
135
-
136
- class String
137
- POSTGRES_BOOL_TRUE = 't'.freeze
138
- POSTGRES_BOOL_FALSE = 'f'.freeze
139
-
140
- def postgres_to_bool
141
- if self == POSTGRES_BOOL_TRUE
142
- true
143
- elsif self == POSTGRES_BOOL_FALSE
144
- false
145
- else
146
- nil
147
- end
148
- end
149
- end
150
-
151
- module Sequel
152
- module Postgres
153
- PG_TYPES = {
154
- 16 => :postgres_to_bool,
155
- 20 => :to_i,
156
- 21 => :to_i,
157
- 22 => :to_i,
158
- 23 => :to_i,
159
- 700 => :to_f,
160
- 701 => :to_f,
161
- 1114 => :to_time
162
- }
163
-
164
- if PGconn.respond_to?(:translate_results=)
165
- PGconn.translate_results = true
166
- AUTO_TRANSLATE = true
167
- else
168
- AUTO_TRANSLATE = false
169
- end
170
-
171
- class Database < Sequel::Database
172
- set_adapter_scheme :postgres
173
-
174
- def connect
175
- conn = PGconn.connect(
176
- @opts[:host] || 'localhost',
177
- @opts[:port] || 5432,
178
- '', '',
179
- @opts[:database],
180
- @opts[:user],
181
- @opts[:password]
182
- )
183
- if encoding = @opts[:encoding] || @opts[:charset]
184
- conn.set_client_encoding(encoding)
185
- end
186
- conn
187
- end
188
-
189
- def disconnect
190
- @pool.disconnect {|c| c.close}
191
- end
192
-
193
- def dataset(opts = nil)
194
- Postgres::Dataset.new(self, opts)
195
- end
196
-
197
- RELATION_QUERY = {:from => [:pg_class], :select => [:relname]}.freeze
198
- RELATION_FILTER = "(relkind = 'r') AND (relname !~ '^pg|sql')".freeze
199
- SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
200
-
201
- def tables
202
- dataset(RELATION_QUERY).filter(RELATION_FILTER).map {|r| r[:relname].to_sym}
203
- end
204
-
205
- def locks
206
- dataset.from("pg_class, pg_locks").
207
- select("pg_class.relname, pg_locks.*").
208
- filter("pg_class.relfilenode=pg_locks.relation")
209
- end
210
-
211
- def execute(sql)
212
- @logger.info(sql) if @logger
213
- @pool.hold {|conn| conn.execute(sql)}
214
- rescue => e
215
- @logger.error(e.message) if @logger
216
- raise e
217
- end
218
-
219
- def execute_and_forget(sql)
220
- @logger.info(sql) if @logger
221
- @pool.hold {|conn| conn.execute(sql).clear}
222
- rescue => e
223
- @logger.error(e.message) if @logger
224
- raise e
225
- end
226
-
227
- def primary_key_for_table(conn, table)
228
- @primary_keys ||= {}
229
- @primary_keys[table] ||= conn.primary_key(table)
230
- end
231
-
232
- RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session/.freeze
233
-
234
- def insert_result(conn, table, values)
235
- begin
236
- result = conn.last_insert_id(table)
237
- return result if result
238
- rescue PGError => e
239
- # An error could occur if the inserted values include a primary key
240
- # value, while the primary key is serial.
241
- if e.message =~ RE_CURRVAL_ERROR
242
- raise Error, "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?"
243
- else
244
- raise e
245
- end
246
- end
247
-
248
- case values
249
- when Hash
250
- values[primary_key_for_table(conn, table)]
251
- when Array
252
- values.first
253
- else
254
- nil
255
- end
256
- end
257
-
258
- def execute_insert(sql, table, values)
259
- @logger.info(sql) if @logger
260
- @pool.hold do |conn|
261
- conn.execute(sql).clear
262
- insert_result(conn, table, values)
263
- end
264
- rescue => e
265
- @logger.error(e.message) if @logger
266
- raise e
267
- end
268
-
269
- def synchronize(&block)
270
- @pool.hold(&block)
271
- end
272
-
273
- SQL_BEGIN = 'BEGIN'.freeze
274
- SQL_COMMIT = 'COMMIT'.freeze
275
- SQL_ROLLBACK = 'ROLLBACK'.freeze
276
-
277
- def transaction
278
- @pool.hold do |conn|
279
- if conn.transaction_in_progress
280
- yield conn
281
- else
282
- @logger.info(SQL_BEGIN) if @logger
283
- conn.async_exec(SQL_BEGIN)
284
- begin
285
- conn.transaction_in_progress = true
286
- result = yield
287
- begin
288
- @logger.info(SQL_COMMIT) if @logger
289
- conn.async_exec(SQL_COMMIT)
290
- rescue => e
291
- @logger.error(e.message) if @logger
292
- raise e
293
- end
294
- result
295
- rescue => e
296
- @logger.info(SQL_ROLLBACK) if @logger
297
- conn.async_exec(SQL_ROLLBACK) rescue nil
298
- raise e unless Error::Rollback === e
299
- ensure
300
- conn.transaction_in_progress = nil
301
- end
302
- end
303
- end
304
- end
305
-
306
- def serial_primary_key_options
307
- {:primary_key => true, :type => :serial}
308
- end
309
-
310
- def drop_table_sql(name)
311
- "DROP TABLE #{name} CASCADE"
312
- end
313
- end
314
-
315
- class Dataset < Sequel::Dataset
316
- def literal(v)
317
- case v
318
- when LiteralString
319
- v
320
- when String, Fixnum, Float, TrueClass, FalseClass
321
- PGconn.quote(v)
322
- else
323
- super
324
- end
325
- end
326
-
327
- def match_expr(l, r)
328
- case r
329
- when Regexp
330
- r.casefold? ? \
331
- "(#{literal(l)} ~* #{literal(r.source)})" :
332
- "(#{literal(l)} ~ #{literal(r.source)})"
333
- else
334
- super
335
- end
336
- end
337
-
338
- FOR_UPDATE = ' FOR UPDATE'.freeze
339
- FOR_SHARE = ' FOR SHARE'.freeze
340
-
341
- def select_sql(opts = nil)
342
- row_lock_mode = opts ? opts[:lock] : @opts[:lock]
343
- sql = super
344
- case row_lock_mode
345
- when :update
346
- sql << FOR_UPDATE
347
- when :share
348
- sql << FOR_SHARE
349
- end
350
- sql
351
- end
352
-
353
- def for_update
354
- clone_merge(:lock => :update)
355
- end
356
-
357
- def for_share
358
- clone_merge(:lock => :share)
359
- end
360
-
361
- EXPLAIN = 'EXPLAIN '.freeze
362
- EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
363
- QUERY_PLAN = 'QUERY PLAN'.to_sym
364
-
365
- def explain(opts = nil)
366
- analysis = []
367
- fetch_rows(EXPLAIN + select_sql(opts)) do |r|
368
- analysis << r[QUERY_PLAN]
369
- end
370
- analysis.join("\r\n")
371
- end
372
-
373
- def analyze(opts = nil)
374
- analysis = []
375
- fetch_rows(EXPLAIN_ANALYZE + select_sql(opts)) do |r|
376
- analysis << r[QUERY_PLAN]
377
- end
378
- analysis.join("\r\n")
379
- end
380
-
381
- LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
382
-
383
- ACCESS_SHARE = 'ACCESS SHARE'.freeze
384
- ROW_SHARE = 'ROW SHARE'.freeze
385
- ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
386
- SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
387
- SHARE = 'SHARE'.freeze
388
- SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
389
- EXCLUSIVE = 'EXCLUSIVE'.freeze
390
- ACCESS_EXCLUSIVE = 'ACCESS EXCLUSIVE'.freeze
391
-
392
- # Locks the table with the specified mode.
393
- def lock(mode, &block)
394
- sql = LOCK % [@opts[:from], mode]
395
- @db.synchronize do
396
- if block # perform locking inside a transaction and yield to block
397
- @db.transaction {@db.execute_and_forget(sql); yield}
398
- else
399
- @db.execute_and_forget(sql) # lock without a transaction
400
- self
401
- end
402
- end
403
- end
404
-
405
- def insert(*values)
406
- @db.execute_insert(insert_sql(*values), @opts[:from],
407
- values.size == 1 ? values.first : values)
408
- end
409
-
410
- def update(*args, &block)
411
- @db.synchronize do
412
- result = @db.execute(update_sql(*args, &block))
413
- begin
414
- affected = result.cmdtuples
415
- ensure
416
- result.clear
417
- end
418
- affected
419
- end
420
- end
421
-
422
- def delete(opts = nil)
423
- @db.synchronize do
424
- result = @db.execute(delete_sql(opts))
425
- begin
426
- affected = result.cmdtuples
427
- ensure
428
- result.clear
429
- end
430
- affected
431
- end
432
- end
433
-
434
- def fetch_rows(sql, &block)
435
- @db.synchronize do
436
- result = @db.execute(sql)
437
- begin
438
- conv = row_converter(result)
439
- result.each {|r| yield conv[r]}
440
- ensure
441
- result.clear
442
- end
443
- end
444
- end
445
-
446
- @@converters_mutex = Mutex.new
447
- @@converters = {}
448
-
449
- def row_converter(result)
450
- @columns = []; translators = []
451
- result.fields.each_with_index do |f, idx|
452
- @columns << f.to_sym
453
- translators << PG_TYPES[result.type(idx)]
454
- end
455
-
456
- # create result signature and memoize the converter
457
- sig = [@columns, translators].hash
458
- @@converters_mutex.synchronize do
459
- @@converters[sig] ||= compile_converter(@columns, translators)
460
- end
461
- end
462
-
463
- def compile_converter(columns, translators)
464
- used_columns = []
465
- kvs = []
466
- columns.each_with_index do |column, idx|
467
- next if used_columns.include?(column)
468
- used_columns << column
469
-
470
- if !AUTO_TRANSLATE and translator = translators[idx]
471
- kvs << ":\"#{column}\" => ((t = r[#{idx}]) ? t.#{translator} : nil)"
472
- else
473
- kvs << ":\"#{column}\" => r[#{idx}]"
474
- end
475
- end
476
- eval("lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}")
477
- end
478
-
479
- def array_tuples_fetch_rows(sql, &block)
480
- @db.synchronize do
481
- result = @db.execute(sql)
482
- begin
483
- conv = array_tuples_row_converter(result)
484
- result.each {|r| yield conv[r]}
485
- ensure
486
- result.clear
487
- end
488
- end
489
- end
490
-
491
- @@array_tuples_converters_mutex = Mutex.new
492
- @@array_tuples_converters = {}
493
-
494
- def array_tuples_row_converter(result)
495
- @columns = []; translators = []
496
- result.fields.each_with_index do |f, idx|
497
- @columns << f.to_sym
498
- translators << PG_TYPES[result.type(idx)]
499
- end
500
-
501
- # create result signature and memoize the converter
502
- sig = [@columns, translators].hash
503
- @@array_tuples_converters_mutex.synchronize do
504
- @@array_tuples_converters[sig] ||= array_tuples_compile_converter(@columns, translators)
505
- end
506
- end
507
-
508
- def array_tuples_compile_converter(columns, translators)
509
- tr = []
510
- columns.each_with_index do |column, idx|
511
- if !AUTO_TRANSLATE and t = translators[idx]
512
- tr << "if (v = r[#{idx}]); r[#{idx}] = v.#{t}; end"
513
- end
514
- end
515
- eval("lambda {|r| r.keys = columns; #{tr.join(';')}; r}")
516
- end
517
- end
518
- end
519
- end