epugh-sequel 0.0.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/README.rdoc +652 -0
- data/VERSION.yml +4 -0
- data/bin/sequel +104 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +85 -0
- data/lib/sequel/adapters/db2.rb +132 -0
- data/lib/sequel/adapters/dbi.rb +101 -0
- data/lib/sequel/adapters/do.rb +197 -0
- data/lib/sequel/adapters/do/mysql.rb +38 -0
- data/lib/sequel/adapters/do/postgres.rb +92 -0
- data/lib/sequel/adapters/do/sqlite.rb +31 -0
- data/lib/sequel/adapters/firebird.rb +307 -0
- data/lib/sequel/adapters/informix.rb +75 -0
- data/lib/sequel/adapters/jdbc.rb +485 -0
- data/lib/sequel/adapters/jdbc/h2.rb +62 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
- data/lib/sequel/adapters/mysql.rb +370 -0
- data/lib/sequel/adapters/odbc.rb +184 -0
- data/lib/sequel/adapters/openbase.rb +57 -0
- data/lib/sequel/adapters/oracle.rb +140 -0
- data/lib/sequel/adapters/postgres.rb +453 -0
- data/lib/sequel/adapters/shared/mssql.rb +93 -0
- data/lib/sequel/adapters/shared/mysql.rb +341 -0
- data/lib/sequel/adapters/shared/oracle.rb +62 -0
- data/lib/sequel/adapters/shared/postgres.rb +743 -0
- data/lib/sequel/adapters/shared/progress.rb +34 -0
- data/lib/sequel/adapters/shared/sqlite.rb +263 -0
- data/lib/sequel/adapters/sqlite.rb +243 -0
- data/lib/sequel/adapters/utils/date_format.rb +21 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
- data/lib/sequel/adapters/utils/unsupported.rb +62 -0
- data/lib/sequel/connection_pool.rb +258 -0
- data/lib/sequel/core.rb +204 -0
- data/lib/sequel/core_sql.rb +185 -0
- data/lib/sequel/database.rb +687 -0
- data/lib/sequel/database/schema_generator.rb +324 -0
- data/lib/sequel/database/schema_methods.rb +164 -0
- data/lib/sequel/database/schema_sql.rb +324 -0
- data/lib/sequel/dataset.rb +422 -0
- data/lib/sequel/dataset/convenience.rb +237 -0
- data/lib/sequel/dataset/prepared_statements.rb +220 -0
- data/lib/sequel/dataset/sql.rb +1105 -0
- data/lib/sequel/deprecated.rb +529 -0
- data/lib/sequel/exceptions.rb +44 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/sequel/extensions/inflector.rb +288 -0
- data/lib/sequel/extensions/pagination.rb +96 -0
- data/lib/sequel/extensions/pretty_table.rb +78 -0
- data/lib/sequel/extensions/query.rb +48 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +44 -0
- data/lib/sequel/migration.rb +212 -0
- data/lib/sequel/model.rb +142 -0
- data/lib/sequel/model/association_reflection.rb +263 -0
- data/lib/sequel/model/associations.rb +1024 -0
- data/lib/sequel/model/base.rb +911 -0
- data/lib/sequel/model/deprecated.rb +188 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +384 -0
- data/lib/sequel/model/errors.rb +37 -0
- data/lib/sequel/model/exceptions.rb +7 -0
- data/lib/sequel/model/inflections.rb +230 -0
- data/lib/sequel/model/plugins.rb +74 -0
- data/lib/sequel/object_graph.rb +230 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +373 -0
- data/lib/sequel/sql.rb +854 -0
- data/lib/sequel/version.rb +11 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/ado_spec.rb +46 -0
- data/spec/adapters/firebird_spec.rb +376 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +875 -0
- data/spec/adapters/oracle_spec.rb +272 -0
- data/spec/adapters/postgres_spec.rb +692 -0
- data/spec/adapters/spec_helper.rb +10 -0
- data/spec/adapters/sqlite_spec.rb +550 -0
- data/spec/core/connection_pool_spec.rb +526 -0
- data/spec/core/core_ext_spec.rb +156 -0
- data/spec/core/core_sql_spec.rb +528 -0
- data/spec/core/database_spec.rb +1214 -0
- data/spec/core/dataset_spec.rb +3513 -0
- data/spec/core/expression_filters_spec.rb +363 -0
- data/spec/core/migration_spec.rb +261 -0
- data/spec/core/object_graph_spec.rb +280 -0
- data/spec/core/pretty_table_spec.rb +58 -0
- data/spec/core/schema_generator_spec.rb +167 -0
- data/spec/core/schema_spec.rb +778 -0
- data/spec/core/spec_helper.rb +82 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/inflector_spec.rb +122 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/schema_spec.rb +111 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/extensions/spec_helper.rb +90 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/validation_class_methods_spec.rb +1054 -0
- data/spec/integration/dataset_test.rb +160 -0
- data/spec/integration/eager_loader_test.rb +683 -0
- data/spec/integration/prepared_statement_test.rb +130 -0
- data/spec/integration/schema_test.rb +183 -0
- data/spec/integration/spec_helper.rb +75 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +93 -0
- data/spec/model/associations_spec.rb +1780 -0
- data/spec/model/base_spec.rb +494 -0
- data/spec/model/caching_spec.rb +217 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1165 -0
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/model/model_spec.rb +588 -0
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/model/record_spec.rb +1243 -0
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +202 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Sequel.require 'adapters/utils/unsupported'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module MSSQL
|
|
5
|
+
module DatabaseMethods
|
|
6
|
+
AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
|
|
7
|
+
SQL_BEGIN = "BEGIN TRANSACTION".freeze
|
|
8
|
+
SQL_COMMIT = "COMMIT TRANSACTION".freeze
|
|
9
|
+
SQL_ROLLBACK = "ROLLBACK TRANSACTION".freeze
|
|
10
|
+
|
|
11
|
+
def auto_increment_sql
|
|
12
|
+
AUTO_INCREMENT
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def dataset(opts = nil)
|
|
16
|
+
ds = super
|
|
17
|
+
ds.extend(DatasetMethods)
|
|
18
|
+
ds
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# SQL to BEGIN a transaction.
|
|
24
|
+
def begin_transaction_sql
|
|
25
|
+
SQL_BEGIN
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# SQL to COMMIT a transaction.
|
|
29
|
+
def commit_transaction_sql
|
|
30
|
+
SQL_COMMIT
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# SQL to ROLLBACK a transaction.
|
|
34
|
+
def rollback_transaction_sql
|
|
35
|
+
SQL_ROLLBACK
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
module DatasetMethods
|
|
40
|
+
include Dataset::UnsupportedIntersectExcept
|
|
41
|
+
|
|
42
|
+
SELECT_CLAUSE_ORDER = %w'limit distinct columns from with join where group order having compounds'.freeze
|
|
43
|
+
|
|
44
|
+
def complex_expression_sql(op, args)
|
|
45
|
+
case op
|
|
46
|
+
when :'||'
|
|
47
|
+
super(:+, args)
|
|
48
|
+
else
|
|
49
|
+
super(op, args)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def full_text_search(cols, terms, opts = {})
|
|
54
|
+
filter("CONTAINS (#{literal(cols)}, #{literal(terms)})")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def multi_insert_sql(columns, values)
|
|
58
|
+
values = values.map {|r| "SELECT #{expression_list(r)}" }.join(" UNION ALL ")
|
|
59
|
+
["INSERT INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) #{values}"]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Allows you to do .nolock on a query
|
|
63
|
+
def nolock
|
|
64
|
+
clone(:with => "(NOLOCK)")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def quoted_identifier(name)
|
|
68
|
+
"[#{name}]"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def literal_string(v)
|
|
74
|
+
"N#{super}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def select_clause_order
|
|
78
|
+
SELECT_CLAUSE_ORDER
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# MSSQL uses TOP for limit, with no offset support
|
|
82
|
+
def select_limit_sql(sql, opts)
|
|
83
|
+
raise(Error, "OFFSET not supported") if opts[:offset]
|
|
84
|
+
sql << " TOP #{opts[:limit]}" if opts[:limit]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# MSSQL uses the WITH statement to lock tables
|
|
88
|
+
def select_with_sql(sql, opts)
|
|
89
|
+
sql << " WITH #{opts[:with]}" if opts[:with]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
Sequel.require 'adapters/utils/unsupported'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
class Database
|
|
5
|
+
# Keep default column_references_sql for add_foreign_key support
|
|
6
|
+
alias default_column_references_sql column_references_sql
|
|
7
|
+
end
|
|
8
|
+
module MySQL
|
|
9
|
+
class << self
|
|
10
|
+
# Set the default options used for CREATE TABLE
|
|
11
|
+
attr_accessor :default_charset, :default_collate, :default_engine
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Methods shared by Database instances that connect to MySQL,
|
|
15
|
+
# currently supported by the native and JDBC adapters.
|
|
16
|
+
module DatabaseMethods
|
|
17
|
+
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
|
18
|
+
NOT_NULL = Sequel::Database::NOT_NULL
|
|
19
|
+
NULL = Sequel::Database::NULL
|
|
20
|
+
PRIMARY_KEY = Sequel::Database::PRIMARY_KEY
|
|
21
|
+
TYPES = Sequel::Database::TYPES.merge(DateTime=>'datetime', \
|
|
22
|
+
TrueClass=>'tinyint', FalseClass=>'tinyint')
|
|
23
|
+
UNIQUE = Sequel::Database::UNIQUE
|
|
24
|
+
UNSIGNED = Sequel::Database::UNSIGNED
|
|
25
|
+
|
|
26
|
+
# Use MySQL specific syntax for rename column, set column type, and
|
|
27
|
+
# drop index cases.
|
|
28
|
+
def alter_table_sql(table, op)
|
|
29
|
+
case op[:op]
|
|
30
|
+
when :add_column
|
|
31
|
+
if related = op.delete(:table)
|
|
32
|
+
sql = super(table, op)
|
|
33
|
+
op[:table] = related
|
|
34
|
+
[sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{default_column_references_sql(op)}"]
|
|
35
|
+
else
|
|
36
|
+
super(table, op)
|
|
37
|
+
end
|
|
38
|
+
when :rename_column
|
|
39
|
+
"ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{quote_identifier(op[:new_name])} #{type_literal(op)}"
|
|
40
|
+
when :set_column_type
|
|
41
|
+
"ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{quote_identifier(op[:name])} #{type_literal(op)}"
|
|
42
|
+
when :drop_index
|
|
43
|
+
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
|
|
44
|
+
else
|
|
45
|
+
super(table, op)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Use MySQL specific AUTO_INCREMENT text.
|
|
50
|
+
def auto_increment_sql
|
|
51
|
+
AUTO_INCREMENT
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Handle MySQL specific syntax for column references
|
|
55
|
+
def column_references_sql(column)
|
|
56
|
+
"#{", FOREIGN KEY (#{quote_identifier(column[:name])})" unless column[:type] == :check}#{super(column)}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Use MySQL specific syntax for engine type and character encoding
|
|
60
|
+
def create_table_sql_list(name, columns, indexes = nil, options = {})
|
|
61
|
+
options[:engine] = Sequel::MySQL.default_engine unless options.include?(:engine)
|
|
62
|
+
options[:charset] = Sequel::MySQL.default_charset unless options.include?(:charset)
|
|
63
|
+
options[:collate] = Sequel::MySQL.default_collate unless options.include?(:collate)
|
|
64
|
+
sql = ["CREATE TABLE #{quote_schema_table(name)} (#{column_list_sql(columns)})#{" ENGINE=#{options[:engine]}" if options[:engine]}#{" DEFAULT CHARSET=#{options[:charset]}" if options[:charset]}#{" DEFAULT COLLATE=#{options[:collate]}" if options[:collate]}"]
|
|
65
|
+
sql.concat(index_list_sql_list(name, indexes)) if indexes && !indexes.empty?
|
|
66
|
+
sql
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Handle MySQL specific index SQL syntax
|
|
70
|
+
def index_definition_sql(table_name, index)
|
|
71
|
+
index_name = quote_identifier(index[:name] || default_index_name(table_name, index[:columns]))
|
|
72
|
+
index_type = case index[:type]
|
|
73
|
+
when :full_text
|
|
74
|
+
"FULLTEXT "
|
|
75
|
+
when :spatial
|
|
76
|
+
"SPATIAL "
|
|
77
|
+
else
|
|
78
|
+
using = " USING #{index[:type]}" unless index[:type] == nil
|
|
79
|
+
"UNIQUE " if index[:unique]
|
|
80
|
+
end
|
|
81
|
+
"CREATE #{index_type}INDEX #{index_name} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}#{using}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Get version of MySQL server, used for determined capabilities.
|
|
85
|
+
def server_version
|
|
86
|
+
m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
|
|
87
|
+
@server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Return an array of symbols specifying table names in the current database.
|
|
91
|
+
#
|
|
92
|
+
# Options:
|
|
93
|
+
# * :server - Set the server to use
|
|
94
|
+
def tables(opts={})
|
|
95
|
+
ds = self['SHOW TABLES'].server(opts[:server])
|
|
96
|
+
ds.identifier_output_method = nil
|
|
97
|
+
ds2 = dataset
|
|
98
|
+
ds.map{|r| ds2.send(:output_identifier, r.values.first)}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Changes the database in use by issuing a USE statement. I would be
|
|
102
|
+
# very careful if I used this.
|
|
103
|
+
def use(db_name)
|
|
104
|
+
disconnect
|
|
105
|
+
@opts[:database] = db_name if self << "USE #{db_name}"
|
|
106
|
+
@schemas = nil
|
|
107
|
+
self
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
|
113
|
+
def identifier_input_method_default
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
|
118
|
+
def identifier_output_method_default
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Use the MySQL specific DESCRIBE syntax to get a table description.
|
|
123
|
+
def schema_parse_table(table_name, opts)
|
|
124
|
+
ds = self["DESCRIBE ?", SQL::Identifier.new(table_name)]
|
|
125
|
+
ds.identifier_output_method = nil
|
|
126
|
+
ds2 = dataset
|
|
127
|
+
ds.map do |row|
|
|
128
|
+
row.delete(:Extra)
|
|
129
|
+
row[:allow_null] = row.delete(:Null) == 'YES'
|
|
130
|
+
row[:default] = row.delete(:Default)
|
|
131
|
+
row[:primary_key] = row.delete(:Key) == 'PRI'
|
|
132
|
+
row[:default] = nil if blank_object?(row[:default])
|
|
133
|
+
row[:db_type] = row.delete(:Type)
|
|
134
|
+
row[:type] = schema_column_type(row[:db_type])
|
|
135
|
+
[ds2.send(:output_identifier, row.delete(:Field)), row]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Override the standard type conversions with MySQL specific ones
|
|
140
|
+
def type_literal_base(column)
|
|
141
|
+
TYPES[column[:type]]
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Dataset methods shared by datasets that use MySQL databases.
|
|
146
|
+
module DatasetMethods
|
|
147
|
+
include Dataset::UnsupportedIntersectExcept
|
|
148
|
+
|
|
149
|
+
BOOL_TRUE = '1'.freeze
|
|
150
|
+
BOOL_FALSE = '0'.freeze
|
|
151
|
+
CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
|
|
152
|
+
TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S'".freeze
|
|
153
|
+
COMMA_SEPARATOR = ', '.freeze
|
|
154
|
+
|
|
155
|
+
# MySQL can't use the varchar type in a cast.
|
|
156
|
+
def cast_sql(expr, type)
|
|
157
|
+
"CAST(#{literal(expr)} AS #{CAST_TYPES[type] || db.send(:type_literal_base, :type=>type)})"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# MySQL specific syntax for LIKE/REGEXP searches, as well as
|
|
161
|
+
# string concatenation.
|
|
162
|
+
def complex_expression_sql(op, args)
|
|
163
|
+
case op
|
|
164
|
+
when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
|
|
165
|
+
"(#{literal(args.at(0))} #{'NOT ' if [:'NOT LIKE', :'NOT ILIKE', :'!~', :'!~*'].include?(op)}#{[:~, :'!~', :'~*', :'!~*'].include?(op) ? 'REGEXP' : 'LIKE'} #{'BINARY ' if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)}#{literal(args.at(1))})"
|
|
166
|
+
when :'||'
|
|
167
|
+
if args.length > 1
|
|
168
|
+
"CONCAT(#{args.collect{|a| literal(a)}.join(', ')})"
|
|
169
|
+
else
|
|
170
|
+
literal(args.at(0))
|
|
171
|
+
end
|
|
172
|
+
else
|
|
173
|
+
super(op, args)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# MySQL supports ORDER and LIMIT clauses in DELETE statements.
|
|
178
|
+
def delete_sql(opts = (defarg=true;nil))
|
|
179
|
+
if defarg
|
|
180
|
+
sql = super()
|
|
181
|
+
opts = @opts
|
|
182
|
+
else
|
|
183
|
+
sql = super
|
|
184
|
+
opts = opts ? @opts.merge(opts) : @opts
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
if order = opts[:order]
|
|
188
|
+
sql << " ORDER BY #{expression_list(order)}"
|
|
189
|
+
end
|
|
190
|
+
if limit = opts[:limit]
|
|
191
|
+
sql << " LIMIT #{limit}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
sql
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# MySQL doesn't support DISTINCT ON
|
|
198
|
+
def distinct(*columns)
|
|
199
|
+
raise(Error, "DISTINCT ON not supported by MySQL") unless columns.empty?
|
|
200
|
+
super
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# MySQL specific full text search syntax.
|
|
204
|
+
def full_text_search(cols, terms, opts = {})
|
|
205
|
+
mode = opts[:boolean] ? " IN BOOLEAN MODE" : ""
|
|
206
|
+
s = if Array === terms
|
|
207
|
+
if mode.empty?
|
|
208
|
+
"MATCH #{literal(Array(cols))} AGAINST #{literal(terms)}"
|
|
209
|
+
else
|
|
210
|
+
"MATCH #{literal(Array(cols))} AGAINST (#{literal(terms)[1...-1]}#{mode})"
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
"MATCH #{literal(Array(cols))} AGAINST (#{literal(terms)}#{mode})"
|
|
214
|
+
end
|
|
215
|
+
filter(s)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# MySQL allows HAVING clause on ungrouped datasets.
|
|
219
|
+
def having(*cond, &block)
|
|
220
|
+
_filter(:having, *cond, &block)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# MySQL doesn't use the SQL standard DEFAULT VALUES.
|
|
224
|
+
def insert_default_values_sql
|
|
225
|
+
"INSERT INTO #{source_list(@opts[:from])} () VALUES ()"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.
|
|
229
|
+
# Raises an error on use of :full_outer type, since MySQL doesn't support it.
|
|
230
|
+
def join_table(type, table, expr=nil, table_alias={})
|
|
231
|
+
type = :inner if (type == :cross) && !expr.nil?
|
|
232
|
+
raise(Sequel::Error, "MySQL doesn't support FULL OUTER JOIN") if type == :full_outer
|
|
233
|
+
super(type, table, expr, table_alias)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Transforms :natural_inner to NATURAL LEFT JOIN and straight to
|
|
237
|
+
# STRAIGHT_JOIN.
|
|
238
|
+
def join_type_sql(join_type)
|
|
239
|
+
case join_type
|
|
240
|
+
when :straight then 'STRAIGHT_JOIN'
|
|
241
|
+
when :natural_inner then 'NATURAL LEFT JOIN'
|
|
242
|
+
else super
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# MySQL specific syntax for inserting multiple values at once.
|
|
247
|
+
def multi_insert_sql(columns, values)
|
|
248
|
+
values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
|
|
249
|
+
["INSERT INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
|
|
253
|
+
def quoted_identifier(c)
|
|
254
|
+
"`#{c}`"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# MySQL specific syntax for REPLACE (aka UPSERT, or update if exists,
|
|
258
|
+
# insert if it doesn't).
|
|
259
|
+
def replace_sql(*values)
|
|
260
|
+
from = source_list(@opts[:from])
|
|
261
|
+
if values.empty?
|
|
262
|
+
"REPLACE INTO #{from} DEFAULT VALUES"
|
|
263
|
+
else
|
|
264
|
+
values = values[0] if values.size == 1
|
|
265
|
+
|
|
266
|
+
# if hash or array with keys we need to transform the values
|
|
267
|
+
if @transform && (values.is_a?(Hash) || (values.is_a?(Array) && values.keys))
|
|
268
|
+
values = transform_save(values)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
case values
|
|
272
|
+
when Array
|
|
273
|
+
if values.empty?
|
|
274
|
+
"REPLACE INTO #{from} DEFAULT VALUES"
|
|
275
|
+
else
|
|
276
|
+
"REPLACE INTO #{from} VALUES #{literal(values)}"
|
|
277
|
+
end
|
|
278
|
+
when Hash
|
|
279
|
+
if values.empty?
|
|
280
|
+
"REPLACE INTO #{from} DEFAULT VALUES"
|
|
281
|
+
else
|
|
282
|
+
fl, vl = [], []
|
|
283
|
+
values.each {|k, v| fl << literal(k.is_a?(String) ? k.to_sym : k); vl << literal(v)}
|
|
284
|
+
"REPLACE INTO #{from} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
|
|
285
|
+
end
|
|
286
|
+
when Dataset
|
|
287
|
+
"REPLACE INTO #{from} #{literal(values)}"
|
|
288
|
+
else
|
|
289
|
+
if values.respond_to?(:values)
|
|
290
|
+
replace_sql(values.values)
|
|
291
|
+
else
|
|
292
|
+
"REPLACE INTO #{from} VALUES (#{literal(values)})"
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# MySQL supports ORDER and LIMIT clauses in UPDATE statements.
|
|
299
|
+
def update_sql(values, opts = (defarg=true;nil))
|
|
300
|
+
if defarg
|
|
301
|
+
sql = super(values)
|
|
302
|
+
opts = @opts
|
|
303
|
+
else
|
|
304
|
+
sql = super
|
|
305
|
+
opts = opts ? @opts.merge(opts) : @opts
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
if order = opts[:order]
|
|
309
|
+
sql << " ORDER BY #{expression_list(order)}"
|
|
310
|
+
end
|
|
311
|
+
if limit = opts[:limit]
|
|
312
|
+
sql << " LIMIT #{limit}"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
sql
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
private
|
|
319
|
+
|
|
320
|
+
# Use MySQL Timestamp format
|
|
321
|
+
def literal_datetime(v)
|
|
322
|
+
v.strftime(TIMESTAMP_FORMAT)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Use 0 for false on MySQL
|
|
326
|
+
def literal_false
|
|
327
|
+
BOOL_FALSE
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Use MySQL Timestamp format
|
|
331
|
+
def literal_time(v)
|
|
332
|
+
v.strftime(TIMESTAMP_FORMAT)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Use 1 for true on MySQL
|
|
336
|
+
def literal_true
|
|
337
|
+
BOOL_TRUE
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Sequel.require %w'date_format unsupported', 'adapters/utils'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module Oracle
|
|
5
|
+
module DatabaseMethods
|
|
6
|
+
def tables(opts={})
|
|
7
|
+
ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE')
|
|
8
|
+
ds.map{|r| ds.send(:output_identifier, r[:tname])}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def table_exists?(name)
|
|
12
|
+
from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module DatasetMethods
|
|
17
|
+
include Dataset::UnsupportedIntersectExceptAll
|
|
18
|
+
include Dataset::SQLStandardDateFormat
|
|
19
|
+
|
|
20
|
+
SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
|
|
21
|
+
|
|
22
|
+
# Oracle doesn't support DISTINCT ON
|
|
23
|
+
def distinct(*columns)
|
|
24
|
+
raise(Error, "DISTINCT ON not supported by Oracle") unless columns.empty?
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
|
|
29
|
+
def except(dataset, all = false)
|
|
30
|
+
raise(Sequel::Error, "EXCEPT ALL not supported") if all
|
|
31
|
+
compound_clone(:minus, dataset, all)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def empty?
|
|
35
|
+
db[:dual].where(exists).get(1) == nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
|
|
41
|
+
# the use of AS anywhere, so this disables it in all cases.
|
|
42
|
+
def as_sql(expression, aliaz)
|
|
43
|
+
"#{expression} #{quote_identifier(aliaz)}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def select_clause_order
|
|
47
|
+
SELECT_CLAUSE_ORDER
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Oracle requires a subselect to do limit and offset
|
|
51
|
+
def select_limit_sql(sql, opts)
|
|
52
|
+
if limit = opts[:limit]
|
|
53
|
+
if (offset = opts[:offset]) && (offset > 0)
|
|
54
|
+
sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
|
|
55
|
+
else
|
|
56
|
+
sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|