sequel 3.0.0 → 3.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.
- data/CHANGELOG +100 -0
- data/README.rdoc +3 -3
- data/bin/sequel +102 -19
- data/doc/reflection.rdoc +83 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/lib/sequel/adapters/ado.rb +11 -0
- data/lib/sequel/adapters/amalgalite.rb +5 -20
- data/lib/sequel/adapters/do.rb +44 -36
- data/lib/sequel/adapters/firebird.rb +29 -43
- data/lib/sequel/adapters/jdbc.rb +17 -27
- data/lib/sequel/adapters/mysql.rb +35 -40
- data/lib/sequel/adapters/odbc.rb +4 -23
- data/lib/sequel/adapters/oracle.rb +22 -19
- data/lib/sequel/adapters/postgres.rb +6 -15
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +29 -10
- data/lib/sequel/adapters/shared/oracle.rb +6 -8
- data/lib/sequel/adapters/shared/postgres.rb +28 -72
- data/lib/sequel/adapters/shared/sqlite.rb +5 -3
- data/lib/sequel/adapters/sqlite.rb +5 -20
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +80 -0
- data/lib/sequel/adapters/utils/unsupported.rb +0 -12
- data/lib/sequel/core.rb +12 -3
- data/lib/sequel/core_sql.rb +1 -8
- data/lib/sequel/database.rb +107 -43
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +38 -4
- data/lib/sequel/dataset.rb +6 -0
- data/lib/sequel/dataset/convenience.rb +2 -2
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/prepared_statements.rb +3 -8
- data/lib/sequel/dataset/sql.rb +93 -19
- data/lib/sequel/extensions/blank.rb +2 -1
- data/lib/sequel/extensions/inflector.rb +4 -3
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +100 -24
- data/lib/sequel/extensions/string_date_time.rb +3 -4
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +96 -38
- data/lib/sequel/model/base.rb +14 -14
- data/lib/sequel/model/plugins.rb +32 -21
- data/lib/sequel/plugins/caching.rb +13 -15
- data/lib/sequel/plugins/identity_map.rb +107 -0
- data/lib/sequel/plugins/lazy_attributes.rb +65 -0
- data/lib/sequel/plugins/many_through_many.rb +188 -0
- data/lib/sequel/plugins/schema.rb +13 -0
- data/lib/sequel/plugins/serialization.rb +53 -37
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/validation_class_methods.rb +28 -7
- data/lib/sequel/plugins/validation_helpers.rb +31 -24
- data/lib/sequel/sql.rb +16 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +47 -1
- data/spec/adapters/firebird_spec.rb +39 -36
- data/spec/adapters/mysql_spec.rb +25 -9
- data/spec/adapters/postgres_spec.rb +11 -24
- data/spec/core/database_spec.rb +54 -13
- data/spec/core/dataset_spec.rb +147 -29
- data/spec/core/object_graph_spec.rb +6 -1
- data/spec/core/schema_spec.rb +34 -0
- data/spec/core/spec_helper.rb +0 -2
- data/spec/extensions/caching_spec.rb +7 -0
- data/spec/extensions/identity_map_spec.rb +158 -0
- data/spec/extensions/lazy_attributes_spec.rb +113 -0
- data/spec/extensions/many_through_many_spec.rb +813 -0
- data/spec/extensions/migration_spec.rb +4 -4
- data/spec/extensions/schema_dumper_spec.rb +114 -13
- data/spec/extensions/schema_spec.rb +19 -3
- data/spec/extensions/serialization_spec.rb +28 -0
- data/spec/extensions/single_table_inheritance_spec.rb +25 -1
- data/spec/extensions/spec_helper.rb +2 -7
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +10 -5
- data/spec/integration/dataset_test.rb +39 -6
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/spec_helper.rb +0 -1
- data/spec/integration/transaction_test.rb +28 -1
- data/spec/model/association_reflection_spec.rb +29 -3
- data/spec/model/associations_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +70 -1
- data/spec/model/plugins_spec.rb +236 -50
- data/spec/model/spec_helper.rb +0 -2
- metadata +18 -5
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -63,30 +63,11 @@ module Sequel
|
|
63
63
|
end
|
64
64
|
alias_method :do, :execute_dui
|
65
65
|
|
66
|
-
# Support single level transactions on ODBC
|
67
|
-
def transaction(opts={})
|
68
|
-
synchronize(opts[:server]) do |conn|
|
69
|
-
return yield(conn) if @transactions.include?(Thread.current)
|
70
|
-
log_info(begin_transaction_sql)
|
71
|
-
conn.do(begin_transaction_sql)
|
72
|
-
begin
|
73
|
-
@transactions << Thread.current
|
74
|
-
yield(conn)
|
75
|
-
rescue ::Exception => e
|
76
|
-
log_info(rollback_transaction_sql)
|
77
|
-
conn.do(rollback_transaction_sql)
|
78
|
-
transaction_error(e)
|
79
|
-
ensure
|
80
|
-
unless e
|
81
|
-
log_info(commit_transaction_sql)
|
82
|
-
conn.do(commit_transaction_sql)
|
83
|
-
end
|
84
|
-
@transactions.delete(Thread.current)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
66
|
private
|
67
|
+
|
68
|
+
def connection_execute_method
|
69
|
+
:do
|
70
|
+
end
|
90
71
|
|
91
72
|
def disconnect_connection(c)
|
92
73
|
c.disconnect
|
@@ -75,31 +75,34 @@ module Sequel
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
78
|
-
|
79
|
-
|
80
|
-
def transaction(opts={})
|
81
|
-
synchronize(opts[:server]) do |conn|
|
82
|
-
return yield(conn) if @transactions.include?(Thread.current)
|
83
|
-
conn.autocommit = false
|
84
|
-
begin
|
85
|
-
@transactions << Thread.current
|
86
|
-
yield(conn)
|
87
|
-
rescue => e
|
88
|
-
conn.rollback
|
89
|
-
raise e unless Rollback === e
|
90
|
-
ensure
|
91
|
-
conn.commit unless e
|
92
|
-
conn.autocommit = true
|
93
|
-
@transactions.delete(Thread.current)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
78
|
+
alias_method :do, :execute
|
97
79
|
|
98
80
|
private
|
81
|
+
|
82
|
+
def begin_transaction(conn)
|
83
|
+
log_info(TRANSACTION_BEGIN)
|
84
|
+
conn.autocommit = false
|
85
|
+
conn
|
86
|
+
end
|
87
|
+
|
88
|
+
def commit_transaction(conn)
|
89
|
+
log_info(TRANSACTION_COMMIT)
|
90
|
+
conn.commit
|
91
|
+
end
|
99
92
|
|
100
93
|
def disconnect_connection(c)
|
101
94
|
c.logoff
|
102
95
|
end
|
96
|
+
|
97
|
+
def remove_transaction(conn)
|
98
|
+
conn.autocommit = true
|
99
|
+
super
|
100
|
+
end
|
101
|
+
|
102
|
+
def rollback_transaction(conn)
|
103
|
+
log_info(TRANSACTION_ROLLBACK)
|
104
|
+
conn.rollback
|
105
|
+
end
|
103
106
|
end
|
104
107
|
|
105
108
|
class Dataset < Sequel::Dataset
|
@@ -115,6 +115,11 @@ module Sequel
|
|
115
115
|
include Sequel::Postgres::AdapterMethods
|
116
116
|
self.translate_results = false if respond_to?(:translate_results=)
|
117
117
|
|
118
|
+
# Hash of prepared statements for this connection. Keys are
|
119
|
+
# string names of the server side prepared statement, and values
|
120
|
+
# are SQL strings.
|
121
|
+
attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
|
122
|
+
|
118
123
|
# Apply connection settings for this connection. Current sets
|
119
124
|
# the date style to ISO in order make Date object creation in ruby faster,
|
120
125
|
# if Postgres.use_iso_date_format is true.
|
@@ -125,6 +130,7 @@ module Sequel
|
|
125
130
|
@db.log_info(sql)
|
126
131
|
execute(sql)
|
127
132
|
end
|
133
|
+
@prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
|
128
134
|
end
|
129
135
|
|
130
136
|
# Execute the given SQL with this connection. If a block is given,
|
@@ -151,21 +157,6 @@ module Sequel
|
|
151
157
|
end
|
152
158
|
end
|
153
159
|
|
154
|
-
# Reapply the connection settings if the connection is reset.
|
155
|
-
def reset(*args, &block)
|
156
|
-
super(*args, &block)
|
157
|
-
apply_connection_settings
|
158
|
-
end
|
159
|
-
|
160
|
-
if SEQUEL_POSTGRES_USES_PG
|
161
|
-
# Hash of prepared statements for this connection. Keys are
|
162
|
-
# string names of the server side prepared statement, and values
|
163
|
-
# are SQL strings.
|
164
|
-
def prepared_statements
|
165
|
-
@prepared_statements ||= {}
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
160
|
private
|
170
161
|
|
171
162
|
# Return the requested values for the given row.
|
@@ -67,7 +67,7 @@ module Sequel
|
|
67
67
|
|
68
68
|
def multi_insert_sql(columns, values)
|
69
69
|
values = values.map {|r| "SELECT #{expression_list(r)}" }.join(" UNION ALL ")
|
70
|
-
["
|
70
|
+
["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) #{values}"]
|
71
71
|
end
|
72
72
|
|
73
73
|
# Allows you to do .nolock on a query
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Sequel.require 'adapters/utils
|
1
|
+
Sequel.require %w'unsupported savepoint_transactions', 'adapters/utils'
|
2
2
|
|
3
3
|
module Sequel
|
4
4
|
class Database
|
@@ -14,6 +14,8 @@ module Sequel
|
|
14
14
|
# Methods shared by Database instances that connect to MySQL,
|
15
15
|
# currently supported by the native and JDBC adapters.
|
16
16
|
module DatabaseMethods
|
17
|
+
include Sequel::Database::SavepointTransactions
|
18
|
+
|
17
19
|
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
18
20
|
CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
|
19
21
|
PRIMARY = 'PRIMARY'.freeze
|
@@ -33,17 +35,22 @@ module Sequel
|
|
33
35
|
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
34
36
|
# is an array of symbols of column names. The value of :unique is true or false
|
35
37
|
# depending on if the index is unique.
|
38
|
+
#
|
39
|
+
# Does not include the primary key index or indexes on partial keys.
|
36
40
|
def indexes(table)
|
37
41
|
indexes = {}
|
42
|
+
remove_indexes = []
|
38
43
|
m = output_identifier_meth
|
39
44
|
im = input_identifier_meth
|
40
45
|
metadata_dataset.with_sql("SHOW INDEX FROM ?", SQL::Identifier.new(im.call(table))).each do |r|
|
41
46
|
name = r[:Key_name]
|
42
47
|
next if name == PRIMARY
|
43
|
-
|
48
|
+
name = m.call(name)
|
49
|
+
remove_indexes << name if r[:Sub_part]
|
50
|
+
i = indexes[name] ||= {:columns=>[], :unique=>r[:Non_unique] != 1}
|
44
51
|
i[:columns] << m.call(r[:Column_name])
|
45
52
|
end
|
46
|
-
indexes
|
53
|
+
indexes.reject{|k,v| remove_indexes.include?(k)}
|
47
54
|
end
|
48
55
|
|
49
56
|
# Get version of MySQL server, used for determined capabilities.
|
@@ -66,7 +73,7 @@ module Sequel
|
|
66
73
|
def use(db_name)
|
67
74
|
disconnect
|
68
75
|
@opts[:database] = db_name if self << "USE #{db_name}"
|
69
|
-
@schemas =
|
76
|
+
@schemas = {}
|
70
77
|
self
|
71
78
|
end
|
72
79
|
|
@@ -105,6 +112,13 @@ module Sequel
|
|
105
112
|
AUTO_INCREMENT
|
106
113
|
end
|
107
114
|
|
115
|
+
# MySQL doesn't allow default values on text columns, so ignore if it the
|
116
|
+
# generic text type is used
|
117
|
+
def column_definition_sql(column)
|
118
|
+
column.delete(:default) if column[:type] == File || (column[:type] == String && column[:text] == true)
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
108
122
|
# Handle MySQL specific syntax for column references
|
109
123
|
def column_references_sql(column)
|
110
124
|
"#{", FOREIGN KEY (#{quote_identifier(column[:name])})" unless column[:type] == :check}#{super(column)}"
|
@@ -238,11 +252,6 @@ module Sequel
|
|
238
252
|
_filter(:having, *cond, &block)
|
239
253
|
end
|
240
254
|
|
241
|
-
# MySQL doesn't use the SQL standard DEFAULT VALUES.
|
242
|
-
def insert_default_values_sql
|
243
|
-
"INSERT INTO #{source_list(@opts[:from])} () VALUES ()"
|
244
|
-
end
|
245
|
-
|
246
255
|
# Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.
|
247
256
|
# Raises an error on use of :full_outer type, since MySQL doesn't support it.
|
248
257
|
def join_table(type, table, expr=nil, table_alias={})
|
@@ -312,7 +321,7 @@ module Sequel
|
|
312
321
|
update_string = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})"}.join(COMMA_SEPARATOR)
|
313
322
|
end
|
314
323
|
values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
|
315
|
-
["
|
324
|
+
["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{" ON DUPLICATE KEY UPDATE #{update_string}" if update_string}"]
|
316
325
|
end
|
317
326
|
|
318
327
|
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
|
@@ -366,6 +375,16 @@ module Sequel
|
|
366
375
|
|
367
376
|
private
|
368
377
|
|
378
|
+
# MySQL supports INSERT IGNORE INTO
|
379
|
+
def insert_sql_base
|
380
|
+
"INSERT #{'IGNORE ' if opts[:insert_ignore]}INTO "
|
381
|
+
end
|
382
|
+
|
383
|
+
# MySQL doesn't use the SQL standard DEFAULT VALUES.
|
384
|
+
def insert_default_values_sql
|
385
|
+
"#{insert_sql_base}#{source_list(@opts[:from])} () VALUES ()"
|
386
|
+
end
|
387
|
+
|
369
388
|
# Use MySQL Timestamp format
|
370
389
|
def literal_datetime(v)
|
371
390
|
v.strftime(TIMESTAMP_FORMAT)
|
@@ -10,14 +10,6 @@ module Sequel
|
|
10
10
|
self << create_sequence_sql(name, opts)
|
11
11
|
end
|
12
12
|
|
13
|
-
def create_table(name, options={}, &block)
|
14
|
-
options = {:generator=>options} if options.is_a?(Schema::Generator)
|
15
|
-
generator = options[:generator] || Schema::Generator.new(self, &block)
|
16
|
-
drop_statement, create_statements = create_table_sql_list(name, generator, options)
|
17
|
-
(execute_ddl(drop_statement) rescue nil) if drop_statement
|
18
|
-
(create_statements + index_sql_list(name, generator.indexes)).each{|sql| execute_ddl(sql)}
|
19
|
-
end
|
20
|
-
|
21
13
|
def create_trigger(*args)
|
22
14
|
self << create_trigger_sql(*args)
|
23
15
|
end
|
@@ -55,6 +47,12 @@ module Sequel
|
|
55
47
|
"CREATE SEQUENCE #{quote_identifier(name)} start with #{opts [:start_with]||1} increment by #{opts[:increment_by]||1} nomaxvalue"
|
56
48
|
end
|
57
49
|
|
50
|
+
def create_table_from_generator(name, generator, options)
|
51
|
+
drop_statement, create_statements = create_table_sql_list(name, generator, options)
|
52
|
+
(execute_ddl(drop_statement) rescue nil) if drop_statement
|
53
|
+
create_statements.each{|sql| execute_ddl(sql)}
|
54
|
+
end
|
55
|
+
|
58
56
|
def create_table_sql_list(name, generator, options={})
|
59
57
|
statements = [create_table_sql(name, generator, options)]
|
60
58
|
drop_seq_statement = nil
|
@@ -1,3 +1,5 @@
|
|
1
|
+
Sequel.require 'adapters/utils/savepoint_transactions'
|
2
|
+
|
1
3
|
module Sequel
|
2
4
|
# Top level module for holding all PostgreSQL-related modules and classes
|
3
5
|
# for Sequel. There are a few module level accessors that are added via
|
@@ -163,14 +165,13 @@ module Sequel
|
|
163
165
|
|
164
166
|
# Methods shared by Database instances that connect to PostgreSQL.
|
165
167
|
module DatabaseMethods
|
168
|
+
include Sequel::Database::SavepointTransactions
|
169
|
+
|
166
170
|
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
167
171
|
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
168
172
|
SQL_BEGIN = 'BEGIN'.freeze
|
169
|
-
SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
|
170
173
|
SQL_COMMIT = 'COMMIT'.freeze
|
171
|
-
SQL_ROLLBACK_TO_SAVEPOINT = 'ROLLBACK TO SAVEPOINT autopoint_%d'.freeze
|
172
174
|
SQL_ROLLBACK = 'ROLLBACK'.freeze
|
173
|
-
SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
|
174
175
|
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
175
176
|
|
176
177
|
# Creates the function in the database. Arguments:
|
@@ -269,6 +270,9 @@ module Sequel
|
|
269
270
|
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
270
271
|
# is an array of symbols of column names. The value of :unique is true or false
|
271
272
|
# depending on if the index is unique.
|
273
|
+
#
|
274
|
+
# This will not output any partial indexes, indexes that aren't valid or ready,
|
275
|
+
# or indexes that contain anything other than simple column references.
|
272
276
|
def indexes(table)
|
273
277
|
m = output_identifier_meth
|
274
278
|
im = input_identifier_meth
|
@@ -278,8 +282,7 @@ module Sequel
|
|
278
282
|
join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
|
279
283
|
join(:pg_class___indc, :oid=>:indexrelid).
|
280
284
|
join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>SQL::Function.new(:ANY, :ind__indkey)).
|
281
|
-
filter(:indc__relkind=>'i', :ind__indisprimary=>false).
|
282
|
-
exclude(0=>SQL::Function.new(:ANY, :ind__indkey)).
|
285
|
+
filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true, :indisready=>true, :indcheckxmin=>false).
|
283
286
|
order(:indc__relname, (0...32).map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
|
284
287
|
select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
|
285
288
|
|
@@ -324,7 +327,7 @@ module Sequel
|
|
324
327
|
# maximum current value of the table's primary key.
|
325
328
|
def reset_primary_key_sequence(table)
|
326
329
|
pk = SQL::Identifier.new(primary_key(table))
|
327
|
-
seq = primary_key_sequence(table)
|
330
|
+
return unless seq = primary_key_sequence(table)
|
328
331
|
db = self
|
329
332
|
seq_ds = db.from(seq.lit)
|
330
333
|
get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
|
@@ -369,60 +372,10 @@ module Sequel
|
|
369
372
|
# * :schema - The schema to search (default_schema by default)
|
370
373
|
# * :server - The server to use
|
371
374
|
def tables(opts={})
|
372
|
-
ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server])
|
373
|
-
ds.join!(:pg_namespace, :oid=>:relnamespace, :nspname=>(opts[:schema]||default_schema).to_s) if opts[:schema] || default_schema
|
375
|
+
ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace, :nspname=>(opts[:schema]||default_schema||'public').to_s)
|
374
376
|
m = output_identifier_meth
|
375
377
|
block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
|
376
378
|
end
|
377
|
-
|
378
|
-
# PostgreSQL supports multi-level transactions using save points.
|
379
|
-
# To use a savepoint instead of reusing the current transaction,
|
380
|
-
# use the :savepoint=>true option.
|
381
|
-
def transaction(opts={})
|
382
|
-
synchronize(opts[:server]) do |conn|
|
383
|
-
return yield(conn) if @transactions.include?(Thread.current) and !opts[:savepoint]
|
384
|
-
conn.transaction_depth ||= 0
|
385
|
-
if conn.transaction_depth > 0
|
386
|
-
log_info(SQL_SAVEPOINT % conn.transaction_depth)
|
387
|
-
conn.execute(SQL_SAVEPOINT % conn.transaction_depth)
|
388
|
-
else
|
389
|
-
log_info(SQL_BEGIN)
|
390
|
-
conn.execute(SQL_BEGIN)
|
391
|
-
end
|
392
|
-
begin
|
393
|
-
conn.transaction_depth += 1
|
394
|
-
@transactions << Thread.current
|
395
|
-
yield conn
|
396
|
-
rescue ::Exception => e
|
397
|
-
if conn.transaction_depth > 1
|
398
|
-
log_info(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
|
399
|
-
conn.execute(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
|
400
|
-
else
|
401
|
-
log_info(SQL_ROLLBACK)
|
402
|
-
conn.execute(SQL_ROLLBACK) rescue nil
|
403
|
-
@transactions.delete(Thread.current)
|
404
|
-
end
|
405
|
-
transaction_error(e, *CONVERTED_EXCEPTIONS)
|
406
|
-
ensure
|
407
|
-
unless e
|
408
|
-
begin
|
409
|
-
if conn.transaction_depth > 1
|
410
|
-
log_info(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
411
|
-
conn.execute(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
412
|
-
else
|
413
|
-
log_info(SQL_COMMIT)
|
414
|
-
conn.execute(SQL_COMMIT)
|
415
|
-
@transactions.delete(Thread.current)
|
416
|
-
end
|
417
|
-
rescue => e
|
418
|
-
log_info(e.message)
|
419
|
-
raise_error(e, :classes=>CONVERTED_EXCEPTIONS)
|
420
|
-
end
|
421
|
-
end
|
422
|
-
conn.transaction_depth -= 1
|
423
|
-
end
|
424
|
-
end
|
425
|
-
end
|
426
379
|
|
427
380
|
private
|
428
381
|
|
@@ -459,6 +412,11 @@ module Sequel
|
|
459
412
|
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
460
413
|
end
|
461
414
|
|
415
|
+
# The errors that the main adapters can raise, depends on the adapter being used
|
416
|
+
def database_error_classes
|
417
|
+
CONVERTED_EXCEPTIONS
|
418
|
+
end
|
419
|
+
|
462
420
|
# SQL for dropping a function from the database.
|
463
421
|
def drop_function_sql(name, opts={})
|
464
422
|
"DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
|
@@ -694,11 +652,12 @@ module Sequel
|
|
694
652
|
|
695
653
|
# Insert given values into the database.
|
696
654
|
def insert(*values)
|
697
|
-
if
|
698
|
-
|
655
|
+
if @opts[:sql]
|
656
|
+
execute_insert(insert_sql(*values))
|
657
|
+
elsif @opts[:disable_insert_returning] || server_version < 80200
|
658
|
+
execute_insert(insert_sql(*values), :table=>opts[:from].first, :values=>values.size == 1 ? values.first : values)
|
699
659
|
else
|
700
|
-
|
701
|
-
:values=>values.size == 1 ? values.first : values)
|
660
|
+
clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
|
702
661
|
end
|
703
662
|
end
|
704
663
|
|
@@ -731,50 +690,47 @@ module Sequel
|
|
731
690
|
|
732
691
|
# postgresql 8.2 introduces support for multi-row insert
|
733
692
|
values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
|
734
|
-
["
|
693
|
+
["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
|
735
694
|
end
|
736
695
|
|
737
696
|
private
|
738
697
|
|
739
698
|
# Use the RETURNING clause to return the primary key of the inserted record, if it exists
|
740
699
|
def insert_returning_pk_sql(*values)
|
741
|
-
pk = db.primary_key(opts[:from].first)
|
700
|
+
pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
|
742
701
|
insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
|
743
702
|
end
|
744
703
|
|
704
|
+
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
745
705
|
def literal_blob(v)
|
746
706
|
"'#{v.gsub(/[\000-\037\047\134\177-\377]/){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"}}'"
|
747
707
|
end
|
748
708
|
|
709
|
+
# PostgreSQL supports fractional timestamps
|
749
710
|
def literal_datetime(v)
|
750
711
|
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
|
751
712
|
end
|
752
713
|
|
714
|
+
# PostgreSQL uses FALSE for false values
|
753
715
|
def literal_false
|
754
716
|
BOOL_FALSE
|
755
717
|
end
|
756
718
|
|
719
|
+
# Assume that SQL standard quoting is on, per Sequel's defaults
|
757
720
|
def literal_string(v)
|
758
721
|
"'#{v.gsub("'", "''")}'"
|
759
722
|
end
|
760
723
|
|
724
|
+
# PostgreSQL uses FALSE for false values
|
761
725
|
def literal_true
|
762
726
|
BOOL_TRUE
|
763
727
|
end
|
764
728
|
|
729
|
+
# PostgreSQL supports fractional times
|
765
730
|
def literal_time(v)
|
766
731
|
"#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
|
767
732
|
end
|
768
733
|
|
769
|
-
# PostgreSQL is smart and can use parantheses around all datasets to get
|
770
|
-
# the correct answers.
|
771
|
-
def select_compounds_sql(sql)
|
772
|
-
return unless @opts[:compounds]
|
773
|
-
@opts[:compounds].each do |type, dataset, all|
|
774
|
-
sql.replace("(#{sql} #{type.to_s.upcase}#{' ALL' if all} #{subselect_sql(dataset)})")
|
775
|
-
end
|
776
|
-
end
|
777
|
-
|
778
734
|
# The order of clauses in the SELECT SQL statement
|
779
735
|
def select_clause_order
|
780
736
|
SELECT_CLAUSE_ORDER
|