sequel_core 2.2.0 → 3.8.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.
- metadata +30 -101
- data/CHANGELOG +0 -1519
- data/COPYING +0 -19
- data/README +0 -313
- data/Rakefile +0 -158
- data/bin/sequel +0 -117
- data/doc/cheat_sheet.rdoc +0 -225
- data/doc/dataset_filtering.rdoc +0 -182
- data/lib/sequel_core.rb +0 -136
- data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -68
- data/lib/sequel_core/adapters/ado.rb +0 -90
- data/lib/sequel_core/adapters/db2.rb +0 -160
- data/lib/sequel_core/adapters/dbi.rb +0 -127
- data/lib/sequel_core/adapters/informix.rb +0 -89
- data/lib/sequel_core/adapters/jdbc.rb +0 -110
- data/lib/sequel_core/adapters/mysql.rb +0 -486
- data/lib/sequel_core/adapters/odbc.rb +0 -167
- data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
- data/lib/sequel_core/adapters/openbase.rb +0 -76
- data/lib/sequel_core/adapters/oracle.rb +0 -182
- data/lib/sequel_core/adapters/postgres.rb +0 -560
- data/lib/sequel_core/adapters/sqlite.rb +0 -270
- data/lib/sequel_core/connection_pool.rb +0 -194
- data/lib/sequel_core/core_ext.rb +0 -197
- data/lib/sequel_core/core_sql.rb +0 -184
- data/lib/sequel_core/database.rb +0 -462
- data/lib/sequel_core/database/schema.rb +0 -156
- data/lib/sequel_core/dataset.rb +0 -457
- data/lib/sequel_core/dataset/callback.rb +0 -13
- data/lib/sequel_core/dataset/convenience.rb +0 -245
- data/lib/sequel_core/dataset/pagination.rb +0 -96
- data/lib/sequel_core/dataset/query.rb +0 -41
- data/lib/sequel_core/dataset/schema.rb +0 -15
- data/lib/sequel_core/dataset/sql.rb +0 -889
- data/lib/sequel_core/deprecated.rb +0 -26
- data/lib/sequel_core/exceptions.rb +0 -42
- data/lib/sequel_core/migration.rb +0 -187
- data/lib/sequel_core/object_graph.rb +0 -216
- data/lib/sequel_core/pretty_table.rb +0 -71
- data/lib/sequel_core/schema.rb +0 -2
- data/lib/sequel_core/schema/generator.rb +0 -239
- data/lib/sequel_core/schema/sql.rb +0 -326
- data/lib/sequel_core/sql.rb +0 -812
- data/lib/sequel_core/worker.rb +0 -68
- data/spec/adapters/informix_spec.rb +0 -96
- data/spec/adapters/mysql_spec.rb +0 -765
- data/spec/adapters/oracle_spec.rb +0 -222
- data/spec/adapters/postgres_spec.rb +0 -441
- data/spec/adapters/sqlite_spec.rb +0 -413
- data/spec/connection_pool_spec.rb +0 -363
- data/spec/core_ext_spec.rb +0 -156
- data/spec/core_sql_spec.rb +0 -427
- data/spec/database_spec.rb +0 -963
- data/spec/dataset_spec.rb +0 -2933
- data/spec/expression_filters_spec.rb +0 -316
- data/spec/migration_spec.rb +0 -261
- data/spec/object_graph_spec.rb +0 -230
- data/spec/pretty_table_spec.rb +0 -58
- data/spec/rcov.opts +0 -6
- data/spec/schema_generator_spec.rb +0 -122
- data/spec/schema_spec.rb +0 -422
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb +0 -7
- data/spec/spec_config.rb.example +0 -8
- data/spec/spec_helper.rb +0 -55
- data/spec/worker_spec.rb +0 -96
@@ -1,560 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'pg'
|
3
|
-
rescue LoadError => e
|
4
|
-
begin
|
5
|
-
require 'postgres'
|
6
|
-
class PGconn
|
7
|
-
unless method_defined?(:escape_string)
|
8
|
-
if self.respond_to?(:escape)
|
9
|
-
def escape_string(str)
|
10
|
-
self.class.escape(str)
|
11
|
-
end
|
12
|
-
else
|
13
|
-
def escape_string(obj)
|
14
|
-
raise Sequel::Error, "string escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
unless method_defined?(:escape_bytea)
|
19
|
-
if self.respond_to?(:escape_bytea)
|
20
|
-
def escape_bytea(obj)
|
21
|
-
self.class.escape_bytea(obj)
|
22
|
-
end
|
23
|
-
else
|
24
|
-
begin
|
25
|
-
require 'postgres-pr/typeconv/conv'
|
26
|
-
require 'postgres-pr/typeconv/bytea'
|
27
|
-
extend Postgres::Conversion
|
28
|
-
def escape_bytea(obj)
|
29
|
-
self.class.encode_bytea(obj)
|
30
|
-
end
|
31
|
-
metaalias :unescape_bytea, :decode_bytea
|
32
|
-
rescue
|
33
|
-
def escape_bytea(obj)
|
34
|
-
raise Sequel::Error, "bytea escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
|
35
|
-
end
|
36
|
-
def self.unescape_bytea(obj)
|
37
|
-
raise Sequel::Error, "bytea unescaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
alias_method :finish, :close unless method_defined?(:finish)
|
43
|
-
end
|
44
|
-
class PGresult
|
45
|
-
alias_method :nfields, :num_fields unless method_defined?(:nfields)
|
46
|
-
alias_method :ntuples, :num_tuples unless method_defined?(:ntuples)
|
47
|
-
alias_method :ftype, :type unless method_defined?(:ftype)
|
48
|
-
alias_method :fname, :fieldname unless method_defined?(:fname)
|
49
|
-
alias_method :cmd_tuples, :cmdtuples unless method_defined?(:cmd_tuples)
|
50
|
-
end
|
51
|
-
rescue LoadError
|
52
|
-
raise e
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
module Sequel
|
57
|
-
module Postgres
|
58
|
-
class Adapter < ::PGconn
|
59
|
-
def connected?
|
60
|
-
status == Adapter::CONNECTION_OK
|
61
|
-
end
|
62
|
-
|
63
|
-
def execute(sql, &block)
|
64
|
-
q = nil
|
65
|
-
begin
|
66
|
-
q = exec(sql)
|
67
|
-
rescue PGError => e
|
68
|
-
unless connected?
|
69
|
-
reset
|
70
|
-
q = exec(sql)
|
71
|
-
else
|
72
|
-
raise e
|
73
|
-
end
|
74
|
-
end
|
75
|
-
begin
|
76
|
-
block ? block[q] : q.cmd_tuples
|
77
|
-
ensure
|
78
|
-
q.clear
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
attr_accessor :transaction_depth
|
83
|
-
|
84
|
-
SELECT_CURRVAL = "SELECT currval('%s')".freeze
|
85
|
-
|
86
|
-
def last_insert_id(table)
|
87
|
-
@table_sequences ||= {}
|
88
|
-
if !@table_sequences.include?(table)
|
89
|
-
pkey_and_seq = pkey_and_sequence(table)
|
90
|
-
if pkey_and_seq
|
91
|
-
@table_sequences[table] = pkey_and_seq[1]
|
92
|
-
end
|
93
|
-
end
|
94
|
-
if seq = @table_sequences[table]
|
95
|
-
execute(SELECT_CURRVAL % seq) do |r|
|
96
|
-
return r.getvalue(0,0).to_i unless r.nil? || (r.ntuples == 0)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
nil # primary key sequence not found
|
100
|
-
end
|
101
|
-
|
102
|
-
# Shamelessly appropriated from ActiveRecord's Postgresql adapter.
|
103
|
-
SELECT_PK_AND_SERIAL_SEQUENCE = <<-end_sql
|
104
|
-
SELECT attr.attname, name.nspname, seq.relname
|
105
|
-
FROM pg_class seq, pg_attribute attr, pg_depend dep,
|
106
|
-
pg_namespace name, pg_constraint cons
|
107
|
-
WHERE seq.oid = dep.objid
|
108
|
-
AND seq.relnamespace = name.oid
|
109
|
-
AND seq.relkind = 'S'
|
110
|
-
AND attr.attrelid = dep.refobjid
|
111
|
-
AND attr.attnum = dep.refobjsubid
|
112
|
-
AND attr.attrelid = cons.conrelid
|
113
|
-
AND attr.attnum = cons.conkey[1]
|
114
|
-
AND cons.contype = 'p'
|
115
|
-
AND dep.refobjid = '%s'::regclass
|
116
|
-
end_sql
|
117
|
-
|
118
|
-
SELECT_PK_AND_CUSTOM_SEQUENCE = <<-end_sql
|
119
|
-
SELECT attr.attname,
|
120
|
-
CASE
|
121
|
-
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
122
|
-
substr(split_part(def.adsrc, '''', 2),
|
123
|
-
strpos(split_part(def.adsrc, '''', 2), '.')+1)
|
124
|
-
ELSE split_part(def.adsrc, '''', 2)
|
125
|
-
END
|
126
|
-
FROM pg_class t
|
127
|
-
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
128
|
-
JOIN pg_attribute attr ON (t.oid = attrelid)
|
129
|
-
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
130
|
-
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
131
|
-
WHERE t.oid = '%s'::regclass
|
132
|
-
AND cons.contype = 'p'
|
133
|
-
AND def.adsrc ~* 'nextval'
|
134
|
-
end_sql
|
135
|
-
|
136
|
-
SELECT_PK = <<-end_sql
|
137
|
-
SELECT pg_attribute.attname
|
138
|
-
FROM pg_class, pg_attribute, pg_index
|
139
|
-
WHERE pg_class.oid = pg_attribute.attrelid AND
|
140
|
-
pg_class.oid = pg_index.indrelid AND
|
141
|
-
pg_index.indkey[0] = pg_attribute.attnum AND
|
142
|
-
pg_index.indisprimary = 't' AND
|
143
|
-
pg_class.relname = '%s'
|
144
|
-
end_sql
|
145
|
-
|
146
|
-
def pkey_and_sequence(table)
|
147
|
-
execute(SELECT_PK_AND_SERIAL_SEQUENCE % table) do |r|
|
148
|
-
return [r.getvalue(0,2), r.getvalue(0,2)] unless r.nil? || (r.ntuples == 0)
|
149
|
-
end
|
150
|
-
|
151
|
-
execute(SELECT_PK_AND_CUSTOM_SEQUENCE % table) do |r|
|
152
|
-
return [r.getvalue(0,0), r.getvalue(0,1)] unless r.nil? || (r.ntuples == 0)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def primary_key(table)
|
157
|
-
execute(SELECT_PK % table) do |r|
|
158
|
-
if (r.nil? || (r.ntuples == 0)) then
|
159
|
-
return nil
|
160
|
-
else
|
161
|
-
r.getvalue(0,0)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def self.string_to_bool(s)
|
167
|
-
if(s.blank?)
|
168
|
-
nil
|
169
|
-
elsif(s.downcase == 't' || s.downcase == 'true')
|
170
|
-
true
|
171
|
-
else
|
172
|
-
false
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
PG_TYPES = {
|
178
|
-
16 => lambda{ |s| Adapter.string_to_bool(s) }, # boolean
|
179
|
-
17 => lambda{ |s| Adapter.unescape_bytea(s).to_blob }, # bytea
|
180
|
-
20 => lambda{ |s| s.to_i }, # int8
|
181
|
-
21 => lambda{ |s| s.to_i }, # int2
|
182
|
-
22 => lambda{ |s| s.to_i }, # int2vector
|
183
|
-
23 => lambda{ |s| s.to_i }, # int4
|
184
|
-
26 => lambda{ |s| s.to_i }, # oid
|
185
|
-
700 => lambda{ |s| s.to_f }, # float4
|
186
|
-
701 => lambda{ |s| s.to_f }, # float8
|
187
|
-
790 => lambda{ |s| s.to_d }, # money
|
188
|
-
1082 => lambda{ |s| s.to_date }, # date
|
189
|
-
1083 => lambda{ |s| s.to_time }, # time without time zone
|
190
|
-
1114 => lambda{ |s| s.to_sequel_time }, # timestamp without time zone
|
191
|
-
1184 => lambda{ |s| s.to_sequel_time }, # timestamp with time zone
|
192
|
-
1186 => lambda{ |s| s.to_i }, # interval
|
193
|
-
1266 => lambda{ |s| s.to_time }, # time with time zone
|
194
|
-
1700 => lambda{ |s| s.to_d }, # numeric
|
195
|
-
}
|
196
|
-
|
197
|
-
if Adapter.respond_to?(:translate_results=)
|
198
|
-
Adapter.translate_results = false
|
199
|
-
end
|
200
|
-
AUTO_TRANSLATE = false
|
201
|
-
|
202
|
-
class Database < Sequel::Database
|
203
|
-
set_adapter_scheme :postgres
|
204
|
-
|
205
|
-
def connect
|
206
|
-
conn = Adapter.connect(
|
207
|
-
@opts[:host] || 'localhost',
|
208
|
-
@opts[:port] || 5432,
|
209
|
-
'', '',
|
210
|
-
@opts[:database],
|
211
|
-
@opts[:user],
|
212
|
-
@opts[:password]
|
213
|
-
)
|
214
|
-
if encoding = @opts[:encoding] || @opts[:charset]
|
215
|
-
conn.set_client_encoding(encoding)
|
216
|
-
end
|
217
|
-
conn
|
218
|
-
end
|
219
|
-
|
220
|
-
def disconnect
|
221
|
-
@pool.disconnect {|c| c.finish}
|
222
|
-
end
|
223
|
-
|
224
|
-
def dataset(opts = nil)
|
225
|
-
Postgres::Dataset.new(self, opts)
|
226
|
-
end
|
227
|
-
|
228
|
-
RELATION_QUERY = {:from => [:pg_class], :select => [:relname]}.freeze
|
229
|
-
RELATION_FILTER = "(relkind = 'r') AND (relname !~ '^pg|sql')".freeze
|
230
|
-
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
231
|
-
|
232
|
-
def tables
|
233
|
-
dataset(RELATION_QUERY).filter(RELATION_FILTER).map {|r| r[:relname].to_sym}
|
234
|
-
end
|
235
|
-
|
236
|
-
def locks
|
237
|
-
dataset.from("pg_class, pg_locks").
|
238
|
-
select("pg_class.relname, pg_locks.*").
|
239
|
-
filter("pg_class.relfilenode=pg_locks.relation")
|
240
|
-
end
|
241
|
-
|
242
|
-
def execute(sql, &block)
|
243
|
-
begin
|
244
|
-
log_info(sql)
|
245
|
-
@pool.hold {|conn| conn.execute(sql, &block)}
|
246
|
-
rescue => e
|
247
|
-
log_info(e.message)
|
248
|
-
raise convert_pgerror(e)
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def primary_key_for_table(conn, table)
|
253
|
-
@primary_keys ||= {}
|
254
|
-
@primary_keys[table] ||= conn.primary_key(table)
|
255
|
-
end
|
256
|
-
|
257
|
-
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session/.freeze
|
258
|
-
|
259
|
-
def insert_result(conn, table, values)
|
260
|
-
begin
|
261
|
-
result = conn.last_insert_id(table)
|
262
|
-
return result if result
|
263
|
-
rescue PGError => e
|
264
|
-
raise(Error, e.message) unless RE_CURRVAL_ERROR.match(e.message)
|
265
|
-
end
|
266
|
-
|
267
|
-
case values
|
268
|
-
when Hash
|
269
|
-
values[primary_key_for_table(conn, table)]
|
270
|
-
when Array
|
271
|
-
values.first
|
272
|
-
else
|
273
|
-
nil
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def server_version
|
278
|
-
return @server_version if @server_version
|
279
|
-
@server_version = pool.hold do |conn|
|
280
|
-
if conn.respond_to?(:server_version)
|
281
|
-
begin
|
282
|
-
conn.server_version
|
283
|
-
rescue StandardError
|
284
|
-
nil
|
285
|
-
end
|
286
|
-
end
|
287
|
-
end
|
288
|
-
unless @server_version
|
289
|
-
m = /PostgreSQL (\d+)\.(\d+)\.(\d+)/.match(get(:version[]))
|
290
|
-
@server_version = (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
291
|
-
end
|
292
|
-
@server_version
|
293
|
-
end
|
294
|
-
|
295
|
-
def execute_insert(sql, table, values)
|
296
|
-
begin
|
297
|
-
log_info(sql)
|
298
|
-
@pool.hold do |conn|
|
299
|
-
conn.execute(sql)
|
300
|
-
insert_result(conn, table, values)
|
301
|
-
end
|
302
|
-
rescue => e
|
303
|
-
log_info(e.message)
|
304
|
-
raise convert_pgerror(e)
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
SQL_BEGIN = 'BEGIN'.freeze
|
309
|
-
SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
|
310
|
-
SQL_COMMIT = 'COMMIT'.freeze
|
311
|
-
SQL_ROLLBACK_TO_SAVEPOINT = 'ROLLBACK TO SAVEPOINT autopoint_%d'.freeze
|
312
|
-
SQL_ROLLBACK = 'ROLLBACK'.freeze
|
313
|
-
SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
|
314
|
-
|
315
|
-
def transaction
|
316
|
-
@pool.hold do |conn|
|
317
|
-
conn.transaction_depth = 0 if conn.transaction_depth.nil?
|
318
|
-
if conn.transaction_depth > 0
|
319
|
-
log_info(SQL_SAVEPOINT % conn.transaction_depth)
|
320
|
-
conn.execute(SQL_SAVEPOINT % conn.transaction_depth)
|
321
|
-
else
|
322
|
-
log_info(SQL_BEGIN)
|
323
|
-
conn.execute(SQL_BEGIN)
|
324
|
-
end
|
325
|
-
begin
|
326
|
-
conn.transaction_depth += 1
|
327
|
-
yield conn
|
328
|
-
rescue ::Exception => e
|
329
|
-
if conn.transaction_depth > 1
|
330
|
-
log_info(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
|
331
|
-
conn.execute(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
|
332
|
-
else
|
333
|
-
log_info(SQL_ROLLBACK)
|
334
|
-
conn.execute(SQL_ROLLBACK) rescue nil
|
335
|
-
end
|
336
|
-
raise convert_pgerror(e) unless Error::Rollback === e
|
337
|
-
ensure
|
338
|
-
unless e
|
339
|
-
begin
|
340
|
-
if conn.transaction_depth < 2
|
341
|
-
log_info(SQL_COMMIT)
|
342
|
-
conn.execute(SQL_COMMIT)
|
343
|
-
else
|
344
|
-
log_info(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
345
|
-
conn.execute(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
346
|
-
end
|
347
|
-
rescue => e
|
348
|
-
log_info(e.message)
|
349
|
-
raise convert_pgerror(e)
|
350
|
-
end
|
351
|
-
end
|
352
|
-
conn.transaction_depth -= 1
|
353
|
-
end
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
def serial_primary_key_options
|
358
|
-
{:primary_key => true, :type => :serial}
|
359
|
-
end
|
360
|
-
|
361
|
-
def index_definition_sql(table_name, index)
|
362
|
-
index_name = index[:name] || default_index_name(table_name, index[:columns])
|
363
|
-
expr = literal(Array(index[:columns]))
|
364
|
-
unique = "UNIQUE " if index[:unique]
|
365
|
-
index_type = index[:type]
|
366
|
-
filter = index[:where] || index[:filter]
|
367
|
-
filter = " WHERE #{filter_expr(filter)}" if filter
|
368
|
-
case index_type
|
369
|
-
when :full_text
|
370
|
-
lang = index[:language] ? "#{literal(index[:language])}, " : ""
|
371
|
-
cols = index[:columns].map {|c| literal(c)}.join(" || ")
|
372
|
-
expr = "(to_tsvector(#{lang}#{cols}))"
|
373
|
-
index_type = :gin
|
374
|
-
when :spatial
|
375
|
-
index_type = :gist
|
376
|
-
end
|
377
|
-
"CREATE #{unique}INDEX #{index_name} ON #{table_name} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
378
|
-
end
|
379
|
-
|
380
|
-
def drop_table_sql(name)
|
381
|
-
"DROP TABLE #{name} CASCADE"
|
382
|
-
end
|
383
|
-
|
384
|
-
private
|
385
|
-
# If the given exception is a PGError, return a Sequel::Error with the same message, otherwise
|
386
|
-
# just return the given exception
|
387
|
-
def convert_pgerror(e)
|
388
|
-
PGError === e ? Error.new(e.message) : e
|
389
|
-
end
|
390
|
-
|
391
|
-
# PostgreSQL currently can always reuse connections. It doesn't need the pool to convert exceptions, either.
|
392
|
-
def connection_pool_default_options
|
393
|
-
super.merge(:pool_convert_exceptions=>false)
|
394
|
-
end
|
395
|
-
|
396
|
-
def schema_ds_filter(table_name, opts)
|
397
|
-
filt = super
|
398
|
-
# Restrict it to the given or public schema, unless specifically requesting :schema = nil
|
399
|
-
filt = SQL::BooleanExpression.new(:AND, filt, {:c__table_schema=>opts[:schema] || 'public'}) if opts[:schema] || !opts.include?(:schema)
|
400
|
-
filt
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
|
-
class Dataset < Sequel::Dataset
|
405
|
-
def quoted_identifier(c)
|
406
|
-
"\"#{c}\""
|
407
|
-
end
|
408
|
-
|
409
|
-
PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
|
410
|
-
BOOL_FALSE = 'false'.freeze
|
411
|
-
BOOL_TRUE = 'true'.freeze
|
412
|
-
|
413
|
-
def literal(v)
|
414
|
-
case v
|
415
|
-
when LiteralString
|
416
|
-
v
|
417
|
-
when String
|
418
|
-
db.synchronize{|c| "'#{SQL::Blob === v ? c.escape_bytea(v) : c.escape_string(v)}'"}
|
419
|
-
when Time
|
420
|
-
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
|
421
|
-
when DateTime
|
422
|
-
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
|
423
|
-
when TrueClass
|
424
|
-
BOOL_TRUE
|
425
|
-
when FalseClass
|
426
|
-
BOOL_FALSE
|
427
|
-
else
|
428
|
-
super
|
429
|
-
end
|
430
|
-
end
|
431
|
-
|
432
|
-
def match_expr(l, r)
|
433
|
-
case r
|
434
|
-
when Regexp
|
435
|
-
r.casefold? ? \
|
436
|
-
"(#{literal(l)} ~* #{literal(r.source)})" :
|
437
|
-
"(#{literal(l)} ~ #{literal(r.source)})"
|
438
|
-
else
|
439
|
-
super
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
def full_text_search(cols, terms, opts = {})
|
444
|
-
lang = opts[:language] ? "#{literal(opts[:language])}, " : ""
|
445
|
-
cols = cols.is_a?(Array) ? cols.map {|c| literal(c)}.join(" || ") : literal(cols)
|
446
|
-
terms = terms.is_a?(Array) ? literal(terms.join(" | ")) : literal(terms)
|
447
|
-
filter("to_tsvector(#{lang}#{cols}) @@ to_tsquery(#{lang}#{terms})")
|
448
|
-
end
|
449
|
-
|
450
|
-
FOR_UPDATE = ' FOR UPDATE'.freeze
|
451
|
-
FOR_SHARE = ' FOR SHARE'.freeze
|
452
|
-
|
453
|
-
def select_sql(opts = nil)
|
454
|
-
row_lock_mode = opts ? opts[:lock] : @opts[:lock]
|
455
|
-
sql = super
|
456
|
-
case row_lock_mode
|
457
|
-
when :update
|
458
|
-
sql << FOR_UPDATE
|
459
|
-
when :share
|
460
|
-
sql << FOR_SHARE
|
461
|
-
end
|
462
|
-
sql
|
463
|
-
end
|
464
|
-
|
465
|
-
def for_update
|
466
|
-
clone(:lock => :update)
|
467
|
-
end
|
468
|
-
|
469
|
-
def for_share
|
470
|
-
clone(:lock => :share)
|
471
|
-
end
|
472
|
-
|
473
|
-
EXPLAIN = 'EXPLAIN '.freeze
|
474
|
-
EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
|
475
|
-
QUERY_PLAN = 'QUERY PLAN'.to_sym
|
476
|
-
|
477
|
-
def explain(opts = nil)
|
478
|
-
analysis = []
|
479
|
-
fetch_rows(EXPLAIN + select_sql(opts)) do |r|
|
480
|
-
analysis << r[QUERY_PLAN]
|
481
|
-
end
|
482
|
-
analysis.join("\r\n")
|
483
|
-
end
|
484
|
-
|
485
|
-
def analyze(opts = nil)
|
486
|
-
analysis = []
|
487
|
-
fetch_rows(EXPLAIN_ANALYZE + select_sql(opts)) do |r|
|
488
|
-
analysis << r[QUERY_PLAN]
|
489
|
-
end
|
490
|
-
analysis.join("\r\n")
|
491
|
-
end
|
492
|
-
|
493
|
-
LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
|
494
|
-
|
495
|
-
ACCESS_SHARE = 'ACCESS SHARE'.freeze
|
496
|
-
ROW_SHARE = 'ROW SHARE'.freeze
|
497
|
-
ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
|
498
|
-
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
499
|
-
SHARE = 'SHARE'.freeze
|
500
|
-
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
501
|
-
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
502
|
-
ACCESS_EXCLUSIVE = 'ACCESS EXCLUSIVE'.freeze
|
503
|
-
|
504
|
-
# Locks the table with the specified mode.
|
505
|
-
def lock(mode, &block)
|
506
|
-
sql = LOCK % [source_list(@opts[:from]), mode]
|
507
|
-
@db.synchronize do
|
508
|
-
if block # perform locking inside a transaction and yield to block
|
509
|
-
@db.transaction {@db.execute(sql); yield}
|
510
|
-
else
|
511
|
-
@db.execute(sql) # lock without a transaction
|
512
|
-
self
|
513
|
-
end
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
def multi_insert_sql(columns, values)
|
518
|
-
return super if @db.server_version < 80200
|
519
|
-
|
520
|
-
# postgresql 8.2 introduces support for multi-row insert
|
521
|
-
columns = column_list(columns)
|
522
|
-
values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
|
523
|
-
["INSERT INTO #{source_list(@opts[:from])} (#{columns}) VALUES #{values}"]
|
524
|
-
end
|
525
|
-
|
526
|
-
def insert(*values)
|
527
|
-
@db.execute_insert(insert_sql(*values), source_list(@opts[:from]),
|
528
|
-
values.size == 1 ? values.first : values)
|
529
|
-
end
|
530
|
-
|
531
|
-
def update(*args, &block)
|
532
|
-
@db.execute(update_sql(*args, &block))
|
533
|
-
end
|
534
|
-
|
535
|
-
def delete(opts = nil)
|
536
|
-
@db.execute(delete_sql(opts))
|
537
|
-
end
|
538
|
-
|
539
|
-
def fetch_rows(sql, &block)
|
540
|
-
@columns = []
|
541
|
-
@db.execute(sql) do |res|
|
542
|
-
(0...res.ntuples).each do |recnum|
|
543
|
-
converted_rec = {}
|
544
|
-
(0...res.nfields).each do |fieldnum|
|
545
|
-
fieldsym = res.fname(fieldnum).to_sym
|
546
|
-
@columns << fieldsym
|
547
|
-
converted_rec[fieldsym] = if value = res.getvalue(recnum,fieldnum)
|
548
|
-
(PG_TYPES[res.ftype(fieldnum)] || lambda{|s| s.to_s}).call(value)
|
549
|
-
else
|
550
|
-
value
|
551
|
-
end
|
552
|
-
end
|
553
|
-
yield converted_rec
|
554
|
-
end
|
555
|
-
end
|
556
|
-
end
|
557
|
-
end
|
558
|
-
end
|
559
|
-
end
|
560
|
-
|