sequel 3.34.0 → 3.35.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.
- data/CHANGELOG +56 -0
- data/README.rdoc +3 -1
- data/Rakefile +2 -10
- data/doc/active_record.rdoc +1 -0
- data/doc/migration.rdoc +18 -7
- data/doc/model_hooks.rdoc +6 -0
- data/doc/opening_databases.rdoc +3 -0
- data/doc/prepared_statements.rdoc +0 -1
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/schema_modification.rdoc +16 -1
- data/doc/thread_safety.rdoc +17 -0
- data/lib/sequel/adapters/do/postgres.rb +1 -52
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +2 -2
- data/lib/sequel/adapters/firebird.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +29 -2
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +23 -19
- data/lib/sequel/adapters/mysql2.rb +1 -1
- data/lib/sequel/adapters/odbc/mssql.rb +14 -1
- data/lib/sequel/adapters/odbc.rb +3 -3
- data/lib/sequel/adapters/oracle.rb +6 -18
- data/lib/sequel/adapters/postgres.rb +36 -53
- data/lib/sequel/adapters/shared/db2.rb +16 -2
- data/lib/sequel/adapters/shared/mssql.rb +40 -9
- data/lib/sequel/adapters/shared/mysql.rb +16 -4
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +2 -0
- data/lib/sequel/adapters/shared/postgres.rb +135 -211
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/swift/postgres.rb +1 -71
- data/lib/sequel/adapters/swift.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +3 -3
- data/lib/sequel/core.rb +27 -4
- data/lib/sequel/database/connecting.rb +7 -8
- data/lib/sequel/database/logging.rb +6 -1
- data/lib/sequel/database/misc.rb +20 -4
- data/lib/sequel/database/query.rb +38 -18
- data/lib/sequel/database/schema_generator.rb +5 -2
- data/lib/sequel/database/schema_methods.rb +34 -8
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/sql.rb +18 -24
- data/lib/sequel/extensions/core_extensions.rb +0 -23
- data/lib/sequel/extensions/migration.rb +22 -8
- data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/model/associations.rb +95 -70
- data/lib/sequel/model/base.rb +19 -20
- data/lib/sequel/model.rb +2 -2
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +16 -1
- data/lib/sequel/plugins/many_through_many.rb +22 -32
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
- data/lib/sequel/plugins/prepared_statements.rb +22 -8
- data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +12 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +30 -79
- data/spec/core/database_spec.rb +46 -2
- data/spec/core/dataset_spec.rb +28 -22
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +51 -0
- data/spec/extensions/arbitrary_servers_spec.rb +0 -4
- data/spec/extensions/association_autoreloading_spec.rb +17 -0
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/core_extensions_spec.rb +1 -24
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/json_serializer_spec.rb +13 -0
- data/spec/extensions/migration_spec.rb +28 -15
- data/spec/extensions/named_timezones_spec.rb +6 -8
- data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
- data/spec/extensions/schema_dumper_spec.rb +3 -1
- data/spec/extensions/single_table_inheritance_spec.rb +5 -0
- data/spec/extensions/xml_serializer_spec.rb +13 -0
- data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
- data/spec/integration/associations_test.rb +5 -7
- data/spec/integration/dataset_test.rb +25 -7
- data/spec/integration/plugin_test.rb +1 -1
- data/spec/integration/schema_test.rb +16 -1
- data/spec/model/associations_spec.rb +2 -2
- metadata +14 -9
- data/lib/sequel/adapters/odbc/db2.rb +0 -17
- /data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
- /data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
- /data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
|
@@ -5,11 +5,6 @@ module Sequel
|
|
|
5
5
|
# Database and Dataset instance methods for JTDS specific
|
|
6
6
|
# support via JDBC.
|
|
7
7
|
module JTDS
|
|
8
|
-
# Database instance methods for JTDS databases accessed via JDBC.
|
|
9
|
-
module DatabaseMethods
|
|
10
|
-
include Sequel::JDBC::MSSQL::DatabaseMethods
|
|
11
|
-
end
|
|
12
|
-
|
|
13
8
|
# Dataset class for JTDS datasets accessed via JDBC.
|
|
14
9
|
class Dataset < JDBC::Dataset
|
|
15
10
|
include Sequel::MSSQL::DatasetMethods
|
|
@@ -7,32 +7,6 @@ module Sequel
|
|
|
7
7
|
# Adapter, Database, and Dataset support for accessing a PostgreSQL
|
|
8
8
|
# database via JDBC.
|
|
9
9
|
module Postgres
|
|
10
|
-
# Methods to add to the JDBC adapter/connection to allow it to work
|
|
11
|
-
# with the shared PostgreSQL code.
|
|
12
|
-
module AdapterMethods
|
|
13
|
-
include Sequel::Postgres::AdapterMethods
|
|
14
|
-
|
|
15
|
-
# Give the JDBC adapter a direct execute method, which creates
|
|
16
|
-
# a statement with the given sql and executes it.
|
|
17
|
-
def execute(sql, args=nil)
|
|
18
|
-
method = block_given? ? :executeQuery : :execute
|
|
19
|
-
@db.send(:statement, self) do |stmt|
|
|
20
|
-
rows = @db.log_yield(sql){stmt.send(method, sql)}
|
|
21
|
-
yield(rows) if block_given?
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
# JDBC specific method of getting specific values from a result set.
|
|
28
|
-
def single_value(r)
|
|
29
|
-
unless r.nil?
|
|
30
|
-
r.next
|
|
31
|
-
r.getString(1) unless r.getRow == 0
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
10
|
# Methods to add to Database instances that access PostgreSQL via
|
|
37
11
|
# JDBC.
|
|
38
12
|
module DatabaseMethods
|
|
@@ -55,19 +29,14 @@ module Sequel
|
|
|
55
29
|
arg.nil? ? cps.setNull(i, JavaSQL::Types::NULL) : super
|
|
56
30
|
end
|
|
57
31
|
|
|
58
|
-
#
|
|
32
|
+
# Execute the connection configuration SQL queries on the connection.
|
|
59
33
|
def setup_connection(conn)
|
|
60
34
|
conn = super(conn)
|
|
61
|
-
conn
|
|
62
|
-
|
|
63
|
-
|
|
35
|
+
statement(conn) do |stmt|
|
|
36
|
+
connection_configuration_sqls.each{|sql| log_yield(sql){stmt.execute(sql)}}
|
|
37
|
+
end
|
|
64
38
|
conn
|
|
65
39
|
end
|
|
66
|
-
|
|
67
|
-
# Call insert_result with the table and values specified in the opts.
|
|
68
|
-
def last_insert_id(conn, opts)
|
|
69
|
-
insert_result(conn, opts[:table], opts[:values])
|
|
70
|
-
end
|
|
71
40
|
end
|
|
72
41
|
|
|
73
42
|
# Dataset subclass used for datasets that connect to PostgreSQL via JDBC.
|
|
@@ -24,7 +24,7 @@ module Sequel
|
|
|
24
24
|
# Use JDBC connection's setAutoCommit to false to start transactions
|
|
25
25
|
def begin_transaction(conn, opts={})
|
|
26
26
|
if supports_savepoints?
|
|
27
|
-
th =
|
|
27
|
+
th = _trans(conn)
|
|
28
28
|
if sps = th[:savepoints]
|
|
29
29
|
sps << log_yield(TRANSACTION_SAVEPOINT){conn.set_savepoint}
|
|
30
30
|
else
|
|
@@ -40,7 +40,7 @@ module Sequel
|
|
|
40
40
|
# Use JDBC connection's commit method to commit transactions
|
|
41
41
|
def commit_transaction(conn, opts={})
|
|
42
42
|
if supports_savepoints?
|
|
43
|
-
sps =
|
|
43
|
+
sps = _trans(conn)[:savepoints]
|
|
44
44
|
if sps.empty?
|
|
45
45
|
log_yield(TRANSACTION_COMMIT){conn.commit}
|
|
46
46
|
elsif supports_releasing_savepoints?
|
|
@@ -54,7 +54,7 @@ module Sequel
|
|
|
54
54
|
# Use JDBC connection's setAutoCommit to true to enable non-transactional behavior
|
|
55
55
|
def remove_transaction(conn, committed)
|
|
56
56
|
if supports_savepoints?
|
|
57
|
-
sps =
|
|
57
|
+
sps = _trans(conn)[:savepoints]
|
|
58
58
|
conn.setAutoCommit(true) if sps.empty?
|
|
59
59
|
sps.pop
|
|
60
60
|
else
|
|
@@ -67,7 +67,7 @@ module Sequel
|
|
|
67
67
|
# Use JDBC connection's rollback method to rollback transactions
|
|
68
68
|
def rollback_transaction(conn, opts={})
|
|
69
69
|
if supports_savepoints?
|
|
70
|
-
sps =
|
|
70
|
+
sps = _trans(conn)[:savepoints]
|
|
71
71
|
if sps.empty?
|
|
72
72
|
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
|
73
73
|
else
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
|
@@ -39,14 +39,14 @@ module Sequel
|
|
|
39
39
|
:mysql=>proc do |db|
|
|
40
40
|
Sequel.ts_require 'adapters/jdbc/mysql'
|
|
41
41
|
db.extend(Sequel::JDBC::MySQL::DatabaseMethods)
|
|
42
|
-
db.
|
|
42
|
+
db.extend_datasets Sequel::MySQL::DatasetMethods
|
|
43
43
|
JDBC.load_gem('mysql')
|
|
44
44
|
com.mysql.jdbc.Driver
|
|
45
45
|
end,
|
|
46
46
|
:sqlite=>proc do |db|
|
|
47
47
|
Sequel.ts_require 'adapters/jdbc/sqlite'
|
|
48
48
|
db.extend(Sequel::JDBC::SQLite::DatabaseMethods)
|
|
49
|
-
db.
|
|
49
|
+
db.extend_datasets Sequel::SQLite::DatasetMethods
|
|
50
50
|
db.set_integer_booleans
|
|
51
51
|
JDBC.load_gem('sqlite3')
|
|
52
52
|
org.sqlite.JDBC
|
|
@@ -60,13 +60,13 @@ module Sequel
|
|
|
60
60
|
:sqlserver=>proc do |db|
|
|
61
61
|
Sequel.ts_require 'adapters/jdbc/sqlserver'
|
|
62
62
|
db.extend(Sequel::JDBC::SQLServer::DatabaseMethods)
|
|
63
|
-
db.
|
|
63
|
+
db.extend_datasets Sequel::MSSQL::DatasetMethods
|
|
64
64
|
db.send(:set_mssql_unicode_strings)
|
|
65
65
|
com.microsoft.sqlserver.jdbc.SQLServerDriver
|
|
66
66
|
end,
|
|
67
67
|
:jtds=>proc do |db|
|
|
68
68
|
Sequel.ts_require 'adapters/jdbc/jtds'
|
|
69
|
-
db.extend(Sequel::JDBC::
|
|
69
|
+
db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
|
|
70
70
|
db.dataset_class = Sequel::JDBC::JTDS::Dataset
|
|
71
71
|
db.send(:set_mssql_unicode_strings)
|
|
72
72
|
JDBC.load_gem('jtds')
|
|
@@ -102,19 +102,19 @@ module Sequel
|
|
|
102
102
|
:"informix-sqli"=>proc do |db|
|
|
103
103
|
Sequel.ts_require 'adapters/jdbc/informix'
|
|
104
104
|
db.extend(Sequel::JDBC::Informix::DatabaseMethods)
|
|
105
|
-
db.
|
|
105
|
+
db.extend_datasets Sequel::Informix::DatasetMethods
|
|
106
106
|
com.informix.jdbc.IfxDriver
|
|
107
107
|
end,
|
|
108
108
|
:db2=>proc do |db|
|
|
109
109
|
Sequel.ts_require 'adapters/jdbc/db2'
|
|
110
110
|
db.extend(Sequel::JDBC::DB2::DatabaseMethods)
|
|
111
|
-
db.
|
|
111
|
+
db.extend_datasets Sequel::DB2::DatasetMethods
|
|
112
112
|
com.ibm.db2.jcc.DB2Driver
|
|
113
113
|
end,
|
|
114
114
|
:firebirdsql=>proc do |db|
|
|
115
115
|
Sequel.ts_require 'adapters/jdbc/firebird'
|
|
116
116
|
db.extend(Sequel::JDBC::Firebird::DatabaseMethods)
|
|
117
|
-
db.
|
|
117
|
+
db.extend_datasets Sequel::Firebird::DatasetMethods
|
|
118
118
|
org.firebirdsql.jdbc.FBDriver
|
|
119
119
|
end
|
|
120
120
|
}
|
|
@@ -151,6 +151,8 @@ module Sequel
|
|
|
151
151
|
# uri, since JDBC requires one.
|
|
152
152
|
def initialize(opts)
|
|
153
153
|
super
|
|
154
|
+
@connection_prepared_statements = {}
|
|
155
|
+
@connection_prepared_statements_mutex = Mutex.new
|
|
154
156
|
@convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
|
|
155
157
|
raise(Error, "No connection string specified") unless uri
|
|
156
158
|
|
|
@@ -312,8 +314,15 @@ module Sequel
|
|
|
312
314
|
|
|
313
315
|
private
|
|
314
316
|
|
|
315
|
-
#
|
|
317
|
+
# Yield the native prepared statements hash for the given connection
|
|
318
|
+
# to the block in a thread-safe manner.
|
|
319
|
+
def cps_sync(conn, &block)
|
|
320
|
+
@connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})}
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Close given adapter connections, and delete any related prepared statements.
|
|
316
324
|
def disconnect_connection(c)
|
|
325
|
+
@connection_prepared_statements_mutex.synchronize{@connection_prepared_statements.delete(c)}
|
|
317
326
|
c.close
|
|
318
327
|
end
|
|
319
328
|
|
|
@@ -336,16 +345,16 @@ module Sequel
|
|
|
336
345
|
ps = name
|
|
337
346
|
name = ps.prepared_statement_name
|
|
338
347
|
else
|
|
339
|
-
ps =
|
|
348
|
+
ps = prepared_statement(name)
|
|
340
349
|
end
|
|
341
350
|
sql = ps.prepared_sql
|
|
342
351
|
synchronize(opts[:server]) do |conn|
|
|
343
|
-
if name and cps = conn
|
|
352
|
+
if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql
|
|
344
353
|
cps = cps[1]
|
|
345
354
|
else
|
|
346
355
|
log_yield("CLOSE #{name}"){cps[1].close} if cps
|
|
347
356
|
cps = log_yield("PREPARE#{" #{name}:" if name} #{sql}"){conn.prepareStatement(sql)}
|
|
348
|
-
conn
|
|
357
|
+
cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
|
|
349
358
|
end
|
|
350
359
|
i = 0
|
|
351
360
|
args.each{|arg| set_ps_arg(cps, arg, i+=1)}
|
|
@@ -476,14 +485,9 @@ module Sequel
|
|
|
476
485
|
end
|
|
477
486
|
end
|
|
478
487
|
|
|
479
|
-
#
|
|
480
|
-
#
|
|
481
|
-
# adapter specific prepared statements.
|
|
488
|
+
# Return the connection. Used to do configuration on the
|
|
489
|
+
# connection object before adding it to the connection pool.
|
|
482
490
|
def setup_connection(conn)
|
|
483
|
-
class << conn
|
|
484
|
-
attr_accessor :prepared_statements
|
|
485
|
-
end
|
|
486
|
-
conn.prepared_statements = {}
|
|
487
491
|
conn
|
|
488
492
|
end
|
|
489
493
|
|
|
@@ -608,7 +612,7 @@ module Sequel
|
|
|
608
612
|
ps.extend(PreparedStatementMethods)
|
|
609
613
|
if name
|
|
610
614
|
ps.prepared_statement_name = name
|
|
611
|
-
db.
|
|
615
|
+
db.set_prepared_statement(name, ps)
|
|
612
616
|
end
|
|
613
617
|
ps
|
|
614
618
|
end
|
|
@@ -28,9 +28,22 @@ module Sequel
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
|
-
|
|
32
31
|
class Dataset < ODBC::Dataset
|
|
33
32
|
include Sequel::MSSQL::DatasetMethods
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
|
37
|
+
# some translation.
|
|
38
|
+
def default_timestamp_format
|
|
39
|
+
TIMESTAMP_FORMAT
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
|
43
|
+
# some translation.
|
|
44
|
+
def literal_date(v)
|
|
45
|
+
v.strftime(ODBC_DATE_FORMAT)
|
|
46
|
+
end
|
|
34
47
|
end
|
|
35
48
|
end
|
|
36
49
|
end
|
data/lib/sequel/adapters/odbc.rb
CHANGED
|
@@ -22,9 +22,9 @@ module Sequel
|
|
|
22
22
|
extend Sequel::Progress::DatabaseMethods
|
|
23
23
|
extend_datasets(Sequel::Progress::DatasetMethods)
|
|
24
24
|
when 'db2'
|
|
25
|
-
Sequel.ts_require 'adapters/
|
|
26
|
-
extend Sequel::
|
|
27
|
-
|
|
25
|
+
Sequel.ts_require 'adapters/shared/db2'
|
|
26
|
+
extend ::Sequel::DB2::DatabaseMethods
|
|
27
|
+
extend_datasets ::Sequel::DB2::DatasetMethods
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -126,7 +126,7 @@ module Sequel
|
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def execute_prepared_statement(conn, type, name, opts)
|
|
129
|
-
ps =
|
|
129
|
+
ps = prepared_statement(name)
|
|
130
130
|
sql = ps.prepared_sql
|
|
131
131
|
if cursora = conn.prepared_statements[name]
|
|
132
132
|
cursor, cursor_sql = cursora
|
|
@@ -243,7 +243,7 @@ module Sequel
|
|
|
243
243
|
|
|
244
244
|
# Default values
|
|
245
245
|
defaults = begin
|
|
246
|
-
metadata_dataset.from(:
|
|
246
|
+
metadata_dataset.from(:user_tab_cols).
|
|
247
247
|
where(:table_name=>im.call(table)).
|
|
248
248
|
to_hash(:column_name, :data_default)
|
|
249
249
|
rescue DatabaseError
|
|
@@ -370,19 +370,6 @@ module Sequel
|
|
|
370
370
|
ps.call(bind_vars, &block)
|
|
371
371
|
end
|
|
372
372
|
|
|
373
|
-
# Prepare the given type of query with the given name and store
|
|
374
|
-
# it in the database. Note that a new native prepared statement is
|
|
375
|
-
# created on each call to this prepared statement.
|
|
376
|
-
def prepare(type, name=nil, *values)
|
|
377
|
-
ps = to_prepared_statement(type, values)
|
|
378
|
-
ps.extend(PreparedStatementMethods)
|
|
379
|
-
if name
|
|
380
|
-
ps.prepared_statement_name = name
|
|
381
|
-
db.prepared_statements[name] = ps
|
|
382
|
-
end
|
|
383
|
-
ps
|
|
384
|
-
end
|
|
385
|
-
|
|
386
373
|
def fetch_rows(sql)
|
|
387
374
|
execute(sql) do |cursor|
|
|
388
375
|
offset = @opts[:offset]
|
|
@@ -403,14 +390,15 @@ module Sequel
|
|
|
403
390
|
self
|
|
404
391
|
end
|
|
405
392
|
|
|
406
|
-
#
|
|
407
|
-
# database
|
|
393
|
+
# Prepare the given type of query with the given name and store
|
|
394
|
+
# it in the database. Note that a new native prepared statement is
|
|
395
|
+
# created on each call to this prepared statement.
|
|
408
396
|
def prepare(type, name=nil, *values)
|
|
409
397
|
ps = to_prepared_statement(type, values)
|
|
410
398
|
ps.extend(PreparedStatementMethods)
|
|
411
399
|
if name
|
|
412
400
|
ps.prepared_statement_name = name
|
|
413
|
-
db.
|
|
401
|
+
db.set_prepared_statement(name, ps)
|
|
414
402
|
end
|
|
415
403
|
ps
|
|
416
404
|
end
|
|
@@ -12,7 +12,7 @@ rescue LoadError => e
|
|
|
12
12
|
class PGconn
|
|
13
13
|
unless method_defined?(:escape_string)
|
|
14
14
|
if self.respond_to?(:escape)
|
|
15
|
-
# If there is no escape_string
|
|
15
|
+
# If there is no escape_string instance method, but there is an
|
|
16
16
|
# escape class method, use that instead.
|
|
17
17
|
def escape_string(str)
|
|
18
18
|
Sequel::Postgres.force_standard_strings ? str.gsub("'", "''") : self.class.escape(str)
|
|
@@ -91,24 +91,29 @@ module Sequel
|
|
|
91
91
|
NAN = 0.0/0.0
|
|
92
92
|
PLUS_INFINITY = 1.0/0.0
|
|
93
93
|
MINUS_INFINITY = -1.0/0.0
|
|
94
|
+
NAN_STR = 'NaN'.freeze
|
|
95
|
+
PLUS_INFINITY_STR = 'Infinity'.freeze
|
|
96
|
+
MINUS_INFINITY_STR = '-Infinity'.freeze
|
|
97
|
+
TRUE_STR = 't'.freeze
|
|
98
|
+
DASH_STR = '-'.freeze
|
|
94
99
|
|
|
95
100
|
TYPE_TRANSLATOR = tt = Class.new do
|
|
96
|
-
def boolean(s) s ==
|
|
101
|
+
def boolean(s) s == TRUE_STR end
|
|
97
102
|
def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
|
|
98
103
|
def integer(s) s.to_i end
|
|
99
104
|
def float(s)
|
|
100
105
|
case s
|
|
101
|
-
when
|
|
106
|
+
when NAN_STR
|
|
102
107
|
NAN
|
|
103
|
-
when
|
|
108
|
+
when PLUS_INFINITY_STR
|
|
104
109
|
PLUS_INFINITY
|
|
105
|
-
when
|
|
110
|
+
when MINUS_INFINITY_STR
|
|
106
111
|
MINUS_INFINITY
|
|
107
112
|
else
|
|
108
113
|
s.to_f
|
|
109
114
|
end
|
|
110
115
|
end
|
|
111
|
-
def date(s) ::Date.new(*s.split(
|
|
116
|
+
def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
|
|
112
117
|
end.new
|
|
113
118
|
|
|
114
119
|
# Hash with type name strings/symbols and callable values for converting PostgreSQL types.
|
|
@@ -149,8 +154,6 @@ module Sequel
|
|
|
149
154
|
class Adapter < ::PGconn
|
|
150
155
|
DISCONNECT_ERROR_RE = /\Acould not receive data from server: Software caused connection abort/
|
|
151
156
|
|
|
152
|
-
include Sequel::Postgres::AdapterMethods
|
|
153
|
-
|
|
154
157
|
self.translate_results = false if respond_to?(:translate_results=)
|
|
155
158
|
|
|
156
159
|
# Hash of prepared statements for this connection. Keys are
|
|
@@ -158,18 +161,6 @@ module Sequel
|
|
|
158
161
|
# are SQL strings.
|
|
159
162
|
attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
|
|
160
163
|
|
|
161
|
-
# Apply connection settings for this connection. Current sets
|
|
162
|
-
# the date style to ISO in order make Date object creation in ruby faster,
|
|
163
|
-
# if Postgres.use_iso_date_format is true.
|
|
164
|
-
def apply_connection_settings
|
|
165
|
-
super
|
|
166
|
-
if Postgres.use_iso_date_format
|
|
167
|
-
sql = "SET DateStyle = 'ISO'"
|
|
168
|
-
execute(sql)
|
|
169
|
-
end
|
|
170
|
-
@prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
|
|
171
|
-
end
|
|
172
|
-
|
|
173
164
|
# Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
|
|
174
165
|
# the connection status cannot be determined or it is not OK.
|
|
175
166
|
def check_disconnect_errors
|
|
@@ -210,11 +201,6 @@ module Sequel
|
|
|
210
201
|
def execute_query(sql, args)
|
|
211
202
|
@db.log_yield(sql, args){args ? async_exec(sql, args) : async_exec(sql)}
|
|
212
203
|
end
|
|
213
|
-
|
|
214
|
-
# Return the requested values for the given row.
|
|
215
|
-
def single_value(r)
|
|
216
|
-
r.getvalue(0, 0) unless r.nil? || (r.ntuples == 0)
|
|
217
|
-
end
|
|
218
204
|
end
|
|
219
205
|
|
|
220
206
|
# Database class for PostgreSQL databases used with Sequel and the
|
|
@@ -223,7 +209,7 @@ module Sequel
|
|
|
223
209
|
include Sequel::Postgres::DatabaseMethods
|
|
224
210
|
|
|
225
211
|
INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
|
|
226
|
-
INFINITE_DATETIME_VALUES = ([
|
|
212
|
+
INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
|
|
227
213
|
|
|
228
214
|
set_adapter_scheme :postgres
|
|
229
215
|
|
|
@@ -299,8 +285,9 @@ module Sequel
|
|
|
299
285
|
conn.async_exec("set client_encoding to '#{encoding}'")
|
|
300
286
|
end
|
|
301
287
|
end
|
|
302
|
-
conn.db
|
|
303
|
-
conn.
|
|
288
|
+
conn.instance_variable_set(:@db, self)
|
|
289
|
+
conn.instance_variable_set(:@prepared_statements, {}) if SEQUEL_POSTGRES_USES_PG
|
|
290
|
+
connection_configuration_sqls.each{|sql| conn.execute(sql)}
|
|
304
291
|
@conversion_procs ||= get_conversion_procs(conn)
|
|
305
292
|
conn
|
|
306
293
|
end
|
|
@@ -312,19 +299,7 @@ module Sequel
|
|
|
312
299
|
synchronize(opts[:server]){|conn| conn.execute(sql, opts[:arguments], &block)}
|
|
313
300
|
end
|
|
314
301
|
end
|
|
315
|
-
|
|
316
|
-
# Insert the values into the table and return the primary key (if
|
|
317
|
-
# automatically generated).
|
|
318
|
-
def execute_insert(sql, opts={})
|
|
319
|
-
return execute(sql, opts) if Symbol === sql
|
|
320
|
-
check_database_errors do
|
|
321
|
-
synchronize(opts[:server]) do |conn|
|
|
322
|
-
conn.execute(sql, opts[:arguments])
|
|
323
|
-
insert_result(conn, opts[:table], opts[:values])
|
|
324
|
-
end
|
|
325
|
-
end
|
|
326
|
-
end
|
|
327
|
-
|
|
302
|
+
|
|
328
303
|
if SEQUEL_POSTGRES_USES_PG
|
|
329
304
|
# +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
|
|
330
305
|
# results directly to the caller. This method is only supported if pg is the
|
|
@@ -470,6 +445,13 @@ module Sequel
|
|
|
470
445
|
end
|
|
471
446
|
end
|
|
472
447
|
|
|
448
|
+
# Set the DateStyle to ISO if configured, for faster date parsing.
|
|
449
|
+
def connection_configuration_sqls
|
|
450
|
+
sqls = super
|
|
451
|
+
sqls << "SET DateStyle = 'ISO'" if Postgres.use_iso_date_format
|
|
452
|
+
sqls
|
|
453
|
+
end
|
|
454
|
+
|
|
473
455
|
# Disconnect given connection
|
|
474
456
|
def disconnect_connection(conn)
|
|
475
457
|
begin
|
|
@@ -487,7 +469,7 @@ module Sequel
|
|
|
487
469
|
# of rows changed. If the :insert option is passed, return the value
|
|
488
470
|
# of the primary key for the last inserted row.
|
|
489
471
|
def execute_prepared_statement(name, opts={})
|
|
490
|
-
ps =
|
|
472
|
+
ps = prepared_statement(name)
|
|
491
473
|
sql = ps.prepared_sql
|
|
492
474
|
ps_name = name.to_s
|
|
493
475
|
synchronize(opts[:server]) do |conn|
|
|
@@ -511,14 +493,10 @@ module Sequel
|
|
|
511
493
|
end
|
|
512
494
|
|
|
513
495
|
q = conn.check_disconnect_errors{log_yield(log_sql, args){conn.exec_prepared(ps_name, args)}}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
block_given? ? yield(q) : q.cmd_tuples
|
|
519
|
-
ensure
|
|
520
|
-
q.clear
|
|
521
|
-
end
|
|
496
|
+
begin
|
|
497
|
+
block_given? ? yield(q) : q.cmd_tuples
|
|
498
|
+
ensure
|
|
499
|
+
q.clear
|
|
522
500
|
end
|
|
523
501
|
end
|
|
524
502
|
end
|
|
@@ -549,6 +527,10 @@ module Sequel
|
|
|
549
527
|
end
|
|
550
528
|
end
|
|
551
529
|
|
|
530
|
+
# Don't log, since logging is done by the underlying connection.
|
|
531
|
+
def log_connection_execute(conn, sql)
|
|
532
|
+
conn.execute(sql)
|
|
533
|
+
end
|
|
552
534
|
|
|
553
535
|
# If the value is an infinite value (either an infinite float or a string returned by
|
|
554
536
|
# by PostgreSQL for an infinite timestamp), return it without converting it if
|
|
@@ -573,6 +555,7 @@ module Sequel
|
|
|
573
555
|
include Sequel::Postgres::DatasetMethods
|
|
574
556
|
|
|
575
557
|
Database::DatasetClass = self
|
|
558
|
+
APOS = Sequel::Dataset::APOS
|
|
576
559
|
|
|
577
560
|
# Yield all rows returned by executing the given SQL and converting
|
|
578
561
|
# the types.
|
|
@@ -708,7 +691,7 @@ module Sequel
|
|
|
708
691
|
ps.extend(PreparedStatementMethods)
|
|
709
692
|
if name
|
|
710
693
|
ps.prepared_statement_name = name
|
|
711
|
-
db.
|
|
694
|
+
db.set_prepared_statement(name, ps)
|
|
712
695
|
end
|
|
713
696
|
ps
|
|
714
697
|
end
|
|
@@ -766,12 +749,12 @@ module Sequel
|
|
|
766
749
|
|
|
767
750
|
# Use the driver's escape_bytea
|
|
768
751
|
def literal_blob_append(sql, v)
|
|
769
|
-
sql <<
|
|
752
|
+
sql << APOS << db.synchronize{|c| c.escape_bytea(v)} << APOS
|
|
770
753
|
end
|
|
771
754
|
|
|
772
755
|
# Use the driver's escape_string
|
|
773
756
|
def literal_string_append(sql, v)
|
|
774
|
-
sql <<
|
|
757
|
+
sql << APOS << db.synchronize{|c| c.escape_string(v)} << APOS
|
|
775
758
|
end
|
|
776
759
|
|
|
777
760
|
# For each row in the result set, yield a hash with column name symbol
|
|
@@ -131,12 +131,24 @@ module Sequel
|
|
|
131
131
|
super
|
|
132
132
|
end
|
|
133
133
|
|
|
134
|
+
# Insert data from the current table into the new table after
|
|
135
|
+
# creating the table, since it is not possible to do it in one step.
|
|
136
|
+
def create_table_as(name, sql, options)
|
|
137
|
+
super
|
|
138
|
+
from(name).insert(sql.is_a?(Dataset) ? sql : dataset.with_sql(sql))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# DB2 requires parens around the SELECT, and DEFINITION ONLY at the end.
|
|
142
|
+
def create_table_as_sql(name, sql, options)
|
|
143
|
+
"#{create_table_prefix_sql(name, options)} AS (#{sql}) DEFINITION ONLY"
|
|
144
|
+
end
|
|
145
|
+
|
|
134
146
|
# Here we use DGTT which has most backward compatibility, which uses
|
|
135
147
|
# DECLARE instead of CREATE. CGTT can only be used after version 9.7.
|
|
136
148
|
# http://www.ibm.com/developerworks/data/library/techarticle/dm-0912globaltemptable/
|
|
137
|
-
def
|
|
149
|
+
def create_table_prefix_sql(name, options)
|
|
138
150
|
if options[:temp]
|
|
139
|
-
"DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)}
|
|
151
|
+
"DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)}"
|
|
140
152
|
else
|
|
141
153
|
super
|
|
142
154
|
end
|
|
@@ -221,6 +233,8 @@ module Sequel
|
|
|
221
233
|
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
222
234
|
when :>>
|
|
223
235
|
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
236
|
+
when :%
|
|
237
|
+
sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
|
|
224
238
|
when :'B~'
|
|
225
239
|
literal_append(sql, SQL::Function.new(:BITNOT, *args))
|
|
226
240
|
when :extract
|