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