sequel 0.3.3 → 0.3.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +20 -0
- data/README +5 -3
- data/Rakefile +1 -1
- data/bin/sequel +2 -2
- data/lib/sequel.rb +1 -1
- data/lib/sequel/array_keys.rb +4 -0
- data/lib/sequel/core_ext.rb +5 -137
- data/lib/sequel/core_sql.rb +151 -0
- data/lib/sequel/dataset.rb +5 -0
- data/lib/sequel/dataset/sequelizer.rb +9 -3
- data/lib/sequel/dataset/sql.rb +24 -31
- data/lib/sequel/model/hooks.rb +1 -1
- data/lib/sequel/mysql.rb +3 -20
- data/spec/adapters/mysql_spec.rb +48 -2
- data/spec/core_ext_spec.rb +0 -192
- data/spec/core_sql_spec.rb +279 -0
- data/spec/dataset_spec.rb +6 -6
- data/spec/sequelizer_spec.rb +7 -0
- metadata +4 -3
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
=== 0.3.4.1 (2007-11-10)
|
2
|
+
|
3
|
+
* Changed Dataset#select_sql to support queries without a FROM clause.
|
4
|
+
|
5
|
+
=== 0.3.4 (2007-11-10)
|
6
|
+
|
7
|
+
* Fixed MySQL adapter to allow calling stored procedures (thanks Sebastian).
|
8
|
+
|
9
|
+
* Changed Dataset#each to always return self.
|
10
|
+
|
11
|
+
* Fixed SQL functions without arguments in block filters.
|
12
|
+
|
13
|
+
* Implemented super-cool Symbol#cast_as method.
|
14
|
+
|
15
|
+
* Fixed error message in command-line tool if failed to load adapter (#85).
|
16
|
+
|
17
|
+
* Refactored code relating to column references for better extendibility (#88).
|
18
|
+
|
19
|
+
* Tiny fix to Model#run_hooks.
|
20
|
+
|
1
21
|
=== 0.3.3 (2007-11-04)
|
2
22
|
|
3
23
|
* Revised code to generate SQL statements without trailing semicolons.
|
data/README
CHANGED
@@ -28,11 +28,13 @@ If you have any comments or suggestions please send an email to ciconia at gmail
|
|
28
28
|
|
29
29
|
Sequel currently supports:
|
30
30
|
|
31
|
-
*
|
32
|
-
* SQLite 3
|
31
|
+
* ADO (on Windows)
|
33
32
|
* DBI
|
34
|
-
* ODBC
|
35
33
|
* MySQL
|
34
|
+
* ODBC
|
35
|
+
* Oracle
|
36
|
+
* PostgreSQL
|
37
|
+
* SQLite 3
|
36
38
|
|
37
39
|
== The Sequel Console
|
38
40
|
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
|
|
6
6
|
include FileUtils
|
7
7
|
|
8
8
|
NAME = "sequel"
|
9
|
-
VERS = "0.3.
|
9
|
+
VERS = "0.3.4.1"
|
10
10
|
CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
|
11
11
|
RDOC_OPTS = ['--quiet', '--title', "Sequel: Concise ORM for Ruby",
|
12
12
|
"--opname", "index.html",
|
data/bin/sequel
CHANGED
@@ -24,8 +24,8 @@ end
|
|
24
24
|
begin
|
25
25
|
scheme = URI.parse(db).scheme
|
26
26
|
require File.join('sequel', scheme)
|
27
|
-
rescue LoadError
|
28
|
-
puts "
|
27
|
+
rescue LoadError => e
|
28
|
+
puts "Failed to load #{scheme} adapter: #{e.message}"
|
29
29
|
exit
|
30
30
|
rescue => e
|
31
31
|
puts e.message
|
data/lib/sequel.rb
CHANGED
data/lib/sequel/array_keys.rb
CHANGED
@@ -208,6 +208,7 @@ module ArrayKeys
|
|
208
208
|
else
|
209
209
|
fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(Array.from_hash(r))]]}
|
210
210
|
end
|
211
|
+
self
|
211
212
|
end
|
212
213
|
end
|
213
214
|
elsif @row_proc
|
@@ -218,18 +219,21 @@ module ArrayKeys
|
|
218
219
|
else
|
219
220
|
fetch_rows(select_sql(opts)) {|r| block[@row_proc[Array.from_hash(r)]]}
|
220
221
|
end
|
222
|
+
self
|
221
223
|
end
|
222
224
|
end
|
223
225
|
elsif @transform
|
224
226
|
class << self
|
225
227
|
def each(opts = nil, &block)
|
226
228
|
fetch_rows(select_sql(opts)) {|r| block[transform_load(Array.from_hash(r))]}
|
229
|
+
self
|
227
230
|
end
|
228
231
|
end
|
229
232
|
else
|
230
233
|
class << self
|
231
234
|
def each(opts = nil, &block)
|
232
235
|
fetch_rows(select_sql(opts)) {|r| block[Array.from_hash(r)]}
|
236
|
+
self
|
233
237
|
end
|
234
238
|
end
|
235
239
|
end
|
data/lib/sequel/core_ext.rb
CHANGED
@@ -7,16 +7,6 @@ module Enumerable
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
# Array extensions
|
11
|
-
class Array
|
12
|
-
# Concatenates an array of strings into an SQL string. ANSI SQL and C-style
|
13
|
-
# comments are removed, as well as excessive white-space.
|
14
|
-
def to_sql
|
15
|
-
map {|l| (l =~ /^(.*)--/ ? $1 : l).chomp}.join(' '). \
|
16
|
-
gsub(/\/\*.*\*\//, '').gsub(/\s+/, ' ').strip
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
10
|
# Range extensions
|
21
11
|
class Range
|
22
12
|
# Returns the interval between the beginning and end of the range.
|
@@ -25,133 +15,11 @@ class Range
|
|
25
15
|
end
|
26
16
|
end
|
27
17
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# String extensions
|
37
|
-
class String
|
38
|
-
# Converts a string into an SQL string by removing comments.
|
39
|
-
# See also Array#to_sql.
|
40
|
-
def to_sql
|
41
|
-
split($/).to_sql
|
42
|
-
end
|
43
|
-
|
44
|
-
# Splits a string into separate SQL statements, removing comments
|
45
|
-
# and excessive white-space.
|
46
|
-
def split_sql
|
47
|
-
to_sql.split(';').map {|s| s.strip}
|
48
|
-
end
|
49
|
-
|
50
|
-
# Converts a string into an LiteralString, in order to override string
|
51
|
-
# literalization, e.g.:
|
52
|
-
#
|
53
|
-
# DB[:items].filter(:abc => 'def').sql #=>
|
54
|
-
# "SELECT * FROM items WHERE (abc = 'def')"
|
55
|
-
#
|
56
|
-
# DB[:items].filter(:abc => 'def'.lit).sql #=>
|
57
|
-
# "SELECT * FROM items WHERE (abc = def)"
|
58
|
-
#
|
59
|
-
def lit
|
60
|
-
Sequel::LiteralString.new(self)
|
61
|
-
end
|
62
|
-
|
63
|
-
alias_method :expr, :lit
|
64
|
-
|
65
|
-
# Converts a string into a Time object.
|
66
|
-
def to_time
|
67
|
-
Time.parse(self)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Converts a string into a column name.
|
71
|
-
def to_field_name
|
72
|
-
self
|
73
|
-
end
|
74
|
-
alias_method :to_column_name, :to_field_name
|
75
|
-
end
|
76
|
-
|
77
|
-
# Methods to format column names and associated constructs. This module is
|
78
|
-
# included in String and Symbol.
|
79
|
-
module ColumnCompositionMethods
|
80
|
-
# Constructs a DESC clause for use in an ORDER BY clause.
|
81
|
-
def DESC
|
82
|
-
"#{to_column_name} DESC".lit
|
83
|
-
end
|
84
|
-
|
85
|
-
# Constructs an AS clause for column aliasing.
|
86
|
-
def AS(target)
|
87
|
-
"#{to_column_name} AS #{target}".lit
|
88
|
-
end
|
89
|
-
|
90
|
-
# Constructs a qualified wildcard (*) clause.
|
91
|
-
def ALL
|
92
|
-
"#{to_s}.*".lit
|
93
|
-
end
|
94
|
-
|
95
|
-
COLUMN_TITLE_RE1 = /^(.*)\sAS\s(.+)$/i.freeze
|
96
|
-
COLUMN_TITLE_RE2 = /^([^\.]+)\.([^\.]+)$/.freeze
|
97
|
-
|
98
|
-
# Returns the column name. If the column name is aliased, the alias is
|
99
|
-
# returned.
|
100
|
-
def field_title
|
101
|
-
case s = to_column_name
|
102
|
-
when COLUMN_TITLE_RE1, COLUMN_TITLE_RE2: $2
|
103
|
-
else
|
104
|
-
s
|
105
|
-
end
|
106
|
-
end
|
107
|
-
alias_method :column_title, :field_title
|
108
|
-
end
|
109
|
-
|
110
|
-
# String extensions
|
111
|
-
class String
|
112
|
-
include ColumnCompositionMethods
|
113
|
-
end
|
114
|
-
|
115
|
-
# Symbol extensions
|
116
|
-
class Symbol
|
117
|
-
include ColumnCompositionMethods
|
118
|
-
|
119
|
-
|
120
|
-
COLUMN_REF_RE1 = /^(\w+)__(\w+)___(\w+)/.freeze
|
121
|
-
COLUMN_REF_RE2 = /^(\w+)___(\w+)$/.freeze
|
122
|
-
COLUMN_REF_RE3 = /^(\w+)__(\w+)$/.freeze
|
123
|
-
|
124
|
-
# Converts a symbol into a column name. This method supports underscore
|
125
|
-
# notation in order to express qualified (two underscores) and aliased
|
126
|
-
# (three underscores) columns:
|
127
|
-
#
|
128
|
-
# :abc.to_column_name #=> "abc"
|
129
|
-
# :abc___a.to_column_name #=> "abc AS a"
|
130
|
-
# :items__abc.to_column_name #=> "items.abc"
|
131
|
-
# :items__abc___a.to_column_name #=> "items.abc AS a"
|
132
|
-
#
|
133
|
-
def to_field_name
|
134
|
-
s = to_s
|
135
|
-
case s
|
136
|
-
when COLUMN_REF_RE1: "#{$1}.#{$2} AS #{$3}"
|
137
|
-
when COLUMN_REF_RE2: "#{$1} AS #{$2}"
|
138
|
-
when COLUMN_REF_RE3: "#{$1}.#{$2}"
|
139
|
-
else
|
140
|
-
s
|
141
|
-
end
|
142
|
-
end
|
143
|
-
alias_method :to_column_name, :to_field_name
|
144
|
-
|
145
|
-
# Converts missing method calls into functions on columns, if the
|
146
|
-
# method name is made of all upper case letters.
|
147
|
-
def method_missing(sym)
|
148
|
-
((s = sym.to_s) =~ /^([A-Z]+)$/) ? \
|
149
|
-
"#{s.downcase}(#{to_column_name})".lit : super
|
150
|
-
end
|
151
|
-
|
152
|
-
# Formats an SQL function with optional parameters
|
153
|
-
def [](*args)
|
154
|
-
"#{to_s}(#{args.join(', ')})".lit
|
18
|
+
# Object extensions
|
19
|
+
class Object
|
20
|
+
def is_one_of?(*classes)
|
21
|
+
classes.each {|c| return c if is_a?(c)}
|
22
|
+
nil
|
155
23
|
end
|
156
24
|
end
|
157
25
|
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# Array extensions
|
2
|
+
class Array
|
3
|
+
# Concatenates an array of strings into an SQL string. ANSI SQL and C-style
|
4
|
+
# comments are removed, as well as excessive white-space.
|
5
|
+
def to_sql
|
6
|
+
map {|l| (l =~ /^(.*)--/ ? $1 : l).chomp}.join(' '). \
|
7
|
+
gsub(/\/\*.*\*\//, '').gsub(/\s+/, ' ').strip
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Sequel
|
12
|
+
# LiteralString is used to represent literal SQL expressions. An
|
13
|
+
# LiteralString is copied verbatim into an SQL statement. Instances of
|
14
|
+
# LiteralString can be created by calling String#expr.
|
15
|
+
class LiteralString < ::String
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# String extensions
|
20
|
+
class String
|
21
|
+
# Converts a string into an SQL string by removing comments.
|
22
|
+
# See also Array#to_sql.
|
23
|
+
def to_sql
|
24
|
+
split($/).to_sql
|
25
|
+
end
|
26
|
+
|
27
|
+
# Splits a string into separate SQL statements, removing comments
|
28
|
+
# and excessive white-space.
|
29
|
+
def split_sql
|
30
|
+
to_sql.split(';').map {|s| s.strip}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Converts a string into an LiteralString, in order to override string
|
34
|
+
# literalization, e.g.:
|
35
|
+
#
|
36
|
+
# DB[:items].filter(:abc => 'def').sql #=>
|
37
|
+
# "SELECT * FROM items WHERE (abc = 'def')"
|
38
|
+
#
|
39
|
+
# DB[:items].filter(:abc => 'def'.lit).sql #=>
|
40
|
+
# "SELECT * FROM items WHERE (abc = def)"
|
41
|
+
#
|
42
|
+
def lit
|
43
|
+
Sequel::LiteralString.new(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
alias_method :expr, :lit
|
47
|
+
|
48
|
+
# Converts a string into a Time object.
|
49
|
+
def to_time
|
50
|
+
Time.parse(self)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
module Sequel
|
56
|
+
module SQL
|
57
|
+
class Expression
|
58
|
+
def lit; self; end
|
59
|
+
end
|
60
|
+
|
61
|
+
class ColumnExpr < Expression
|
62
|
+
def initialize(l, op, r = nil); @l, @op, @r = l, op, r; end
|
63
|
+
|
64
|
+
def to_s(ds)
|
65
|
+
@r ? \
|
66
|
+
"#{ds.literal(@l)} #{@op} #{ds.literal(@r)}" : \
|
67
|
+
"#{ds.literal(@l)} #{@op}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Function < Expression
|
72
|
+
def initialize(f, *args); @f, @args = f, args; end
|
73
|
+
|
74
|
+
def to_s(ds)
|
75
|
+
args = @args.empty? ? '' : ds.literal(@args)
|
76
|
+
"#{@f}(#{args})"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class ColumnAll < Expression
|
81
|
+
def initialize(t); @t = t; end
|
82
|
+
def to_s(ds); "#{@t}.*"; end
|
83
|
+
end
|
84
|
+
|
85
|
+
module ColumnMethods
|
86
|
+
AS = 'AS'.freeze
|
87
|
+
DESC = 'DESC'.freeze
|
88
|
+
ASC = 'ASC'.freeze
|
89
|
+
|
90
|
+
def as(a); ColumnExpr.new(self, AS, a); end
|
91
|
+
alias_method :AS, :as
|
92
|
+
|
93
|
+
def desc; ColumnExpr.new(self, DESC); end
|
94
|
+
alias_method :DESC, :desc
|
95
|
+
|
96
|
+
def asc; ColumnExpr.new(self, ASC); end
|
97
|
+
alias_method :ASC, :asc
|
98
|
+
|
99
|
+
def all; Sequel::SQL::ColumnAll.new(self); end
|
100
|
+
alias_method :ALL, :all
|
101
|
+
|
102
|
+
def cast_as(t)
|
103
|
+
if t.is_a?(Symbol)
|
104
|
+
t = t.to_s.lit
|
105
|
+
end
|
106
|
+
Sequel::SQL::Function.new(:cast, self.as(t))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Object
|
113
|
+
include Sequel::SQL::ColumnMethods
|
114
|
+
end
|
115
|
+
|
116
|
+
class Symbol
|
117
|
+
def [](*args); Sequel::SQL::Function.new(self, *args); end
|
118
|
+
|
119
|
+
COLUMN_REF_RE1 = /^(\w+)__(\w+)___(\w+)/.freeze
|
120
|
+
COLUMN_REF_RE2 = /^(\w+)___(\w+)$/.freeze
|
121
|
+
COLUMN_REF_RE3 = /^(\w+)__(\w+)$/.freeze
|
122
|
+
|
123
|
+
# Converts a symbol into a column name. This method supports underscore
|
124
|
+
# notation in order to express qualified (two underscores) and aliased
|
125
|
+
# (three underscores) columns:
|
126
|
+
#
|
127
|
+
# ds = DB[:items]
|
128
|
+
# :abc.to_column_ref(ds) #=> "abc"
|
129
|
+
# :abc___a.to_column_ref(ds) #=> "abc AS a"
|
130
|
+
# :items__abc.to_column_ref(ds) #=> "items.abc"
|
131
|
+
# :items__abc___a.to_column_ref(ds) #=> "items.abc AS a"
|
132
|
+
#
|
133
|
+
def to_column_ref(ds)
|
134
|
+
case s = to_s
|
135
|
+
when COLUMN_REF_RE1: "#{$1}.#{ds.quote_column_ref($2)} AS #{ds.quote_column_ref($3)}"
|
136
|
+
when COLUMN_REF_RE2: "#{ds.quote_column_ref($1)} AS #{ds.quote_column_ref($2)}"
|
137
|
+
when COLUMN_REF_RE3: "#{$1}.#{ds.quote_column_ref($2)}"
|
138
|
+
else ds.quote_column_ref(s)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Converts missing method calls into functions on columns, if the
|
143
|
+
# method name is made of all upper case letters.
|
144
|
+
def method_missing(sym)
|
145
|
+
if ((s = sym.to_s) =~ /^([A-Z]+)$/)
|
146
|
+
Sequel::SQL::Function.new(s.downcase, self)
|
147
|
+
else
|
148
|
+
super
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
data/lib/sequel/dataset.rb
CHANGED
@@ -161,6 +161,7 @@ module Sequel
|
|
161
161
|
# Iterates over the records in the dataset
|
162
162
|
def each(opts = nil, &block)
|
163
163
|
fetch_rows(select_sql(opts), &block)
|
164
|
+
self
|
164
165
|
end
|
165
166
|
|
166
167
|
# Returns the the model classes associated with the dataset as a hash.
|
@@ -347,6 +348,7 @@ module Sequel
|
|
347
348
|
else
|
348
349
|
fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(r)]]}
|
349
350
|
end
|
351
|
+
self
|
350
352
|
end
|
351
353
|
end
|
352
354
|
elsif @row_proc
|
@@ -357,18 +359,21 @@ module Sequel
|
|
357
359
|
else
|
358
360
|
fetch_rows(select_sql(opts)) {|r| block[@row_proc[r]]}
|
359
361
|
end
|
362
|
+
self
|
360
363
|
end
|
361
364
|
end
|
362
365
|
elsif @transform
|
363
366
|
class << self
|
364
367
|
def each(opts = nil, &block)
|
365
368
|
fetch_rows(select_sql(opts)) {|r| block[transform_load(r)]}
|
369
|
+
self
|
366
370
|
end
|
367
371
|
end
|
368
372
|
else
|
369
373
|
class << self
|
370
374
|
def each(opts = nil, &block)
|
371
375
|
fetch_rows(select_sql(opts), &block)
|
376
|
+
self
|
372
377
|
end
|
373
378
|
end
|
374
379
|
end
|
@@ -108,7 +108,8 @@ class Sequel::Dataset
|
|
108
108
|
when :>, :<, :>=, :<=
|
109
109
|
l = eval_expr(e[1], b)
|
110
110
|
r = eval_expr(e[3][1], b)
|
111
|
-
if (Symbol
|
111
|
+
if l.is_one_of?(Symbol, Sequel::LiteralString, Sequel::SQL::Expression) || \
|
112
|
+
r.is_one_of?(Symbol, Sequel::LiteralString, Sequel::SQL::Expression)
|
112
113
|
"(#{literal(l)} #{op} #{literal(r)})"
|
113
114
|
else
|
114
115
|
ext_expr(e, b)
|
@@ -124,7 +125,8 @@ class Sequel::Dataset
|
|
124
125
|
when :+, :-, :*, :/, :%
|
125
126
|
l = eval_expr(e[1], b)
|
126
127
|
r = eval_expr(e[3][1], b)
|
127
|
-
if (Symbol
|
128
|
+
if l.is_one_of?(Symbol, Sequel::LiteralString, Sequel::SQL::Expression) || \
|
129
|
+
r.is_one_of?(Symbol, Sequel::LiteralString, Sequel::SQL::Expression)
|
128
130
|
"(#{literal(l)} #{op} #{literal(r)})".lit
|
129
131
|
else
|
130
132
|
ext_expr(e, b)
|
@@ -146,7 +148,11 @@ class Sequel::Dataset
|
|
146
148
|
else
|
147
149
|
if (op == :[]) && (e[1][0] == :lit) && (Symbol === e[1][1])
|
148
150
|
# SQL Functions, e.g.: :sum[:x]
|
149
|
-
e[
|
151
|
+
if e[3]
|
152
|
+
e[1][1][*eval_expr(e[3], b)]
|
153
|
+
else
|
154
|
+
e[1][1][]
|
155
|
+
end
|
150
156
|
else
|
151
157
|
# external code
|
152
158
|
ext_expr(e, b)
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -3,26 +3,10 @@ module Sequel
|
|
3
3
|
# The Dataset SQL module implements all the dataset methods concerned with
|
4
4
|
# generating SQL statements for retrieving and manipulating records.
|
5
5
|
module SQL
|
6
|
-
# Returns a valid SQL column name as a string. Column names specified as
|
7
|
-
# symbols can include double underscores to denote a dot separator, e.g.
|
8
|
-
# :posts__id will be converted into posts.id.
|
9
|
-
def column_name(column)
|
10
|
-
case column
|
11
|
-
when Symbol, String:
|
12
|
-
quoted_column_name(column.to_column_name)
|
13
|
-
when Hash:
|
14
|
-
column.map {|f,a| "#{column_name(f)} AS #{column_name(a)}"}.join(COMMA_SEPARATOR)
|
15
|
-
else
|
16
|
-
column
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
6
|
# Adds quoting to column references. This method is just a stub and can
|
21
7
|
# be overriden in adapters in order to provide correct column quoting
|
22
8
|
# behavior.
|
23
|
-
def
|
24
|
-
name
|
25
|
-
end
|
9
|
+
def quote_column_ref(name); name.to_s; end
|
26
10
|
|
27
11
|
ALIASED_REGEXP = /^(.*)\s(.*)$/.freeze
|
28
12
|
QUALIFIED_REGEXP = /^(.*)\.(.*)$/.freeze
|
@@ -30,7 +14,7 @@ module Sequel
|
|
30
14
|
# Returns a qualified column name (including a table name) if the column
|
31
15
|
# name isn't already qualified.
|
32
16
|
def qualified_column_name(column, table)
|
33
|
-
column =
|
17
|
+
column = literal(column)
|
34
18
|
if column =~ QUALIFIED_REGEXP
|
35
19
|
# column is already qualified
|
36
20
|
column
|
@@ -105,7 +89,8 @@ module Sequel
|
|
105
89
|
when NilClass: NULL
|
106
90
|
when TrueClass: TRUE
|
107
91
|
when FalseClass: FALSE
|
108
|
-
when Symbol:
|
92
|
+
when Symbol: v.to_column_ref(self)
|
93
|
+
when Sequel::SQL::Expression: v.to_s(self)
|
109
94
|
when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
|
110
95
|
when Time: v.strftime(TIMESTAMP_FORMAT)
|
111
96
|
when Date: v.strftime(DATE_FORMAT)
|
@@ -170,20 +155,25 @@ module Sequel
|
|
170
155
|
order(*invert_order(order.empty? ? @opts[:order] : order))
|
171
156
|
end
|
172
157
|
|
173
|
-
DESC_ORDER_REGEXP =
|
158
|
+
DESC_ORDER_REGEXP = /^(.*)\sDESC$/i.freeze
|
174
159
|
|
175
160
|
# Inverts the given order by breaking it into a list of column references
|
176
161
|
# and inverting them.
|
177
162
|
#
|
178
|
-
# dataset.invert_order('id DESC') #=> "id"
|
179
|
-
# dataset.invert_order('category, price DESC') #=>
|
180
|
-
# "category DESC, price"
|
163
|
+
# dataset.invert_order(['id DESC']) #=> ["id"]
|
164
|
+
# dataset.invert_order(['category, price DESC']) #=>
|
165
|
+
# ["category DESC, price"]
|
181
166
|
def invert_order(order)
|
182
167
|
new_order = []
|
183
168
|
order.each do |f|
|
184
|
-
f.
|
185
|
-
|
186
|
-
|
169
|
+
if f.is_a?(String)
|
170
|
+
f.split(',').each do |r|
|
171
|
+
r.strip!
|
172
|
+
new_order << ((r =~ DESC_ORDER_REGEXP ? $1 : r.to_sym.desc).lit)
|
173
|
+
end
|
174
|
+
else
|
175
|
+
r = literal(f).strip
|
176
|
+
new_order << ((r =~ DESC_ORDER_REGEXP ? $1 : r.to_sym.desc).lit)
|
187
177
|
end
|
188
178
|
end
|
189
179
|
new_order
|
@@ -381,10 +371,13 @@ module Sequel
|
|
381
371
|
|
382
372
|
columns = opts[:select]
|
383
373
|
select_columns = columns ? column_list(columns) : WILDCARD
|
384
|
-
select_source = source_list(opts[:from])
|
385
374
|
sql = opts[:distinct] ? \
|
386
|
-
|
387
|
-
|
375
|
+
"SELECT DISTINCT #{select_columns}" : \
|
376
|
+
"SELECT #{select_columns}"
|
377
|
+
|
378
|
+
if opts[:from]
|
379
|
+
sql << " FROM #{source_list(opts[:from])}"
|
380
|
+
end
|
388
381
|
|
389
382
|
if join = opts[:join]
|
390
383
|
sql << join
|
@@ -460,7 +453,7 @@ module Sequel
|
|
460
453
|
"INSERT INTO #{@opts[:from]} DEFAULT VALUES"
|
461
454
|
else
|
462
455
|
fl, vl = [], []
|
463
|
-
values.each {|k, v| fl <<
|
456
|
+
values.each {|k, v| fl << literal(k); vl << literal(v)}
|
464
457
|
"INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
|
465
458
|
end
|
466
459
|
when Dataset
|
@@ -488,7 +481,7 @@ module Sequel
|
|
488
481
|
values = values.to_hash
|
489
482
|
end
|
490
483
|
values = transform_save(values) if @transform
|
491
|
-
set_list = values.map {|k, v| "#{
|
484
|
+
set_list = values.map {|k, v| "#{literal(k)} = #{literal(v)}"}.
|
492
485
|
join(COMMA_SEPARATOR)
|
493
486
|
sql = "UPDATE #{@opts[:from]} SET #{set_list}"
|
494
487
|
|
data/lib/sequel/model/hooks.rb
CHANGED
@@ -116,7 +116,7 @@ module Sequel
|
|
116
116
|
|
117
117
|
# Evaluates specified chain of Hooks through <tt>instance_eval</tt>.
|
118
118
|
def run_hooks(key)
|
119
|
-
model.hooks[key].each {
|
119
|
+
model.hooks[key].each {|h| instance_eval(&h)}
|
120
120
|
end
|
121
121
|
|
122
122
|
def self.has_hooks?(key)
|
data/lib/sequel/mysql.rb
CHANGED
@@ -93,7 +93,7 @@ module Sequel
|
|
93
93
|
|
94
94
|
def connect
|
95
95
|
conn = Mysql.real_connect(@opts[:host], @opts[:user], @opts[:password],
|
96
|
-
@opts[:database], @opts[:port])
|
96
|
+
@opts[:database], @opts[:port], nil, Mysql::CLIENT_MULTI_RESULTS)
|
97
97
|
conn.query_with_result = false
|
98
98
|
if encoding = @opts[:encoding] || @opts[:charset]
|
99
99
|
conn.query("set character_set_connection = '#{encoding}'")
|
@@ -172,31 +172,14 @@ module Sequel
|
|
172
172
|
end
|
173
173
|
|
174
174
|
class Dataset < Sequel::Dataset
|
175
|
-
|
176
|
-
def quote_column(f)
|
177
|
-
(f.nil? || f.empty? || f =~ UNQUOTABLE_COLUMN_RE) ? f : "`#{f}`"
|
178
|
-
end
|
179
|
-
|
180
|
-
COLUMN_EXPR_RE = /^([^\(]+\()?([^\.]+\.)?([^\s\)]+)?(\))?(\sAS\s(.+))?$/i.freeze
|
181
|
-
COLUMN_ORDER_RE = /^(.*) (DESC|ASC)$/i.freeze
|
182
|
-
def quoted_column_name(name)
|
183
|
-
case name
|
184
|
-
when COLUMN_EXPR_RE:
|
185
|
-
$6 ? \
|
186
|
-
"#{$1}#{$2}#{quote_column($3)}#{$4} AS #{quote_column($6)}" : \
|
187
|
-
"#{$1}#{$2}#{quote_column($3)}#{$4}"
|
188
|
-
when COLUMN_ORDER_RE: "#{quote_column($1)} #{$2}"
|
189
|
-
else
|
190
|
-
quote_column(name)
|
191
|
-
end
|
192
|
-
end
|
175
|
+
def quote_column_ref(c); "`#{c}`"; end
|
193
176
|
|
194
177
|
TRUE = '1'
|
195
178
|
FALSE = '0'
|
196
179
|
|
197
180
|
def literal(v)
|
198
181
|
case v
|
199
|
-
when LiteralString:
|
182
|
+
when LiteralString: v
|
200
183
|
when String: "'#{v.gsub(/'|\\/, '\&\&')}'"
|
201
184
|
when true: TRUE
|
202
185
|
when false: FALSE
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -97,17 +97,29 @@ context "A MySQL dataset" do
|
|
97
97
|
@d.order(:name.DESC).sql.should == \
|
98
98
|
'SELECT * FROM items ORDER BY `name` DESC'
|
99
99
|
|
100
|
-
@d.select('items.name AS item_name'.
|
101
|
-
'SELECT items
|
100
|
+
@d.select('items.name AS item_name'.lit).sql.should == \
|
101
|
+
'SELECT items.name AS item_name FROM items'
|
102
102
|
|
103
103
|
@d.select('`name`'.lit).sql.should == \
|
104
104
|
'SELECT `name` FROM items'
|
105
105
|
|
106
106
|
@d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
|
107
107
|
'SELECT max(items.`name`) AS `max_name` FROM items'
|
108
|
+
|
109
|
+
@d.select(:test[:abc, 'hello']).sql.should == \
|
110
|
+
"SELECT test(`abc`, 'hello') FROM items"
|
111
|
+
|
112
|
+
@d.select(:test[:abc__def, 'hello']).sql.should == \
|
113
|
+
"SELECT test(abc.`def`, 'hello') FROM items"
|
114
|
+
|
115
|
+
@d.select(:test[:abc__def, 'hello'].as(:x2)).sql.should == \
|
116
|
+
"SELECT test(abc.`def`, 'hello') AS `x2` FROM items"
|
108
117
|
|
109
118
|
@d.insert_sql(:value => 333).should == \
|
110
119
|
'INSERT INTO items (`value`) VALUES (333)'
|
120
|
+
|
121
|
+
@d.insert_sql(:x => :y).should == \
|
122
|
+
'INSERT INTO items (`x`) VALUES (`y`)'
|
111
123
|
end
|
112
124
|
|
113
125
|
specify "should support ORDER clause in UPDATE statements" do
|
@@ -188,3 +200,37 @@ context "A MySQL dataset in array tuples mode" do
|
|
188
200
|
a[:value].should == '123'
|
189
201
|
end
|
190
202
|
end
|
203
|
+
|
204
|
+
context "MySQL datasets" do
|
205
|
+
setup do
|
206
|
+
@d = MYSQL_DB[:orders]
|
207
|
+
end
|
208
|
+
|
209
|
+
specify "should correctly quote column references" do
|
210
|
+
market = 'ICE'
|
211
|
+
ack_stamp = Time.now - 15 * 60 # 15 minutes ago
|
212
|
+
@d.query do
|
213
|
+
select :market, :minute[:from_unixtime[:ack]].as(:minute)
|
214
|
+
where do
|
215
|
+
:ack > ack_stamp
|
216
|
+
:market == market
|
217
|
+
end
|
218
|
+
group_by :minute[:from_unixtime[:ack]]
|
219
|
+
end.sql.should == \
|
220
|
+
"SELECT `market`, minute(from_unixtime(`ack`)) AS `minute` FROM orders WHERE ((`ack` > #{@d.literal(ack_stamp)}) AND (`market` = 'ICE')) GROUP BY minute(from_unixtime(`ack`))"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "Simple stored procedure test" do
|
225
|
+
setup do
|
226
|
+
# Create a simple stored procedure but drop it first if there
|
227
|
+
MYSQL_DB.execute("DROP PROCEDURE IF EXISTS sp_get_server_id;")
|
228
|
+
MYSQL_DB.execute("CREATE PROCEDURE sp_get_server_id() SQL SECURITY DEFINER SELECT @@SERVER_ID as server_id;")
|
229
|
+
end
|
230
|
+
|
231
|
+
specify "should return the server-id via a stored procedure call" do
|
232
|
+
@server_id = MYSQL_DB["SELECT @@SERVER_ID as server_id;"].first[:server_id] # grab the server_id via a simple query
|
233
|
+
@server_id_by_sp = MYSQL_DB["CALL sp_get_server_id();"].first[:server_id]
|
234
|
+
@server_id_by_sp.should == @server_id # compare it to output from stored procedure
|
235
|
+
end
|
236
|
+
end
|
data/spec/core_ext_spec.rb
CHANGED
@@ -8,92 +8,6 @@ context "Enumerable#send_each" do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
context "Array#to_sql" do
|
12
|
-
specify "should concatenate multiple lines into a single string" do
|
13
|
-
"SELECT * \r\nFROM items\r\n WHERE a = 1".split.to_sql. \
|
14
|
-
should == 'SELECT * FROM items WHERE a = 1'
|
15
|
-
end
|
16
|
-
|
17
|
-
specify "should remove superfluous white space and line breaks" do
|
18
|
-
"\tSELECT * \n FROM items ".split.to_sql. \
|
19
|
-
should == 'SELECT * FROM items'
|
20
|
-
end
|
21
|
-
|
22
|
-
specify "should remove ANSI SQL comments" do
|
23
|
-
"SELECT * --comment\r\n FROM items\r\n --comment".split.to_sql. \
|
24
|
-
should == 'SELECT * FROM items'
|
25
|
-
end
|
26
|
-
|
27
|
-
specify "should remove C-style comments" do
|
28
|
-
"SELECT * \r\n /* comment comment\r\n comment\r\n FROM items */\r\n FROM items\r\n--comment".split.to_sql. \
|
29
|
-
should == 'SELECT * FROM items'
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context "String#to_sql" do
|
34
|
-
specify "should concatenate multiple lines into a single string" do
|
35
|
-
"SELECT * \r\nFROM items\r\nWHERE a = 1".to_sql. \
|
36
|
-
should == 'SELECT * FROM items WHERE a = 1'
|
37
|
-
end
|
38
|
-
|
39
|
-
specify "should remove superfluous white space and line breaks" do
|
40
|
-
"\tSELECT * \r\n FROM items ".to_sql. \
|
41
|
-
should == 'SELECT * FROM items'
|
42
|
-
end
|
43
|
-
|
44
|
-
specify "should remove ANSI SQL comments" do
|
45
|
-
"SELECT * --comment \r\n FROM items\r\n --comment".to_sql. \
|
46
|
-
should == 'SELECT * FROM items'
|
47
|
-
end
|
48
|
-
|
49
|
-
specify "should remove C-style comments" do
|
50
|
-
"SELECT * \r\n/* comment comment\r\ncomment\r\nFROM items */\r\nFROM items\r\n--comment".to_sql. \
|
51
|
-
should == 'SELECT * FROM items'
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context "String#lit" do
|
56
|
-
specify "should return an LiteralString object" do
|
57
|
-
'xyz'.lit.should be_a_kind_of(Sequel::LiteralString)
|
58
|
-
'xyz'.lit.to_s.should == 'xyz'
|
59
|
-
end
|
60
|
-
|
61
|
-
specify "should inhibit string literalization" do
|
62
|
-
db = Sequel::Database.new
|
63
|
-
ds = db[:t]
|
64
|
-
|
65
|
-
ds.update_sql(:stamp => "NOW()".lit).should == \
|
66
|
-
"UPDATE t SET stamp = NOW()"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
context "String#expr" do
|
71
|
-
specify "should return an LiteralString object" do
|
72
|
-
'xyz'.expr.should be_a_kind_of(Sequel::LiteralString)
|
73
|
-
'xyz'.expr.to_s.should == 'xyz'
|
74
|
-
end
|
75
|
-
|
76
|
-
specify "should inhibit string literalization" do
|
77
|
-
db = Sequel::Database.new
|
78
|
-
ds = db[:t]
|
79
|
-
|
80
|
-
ds.update_sql(:stamp => "NOW()".expr).should == \
|
81
|
-
"UPDATE t SET stamp = NOW()"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
context "String#split_sql" do
|
86
|
-
specify "should split a string containing multiple statements" do
|
87
|
-
"DROP TABLE a; DROP TABLE c".split_sql.should == \
|
88
|
-
['DROP TABLE a', 'DROP TABLE c']
|
89
|
-
end
|
90
|
-
|
91
|
-
specify "should remove comments from the string" do
|
92
|
-
"DROP TABLE a;/* DROP TABLE b; DROP TABLE c;*/DROP TABLE d".split_sql.should == \
|
93
|
-
['DROP TABLE a', 'DROP TABLE d']
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
11
|
context "String#to_time" do
|
98
12
|
specify "should convert the string into a Time object" do
|
99
13
|
"2007-07-11".to_time.should == Time.parse("2007-07-11")
|
@@ -101,112 +15,6 @@ context "String#to_time" do
|
|
101
15
|
end
|
102
16
|
end
|
103
17
|
|
104
|
-
context "Symbol#DESC" do
|
105
|
-
specify "should append the symbol with DESC" do
|
106
|
-
:hey.DESC.should == 'hey DESC'
|
107
|
-
end
|
108
|
-
|
109
|
-
specify "should support qualified symbol notation" do
|
110
|
-
:abc__def.DESC.should == 'abc.def DESC'
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
context "Symbol#AS" do
|
115
|
-
specify "should append an AS clause" do
|
116
|
-
:hey.AS(:ho).should == 'hey AS ho'
|
117
|
-
end
|
118
|
-
|
119
|
-
specify "should support qualified symbol notation" do
|
120
|
-
:abc__def.AS(:x).should == 'abc.def AS x'
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
context "Symbol#ALL" do
|
125
|
-
specify "should format a qualified wildcard" do
|
126
|
-
:xyz.ALL.should == 'xyz.*'
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
context "Symbol#to_column_name" do
|
131
|
-
specify "should convert qualified symbol notation into dot notation" do
|
132
|
-
:abc__def.to_column_name.should == 'abc.def'
|
133
|
-
end
|
134
|
-
|
135
|
-
specify "should convert AS symbol notation into SQL AS notation" do
|
136
|
-
:xyz___x.to_column_name.should == 'xyz AS x'
|
137
|
-
:abc__def___x.to_column_name.should == 'abc.def AS x'
|
138
|
-
end
|
139
|
-
|
140
|
-
specify "should support names with digits" do
|
141
|
-
:abc2.to_column_name.should == 'abc2'
|
142
|
-
:xx__yy3.to_column_name.should == 'xx.yy3'
|
143
|
-
:ab34__temp3_4ax.to_column_name.should == 'ab34.temp3_4ax'
|
144
|
-
:x1___y2.to_column_name.should == 'x1 AS y2'
|
145
|
-
:abc2__def3___ggg4.to_column_name.should == 'abc2.def3 AS ggg4'
|
146
|
-
end
|
147
|
-
|
148
|
-
specify "should support upper case and lower case" do
|
149
|
-
:ABC.to_column_name.should == 'ABC'
|
150
|
-
:Zvashtoy__aBcD.to_column_name.should == 'Zvashtoy.aBcD'
|
151
|
-
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
context "ColumnCompositionMethods#column_title" do
|
156
|
-
specify "should return the column name for non aliased columns" do
|
157
|
-
:xyz.column_title.should == 'xyz'
|
158
|
-
:abc__xyz.column_title.should == 'xyz'
|
159
|
-
|
160
|
-
'abc'.column_title.should == 'abc'
|
161
|
-
'abc.def'.column_title.should == 'def'
|
162
|
-
end
|
163
|
-
|
164
|
-
specify "should return the column alias for aliased columns" do
|
165
|
-
:xyz___x.column_title.should == 'x'
|
166
|
-
:abc__xyz___y.column_title.should == 'y'
|
167
|
-
|
168
|
-
'abc AS x'.column_title.should == 'x'
|
169
|
-
'abc as y'.column_title.should == 'y'
|
170
|
-
'abc.def AS d'.column_title.should == 'd'
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
context "Symbol" do
|
175
|
-
specify "should support MIN for specifying min function" do
|
176
|
-
:abc__def.MIN.should == 'min(abc.def)'
|
177
|
-
end
|
178
|
-
|
179
|
-
specify "should support MAX for specifying max function" do
|
180
|
-
:abc__def.MAX.should == 'max(abc.def)'
|
181
|
-
end
|
182
|
-
|
183
|
-
specify "should support SUM for specifying sum function" do
|
184
|
-
:abc__def.SUM.should == 'sum(abc.def)'
|
185
|
-
end
|
186
|
-
|
187
|
-
specify "should support AVG for specifying avg function" do
|
188
|
-
:abc__def.AVG.should == 'avg(abc.def)'
|
189
|
-
end
|
190
|
-
|
191
|
-
specify "should support COUNT for specifying count function" do
|
192
|
-
:abc__def.COUNT.should == 'count(abc.def)'
|
193
|
-
end
|
194
|
-
|
195
|
-
specify "should support any other function using upper case letters" do
|
196
|
-
:abc__def.DADA.should == 'dada(abc.def)'
|
197
|
-
end
|
198
|
-
|
199
|
-
specify "should support upper case outer functions" do
|
200
|
-
:COUNT['1'].should == 'COUNT(1)'
|
201
|
-
end
|
202
|
-
|
203
|
-
specify "should inhibit string literalization" do
|
204
|
-
db = Sequel::Database.new
|
205
|
-
ds = db[:t]
|
206
|
-
ds.select(:COUNT['1']).sql.should == "SELECT COUNT(1) FROM t"
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
18
|
context "Range#interval" do
|
211
19
|
specify "should return the interval between the beginning and end of the range" do
|
212
20
|
(1..10).interval.should == 9
|
@@ -0,0 +1,279 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
context "Array#to_sql" do
|
4
|
+
specify "should concatenate multiple lines into a single string" do
|
5
|
+
"SELECT * \r\nFROM items\r\n WHERE a = 1".split.to_sql. \
|
6
|
+
should == 'SELECT * FROM items WHERE a = 1'
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "should remove superfluous white space and line breaks" do
|
10
|
+
"\tSELECT * \n FROM items ".split.to_sql. \
|
11
|
+
should == 'SELECT * FROM items'
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "should remove ANSI SQL comments" do
|
15
|
+
"SELECT * --comment\r\n FROM items\r\n --comment".split.to_sql. \
|
16
|
+
should == 'SELECT * FROM items'
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "should remove C-style comments" do
|
20
|
+
"SELECT * \r\n /* comment comment\r\n comment\r\n FROM items */\r\n FROM items\r\n--comment".split.to_sql. \
|
21
|
+
should == 'SELECT * FROM items'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "String#to_sql" do
|
26
|
+
specify "should concatenate multiple lines into a single string" do
|
27
|
+
"SELECT * \r\nFROM items\r\nWHERE a = 1".to_sql. \
|
28
|
+
should == 'SELECT * FROM items WHERE a = 1'
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should remove superfluous white space and line breaks" do
|
32
|
+
"\tSELECT * \r\n FROM items ".to_sql. \
|
33
|
+
should == 'SELECT * FROM items'
|
34
|
+
end
|
35
|
+
|
36
|
+
specify "should remove ANSI SQL comments" do
|
37
|
+
"SELECT * --comment \r\n FROM items\r\n --comment".to_sql. \
|
38
|
+
should == 'SELECT * FROM items'
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "should remove C-style comments" do
|
42
|
+
"SELECT * \r\n/* comment comment\r\ncomment\r\nFROM items */\r\nFROM items\r\n--comment".to_sql. \
|
43
|
+
should == 'SELECT * FROM items'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "String#lit" do
|
48
|
+
specify "should return an LiteralString object" do
|
49
|
+
'xyz'.lit.should be_a_kind_of(Sequel::LiteralString)
|
50
|
+
'xyz'.lit.to_s.should == 'xyz'
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "should inhibit string literalization" do
|
54
|
+
db = Sequel::Database.new
|
55
|
+
ds = db[:t]
|
56
|
+
|
57
|
+
ds.update_sql(:stamp => "NOW()".lit).should == \
|
58
|
+
"UPDATE t SET stamp = NOW()"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "String#expr" do
|
63
|
+
specify "should return an LiteralString object" do
|
64
|
+
'xyz'.expr.should be_a_kind_of(Sequel::LiteralString)
|
65
|
+
'xyz'.expr.to_s.should == 'xyz'
|
66
|
+
end
|
67
|
+
|
68
|
+
specify "should inhibit string literalization" do
|
69
|
+
db = Sequel::Database.new
|
70
|
+
ds = db[:t]
|
71
|
+
|
72
|
+
ds.update_sql(:stamp => "NOW()".expr).should == \
|
73
|
+
"UPDATE t SET stamp = NOW()"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "String#split_sql" do
|
78
|
+
specify "should split a string containing multiple statements" do
|
79
|
+
"DROP TABLE a; DROP TABLE c".split_sql.should == \
|
80
|
+
['DROP TABLE a', 'DROP TABLE c']
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "should remove comments from the string" do
|
84
|
+
"DROP TABLE a;/* DROP TABLE b; DROP TABLE c;*/DROP TABLE d".split_sql.should == \
|
85
|
+
['DROP TABLE a', 'DROP TABLE d']
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "#DESC/#desc" do
|
90
|
+
setup do
|
91
|
+
@ds = Sequel::Dataset.new(nil)
|
92
|
+
end
|
93
|
+
|
94
|
+
specify "should format a DESC clause for a column ref" do
|
95
|
+
:test.DESC.to_s(@ds).should == 'test DESC'
|
96
|
+
:test.desc.to_s(@ds).should == 'test DESC'
|
97
|
+
|
98
|
+
:items__price.DESC.to_s(@ds).should == 'items.price DESC'
|
99
|
+
:items__price.desc.to_s(@ds).should == 'items.price DESC'
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "should format a DESC clause for a function" do
|
103
|
+
:avg[:test].DESC.to_s(@ds).should == 'avg(test) DESC'
|
104
|
+
:avg[:test].desc.to_s(@ds).should == 'avg(test) DESC'
|
105
|
+
end
|
106
|
+
|
107
|
+
specify "should format a DESC clause for a literal value" do
|
108
|
+
1.DESC.to_s(@ds).should == '1 DESC'
|
109
|
+
'abc'.desc.to_s(@ds).should == "'abc' DESC"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "#ASC/#asc" do
|
114
|
+
setup do
|
115
|
+
@ds = Sequel::Dataset.new(nil)
|
116
|
+
end
|
117
|
+
|
118
|
+
specify "should format a ASC clause for a column ref" do
|
119
|
+
:test.ASC.to_s(@ds).should == 'test ASC'
|
120
|
+
:test.asc.to_s(@ds).should == 'test ASC'
|
121
|
+
|
122
|
+
:items__price.ASC.to_s(@ds).should == 'items.price ASC'
|
123
|
+
:items__price.asc.to_s(@ds).should == 'items.price ASC'
|
124
|
+
end
|
125
|
+
|
126
|
+
specify "should format a ASC clause for a function" do
|
127
|
+
:avg[:test].ASC.to_s(@ds).should == 'avg(test) ASC'
|
128
|
+
:avg[:test].asc.to_s(@ds).should == 'avg(test) ASC'
|
129
|
+
end
|
130
|
+
|
131
|
+
specify "should format a ASC clause for a literal value" do
|
132
|
+
1.ASC.to_s(@ds).should == '1 ASC'
|
133
|
+
'abc'.asc.to_s(@ds).should == "'abc' ASC"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "#AS/#as" do
|
138
|
+
setup do
|
139
|
+
@ds = Sequel::Dataset.new(nil)
|
140
|
+
end
|
141
|
+
|
142
|
+
specify "should format a AS clause for a column ref" do
|
143
|
+
:test.AS(:t).to_s(@ds).should == 'test AS t'
|
144
|
+
:test.as(:t).to_s(@ds).should == 'test AS t'
|
145
|
+
|
146
|
+
:items__price.AS(:p).to_s(@ds).should == 'items.price AS p'
|
147
|
+
:items__price.as(:p).to_s(@ds).should == 'items.price AS p'
|
148
|
+
end
|
149
|
+
|
150
|
+
specify "should format a AS clause for a function" do
|
151
|
+
:avg[:test].AS(:avg).to_s(@ds).should == 'avg(test) AS avg'
|
152
|
+
:avg[:test].as(:avg).to_s(@ds).should == 'avg(test) AS avg'
|
153
|
+
end
|
154
|
+
|
155
|
+
specify "should format a AS clause for a literal value" do
|
156
|
+
1.AS(:one).to_s(@ds).should == '1 AS one'
|
157
|
+
'abc'.as(:abc).to_s(@ds).should == "'abc' AS abc"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "Column references" do
|
162
|
+
setup do
|
163
|
+
@c = Class.new(Sequel::Dataset) do
|
164
|
+
def quote_column_ref(c); "`#{c}`"; end
|
165
|
+
end
|
166
|
+
@ds = @c.new(nil)
|
167
|
+
end
|
168
|
+
|
169
|
+
specify "should be quoted properly" do
|
170
|
+
@ds.literal(:xyz).should == "`xyz`"
|
171
|
+
@ds.literal(:xyz__abc).should == "xyz.`abc`"
|
172
|
+
|
173
|
+
@ds.literal(:xyz.as(:x)).should == "`xyz` AS `x`"
|
174
|
+
@ds.literal(:xyz__abc.as(:x)).should == "xyz.`abc` AS `x`"
|
175
|
+
|
176
|
+
@ds.literal(:xyz___x).should == "`xyz` AS `x`"
|
177
|
+
@ds.literal(:xyz__abc___x).should == "xyz.`abc` AS `x`"
|
178
|
+
end
|
179
|
+
|
180
|
+
specify "should be quoted properly in SQL functions" do
|
181
|
+
@ds.literal(:avg[:xyz]).should == "avg(`xyz`)"
|
182
|
+
@ds.literal(:avg[:xyz, 1]).should == "avg(`xyz`, 1)"
|
183
|
+
@ds.literal(:avg[:xyz].as(:a)).should == "avg(`xyz`) AS `a`"
|
184
|
+
end
|
185
|
+
|
186
|
+
specify "should be quoted properly in ASC/DESC clauses" do
|
187
|
+
@ds.literal(:xyz.ASC).should == "`xyz` ASC"
|
188
|
+
@ds.literal(:avg[:xyz, 1].desc).should == "avg(`xyz`, 1) DESC"
|
189
|
+
end
|
190
|
+
|
191
|
+
specify "should be quoted properly in a cast function" do
|
192
|
+
@ds.literal(:x.cast_as(:integer)).should == "cast(`x` AS integer)"
|
193
|
+
@ds.literal(:x__y.cast_as(:varchar[20])).should == "cast(x.`y` AS varchar(20))"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "Symbol#ALL/#all" do
|
198
|
+
setup do
|
199
|
+
@ds = Sequel::Dataset.new(nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
specify "should format a qualified wildcard" do
|
203
|
+
:xyz.ALL.to_s(@ds).should == 'xyz.*'
|
204
|
+
:abc.all.to_s(@ds).should == 'abc.*'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "Symbol#to_column_ref" do
|
209
|
+
setup do
|
210
|
+
@ds = Sequel::Dataset.new(nil)
|
211
|
+
end
|
212
|
+
|
213
|
+
specify "should convert qualified symbol notation into dot notation" do
|
214
|
+
:abc__def.to_column_ref(@ds).should == 'abc.def'
|
215
|
+
end
|
216
|
+
|
217
|
+
specify "should convert AS symbol notation into SQL AS notation" do
|
218
|
+
:xyz___x.to_column_ref(@ds).should == 'xyz AS x'
|
219
|
+
:abc__def___x.to_column_ref(@ds).should == 'abc.def AS x'
|
220
|
+
end
|
221
|
+
|
222
|
+
specify "should support names with digits" do
|
223
|
+
:abc2.to_column_ref(@ds).should == 'abc2'
|
224
|
+
:xx__yy3.to_column_ref(@ds).should == 'xx.yy3'
|
225
|
+
:ab34__temp3_4ax.to_column_ref(@ds).should == 'ab34.temp3_4ax'
|
226
|
+
:x1___y2.to_column_ref(@ds).should == 'x1 AS y2'
|
227
|
+
:abc2__def3___ggg4.to_column_ref(@ds).should == 'abc2.def3 AS ggg4'
|
228
|
+
end
|
229
|
+
|
230
|
+
specify "should support upper case and lower case" do
|
231
|
+
:ABC.to_column_ref(@ds).should == 'ABC'
|
232
|
+
:Zvashtoy__aBcD.to_column_ref(@ds).should == 'Zvashtoy.aBcD'
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "Symbol" do
|
237
|
+
setup do
|
238
|
+
@ds = Sequel::Dataset.new(nil)
|
239
|
+
end
|
240
|
+
|
241
|
+
specify "should support MIN for specifying min function" do
|
242
|
+
:abc__def.MIN.to_s(@ds).should == 'min(abc.def)'
|
243
|
+
end
|
244
|
+
|
245
|
+
specify "should support MAX for specifying max function" do
|
246
|
+
:abc__def.MAX.to_s(@ds).should == 'max(abc.def)'
|
247
|
+
end
|
248
|
+
|
249
|
+
specify "should support SUM for specifying sum function" do
|
250
|
+
:abc__def.SUM.to_s(@ds).should == 'sum(abc.def)'
|
251
|
+
end
|
252
|
+
|
253
|
+
specify "should support AVG for specifying avg function" do
|
254
|
+
:abc__def.AVG.to_s(@ds).should == 'avg(abc.def)'
|
255
|
+
end
|
256
|
+
|
257
|
+
specify "should support COUNT for specifying count function" do
|
258
|
+
:abc__def.COUNT.to_s(@ds).should == 'count(abc.def)'
|
259
|
+
end
|
260
|
+
|
261
|
+
specify "should support any other function using upper case letters" do
|
262
|
+
:abc__def.DADA.to_s(@ds).should == 'dada(abc.def)'
|
263
|
+
end
|
264
|
+
|
265
|
+
specify "should support upper case outer functions" do
|
266
|
+
:COUNT['1'].to_s(@ds).should == "COUNT('1')"
|
267
|
+
end
|
268
|
+
|
269
|
+
specify "should inhibit string literalization" do
|
270
|
+
db = Sequel::Database.new
|
271
|
+
ds = db[:t]
|
272
|
+
ds.select(:COUNT['1']).sql.should == "SELECT COUNT('1') FROM t"
|
273
|
+
end
|
274
|
+
|
275
|
+
specify "should support cast function" do
|
276
|
+
:abc.cast_as(:integer).to_s(@ds).should == "cast(abc AS integer)"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
data/spec/dataset_spec.rb
CHANGED
@@ -795,12 +795,12 @@ context "Dataset#qualified_column_name" do
|
|
795
795
|
end
|
796
796
|
|
797
797
|
specify "should return the same if already qualified" do
|
798
|
-
@dataset.qualified_column_name('test.a', :items).should == 'test.a'
|
798
|
+
@dataset.qualified_column_name('test.a'.lit, :items).should == 'test.a'
|
799
799
|
@dataset.qualified_column_name(:ccc__b, :items).should == 'ccc.b'
|
800
800
|
end
|
801
801
|
|
802
802
|
specify "should qualify the column with the supplied table name" do
|
803
|
-
@dataset.qualified_column_name('a', :items).should == 'items.a'
|
803
|
+
@dataset.qualified_column_name('a'.lit, :items).should == 'items.a'
|
804
804
|
@dataset.qualified_column_name(:b1, :items).should == 'items.b1'
|
805
805
|
end
|
806
806
|
end
|
@@ -1274,16 +1274,16 @@ context "Dataset#last" do
|
|
1274
1274
|
|
1275
1275
|
specify "should invert the order" do
|
1276
1276
|
@d.order(:a).last
|
1277
|
-
@c.last_dataset.opts[:order].should == [
|
1277
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:a.DESC])
|
1278
1278
|
|
1279
1279
|
@d.order(:b.DESC).last
|
1280
|
-
@c.last_dataset.opts[:order].should ==
|
1280
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal(:b)
|
1281
1281
|
|
1282
1282
|
@d.order(:c, :d).last
|
1283
|
-
@c.last_dataset.opts[:order].should == [
|
1283
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:c.DESC, :d.DESC])
|
1284
1284
|
|
1285
1285
|
@d.order(:e.DESC, :f).last
|
1286
|
-
@c.last_dataset.opts[:order].should == [
|
1286
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:e, :f.DESC])
|
1287
1287
|
end
|
1288
1288
|
|
1289
1289
|
specify "should return the first matching record if a hash is specified" do
|
data/spec/sequelizer_spec.rb
CHANGED
@@ -163,6 +163,13 @@ context "Proc#to_sql" do
|
|
163
163
|
"(x(1, (SELECT z FROM y), 'a''b') > 100)"
|
164
164
|
end
|
165
165
|
|
166
|
+
specify "should support SQL functions without arguments" do
|
167
|
+
proc {:abc[] > 100}.to_sql.should == "(abc() > 100)"
|
168
|
+
|
169
|
+
proc {:now[] - :last_stamp > 100}.to_sql.should == \
|
170
|
+
"((now() - last_stamp) > 100)"
|
171
|
+
end
|
172
|
+
|
166
173
|
specify "should do stuff like..." do
|
167
174
|
proc {:price < 100 || :category != 'ruby'}.to_sql.should == \
|
168
175
|
"((price < 100) OR (NOT (category = 'ruby')))"
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: sequel
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.3.
|
7
|
-
date: 2007-11-
|
6
|
+
version: 0.3.4.1
|
7
|
+
date: 2007-11-10 00:00:00 +02:00
|
8
8
|
summary: Lightweight ORM library for Ruby
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,7 +33,6 @@ files:
|
|
33
33
|
- README
|
34
34
|
- Rakefile
|
35
35
|
- bin/sequel
|
36
|
-
- doc/rdoc
|
37
36
|
- spec/adapters
|
38
37
|
- spec/adapters/mysql_spec.rb
|
39
38
|
- spec/adapters/oracle_spec.rb
|
@@ -42,6 +41,7 @@ files:
|
|
42
41
|
- spec/array_keys_spec.rb
|
43
42
|
- spec/connection_pool_spec.rb
|
44
43
|
- spec/core_ext_spec.rb
|
44
|
+
- spec/core_sql_spec.rb
|
45
45
|
- spec/database_spec.rb
|
46
46
|
- spec/dataset_spec.rb
|
47
47
|
- spec/migration_spec.rb
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- lib/sequel/array_keys.rb
|
58
58
|
- lib/sequel/connection_pool.rb
|
59
59
|
- lib/sequel/core_ext.rb
|
60
|
+
- lib/sequel/core_sql.rb
|
60
61
|
- lib/sequel/database.rb
|
61
62
|
- lib/sequel/dataset
|
62
63
|
- lib/sequel/dataset/convenience.rb
|