sequel 0.5.0.2 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
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