sequel 0.3.3 → 0.3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +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
|