colincasey-sequel 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +7 -1
  2. data/doc/advanced_associations.rdoc +614 -0
  3. data/doc/cheat_sheet.rdoc +223 -0
  4. data/doc/dataset_filtering.rdoc +158 -0
  5. data/doc/prepared_statements.rdoc +104 -0
  6. data/doc/release_notes/1.0.txt +38 -0
  7. data/doc/release_notes/1.1.txt +143 -0
  8. data/doc/release_notes/1.3.txt +101 -0
  9. data/doc/release_notes/1.4.0.txt +53 -0
  10. data/doc/release_notes/1.5.0.txt +155 -0
  11. data/doc/release_notes/2.0.0.txt +298 -0
  12. data/doc/release_notes/2.1.0.txt +271 -0
  13. data/doc/release_notes/2.10.0.txt +328 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/doc/schema.rdoc +29 -0
  23. data/doc/sharding.rdoc +113 -0
  24. data/lib/sequel.rb +1 -0
  25. data/lib/sequel_core/adapters/ado.rb +89 -0
  26. data/lib/sequel_core/adapters/db2.rb +143 -0
  27. data/lib/sequel_core/adapters/dbi.rb +112 -0
  28. data/lib/sequel_core/adapters/do/mysql.rb +38 -0
  29. data/lib/sequel_core/adapters/do/postgres.rb +92 -0
  30. data/lib/sequel_core/adapters/do/sqlite.rb +31 -0
  31. data/lib/sequel_core/adapters/do.rb +205 -0
  32. data/lib/sequel_core/adapters/firebird.rb +298 -0
  33. data/lib/sequel_core/adapters/informix.rb +85 -0
  34. data/lib/sequel_core/adapters/jdbc/h2.rb +69 -0
  35. data/lib/sequel_core/adapters/jdbc/mysql.rb +66 -0
  36. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  37. data/lib/sequel_core/adapters/jdbc/postgresql.rb +113 -0
  38. data/lib/sequel_core/adapters/jdbc/sqlite.rb +43 -0
  39. data/lib/sequel_core/adapters/jdbc.rb +491 -0
  40. data/lib/sequel_core/adapters/mysql.rb +369 -0
  41. data/lib/sequel_core/adapters/odbc.rb +174 -0
  42. data/lib/sequel_core/adapters/openbase.rb +68 -0
  43. data/lib/sequel_core/adapters/oracle.rb +107 -0
  44. data/lib/sequel_core/adapters/postgres.rb +456 -0
  45. data/lib/sequel_core/adapters/shared/ms_access.rb +110 -0
  46. data/lib/sequel_core/adapters/shared/mssql.rb +102 -0
  47. data/lib/sequel_core/adapters/shared/mysql.rb +325 -0
  48. data/lib/sequel_core/adapters/shared/oracle.rb +61 -0
  49. data/lib/sequel_core/adapters/shared/postgres.rb +715 -0
  50. data/lib/sequel_core/adapters/shared/progress.rb +31 -0
  51. data/lib/sequel_core/adapters/shared/sqlite.rb +265 -0
  52. data/lib/sequel_core/adapters/sqlite.rb +248 -0
  53. data/lib/sequel_core/connection_pool.rb +258 -0
  54. data/lib/sequel_core/core_ext.rb +217 -0
  55. data/lib/sequel_core/core_sql.rb +202 -0
  56. data/lib/sequel_core/database/schema.rb +164 -0
  57. data/lib/sequel_core/database.rb +691 -0
  58. data/lib/sequel_core/dataset/callback.rb +13 -0
  59. data/lib/sequel_core/dataset/convenience.rb +237 -0
  60. data/lib/sequel_core/dataset/pagination.rb +96 -0
  61. data/lib/sequel_core/dataset/prepared_statements.rb +220 -0
  62. data/lib/sequel_core/dataset/query.rb +41 -0
  63. data/lib/sequel_core/dataset/schema.rb +15 -0
  64. data/lib/sequel_core/dataset/sql.rb +1010 -0
  65. data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
  66. data/lib/sequel_core/dataset/unsupported.rb +43 -0
  67. data/lib/sequel_core/dataset.rb +511 -0
  68. data/lib/sequel_core/deprecated.rb +26 -0
  69. data/lib/sequel_core/exceptions.rb +44 -0
  70. data/lib/sequel_core/migration.rb +212 -0
  71. data/lib/sequel_core/object_graph.rb +230 -0
  72. data/lib/sequel_core/pretty_table.rb +71 -0
  73. data/lib/sequel_core/schema/generator.rb +320 -0
  74. data/lib/sequel_core/schema/sql.rb +325 -0
  75. data/lib/sequel_core/schema.rb +2 -0
  76. data/lib/sequel_core/sql.rb +887 -0
  77. data/lib/sequel_core/version.rb +11 -0
  78. data/lib/sequel_core.rb +172 -0
  79. data/lib/sequel_model/association_reflection.rb +267 -0
  80. data/lib/sequel_model/associations.rb +499 -0
  81. data/lib/sequel_model/base.rb +523 -0
  82. data/lib/sequel_model/caching.rb +82 -0
  83. data/lib/sequel_model/dataset_methods.rb +26 -0
  84. data/lib/sequel_model/eager_loading.rb +370 -0
  85. data/lib/sequel_model/exceptions.rb +7 -0
  86. data/lib/sequel_model/hooks.rb +101 -0
  87. data/lib/sequel_model/inflector.rb +281 -0
  88. data/lib/sequel_model/plugins.rb +62 -0
  89. data/lib/sequel_model/record.rb +568 -0
  90. data/lib/sequel_model/schema.rb +49 -0
  91. data/lib/sequel_model/validations.rb +429 -0
  92. data/lib/sequel_model.rb +91 -0
  93. data/spec/adapters/ado_spec.rb +46 -0
  94. data/spec/adapters/firebird_spec.rb +376 -0
  95. data/spec/adapters/informix_spec.rb +96 -0
  96. data/spec/adapters/mysql_spec.rb +881 -0
  97. data/spec/adapters/oracle_spec.rb +244 -0
  98. data/spec/adapters/postgres_spec.rb +687 -0
  99. data/spec/adapters/spec_helper.rb +10 -0
  100. data/spec/adapters/sqlite_spec.rb +555 -0
  101. data/spec/integration/dataset_test.rb +134 -0
  102. data/spec/integration/eager_loader_test.rb +696 -0
  103. data/spec/integration/prepared_statement_test.rb +130 -0
  104. data/spec/integration/schema_test.rb +180 -0
  105. data/spec/integration/spec_helper.rb +58 -0
  106. data/spec/integration/type_test.rb +96 -0
  107. data/spec/rcov.opts +6 -0
  108. data/spec/sequel_core/connection_pool_spec.rb +526 -0
  109. data/spec/sequel_core/core_ext_spec.rb +156 -0
  110. data/spec/sequel_core/core_sql_spec.rb +522 -0
  111. data/spec/sequel_core/database_spec.rb +1188 -0
  112. data/spec/sequel_core/dataset_spec.rb +3481 -0
  113. data/spec/sequel_core/expression_filters_spec.rb +363 -0
  114. data/spec/sequel_core/migration_spec.rb +261 -0
  115. data/spec/sequel_core/object_graph_spec.rb +272 -0
  116. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  117. data/spec/sequel_core/schema_generator_spec.rb +167 -0
  118. data/spec/sequel_core/schema_spec.rb +780 -0
  119. data/spec/sequel_core/spec_helper.rb +55 -0
  120. data/spec/sequel_core/version_spec.rb +7 -0
  121. data/spec/sequel_model/association_reflection_spec.rb +93 -0
  122. data/spec/sequel_model/associations_spec.rb +1767 -0
  123. data/spec/sequel_model/base_spec.rb +419 -0
  124. data/spec/sequel_model/caching_spec.rb +215 -0
  125. data/spec/sequel_model/dataset_methods_spec.rb +78 -0
  126. data/spec/sequel_model/eager_loading_spec.rb +1165 -0
  127. data/spec/sequel_model/hooks_spec.rb +485 -0
  128. data/spec/sequel_model/inflector_spec.rb +119 -0
  129. data/spec/sequel_model/model_spec.rb +588 -0
  130. data/spec/sequel_model/plugins_spec.rb +80 -0
  131. data/spec/sequel_model/record_spec.rb +1184 -0
  132. data/spec/sequel_model/schema_spec.rb +90 -0
  133. data/spec/sequel_model/spec_helper.rb +78 -0
  134. data/spec/sequel_model/validations_spec.rb +1067 -0
  135. data/spec/spec.opts +0 -0
  136. data/spec/spec_config.rb.example +10 -0
  137. metadata +177 -3
@@ -0,0 +1,223 @@
1
+ = Cheat Sheet
2
+
3
+ == Open a database
4
+
5
+ require 'rubygems'
6
+ require 'sequel'
7
+
8
+ DB = Sequel.sqlite('my_blog.db')
9
+ DB = Sequel.connect('postgres://user:password@localhost/my_db')
10
+ DB = Sequel.mysql('my_db', :user => 'user', :password => 'password', :host => 'localhost')
11
+ DB = Sequel.ado('mydb')
12
+
13
+ == Open an SQLite memory database
14
+
15
+ Without a filename argument, the sqlite adapter will setup a new sqlite database in RAM.
16
+
17
+ DB = Sequel.sqlite
18
+
19
+ == Logging SQL statements
20
+
21
+ require 'logger'
22
+ DB = Sequel.sqlite '', :loggers => [Logger.new($stdout)]
23
+ # or
24
+ DB.loggers << Logger.new(...)
25
+
26
+ == Using raw SQL
27
+
28
+ DB << "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT(3) NOT NULL)"
29
+ DB.fetch("SELECT name FROM users") do |row|
30
+ p r[:name]
31
+ end
32
+ dataset = DB["SELECT age FROM users WHERE name = ?", name]
33
+ dataset.print
34
+ dataset.map(:age)
35
+
36
+ == Create a dataset
37
+
38
+ dataset = DB[:items]
39
+ dataset = DB.dataset.from(:items)
40
+
41
+ == Most dataset methods are chainable
42
+
43
+ dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department)
44
+ # or
45
+ dataset = DB.query do
46
+ from :managers
47
+ where :salary => 5000..10000
48
+ order :name, :department
49
+ end
50
+
51
+ == Insert rows
52
+
53
+ dataset.insert(:name => 'Sharon', :grade => 50)
54
+ dataset << {:name => 'Sharon', :grade => 50} # same effect as above
55
+
56
+ == Retrieve rows
57
+
58
+ dataset.each {|r| p r}
59
+ dataset.all #=> [{...}, {...}, ...]
60
+ dataset.first
61
+ dataset.order(:name).last # works only for ordered datasets
62
+
63
+ == Update/Delete rows
64
+
65
+ dataset.filter(:active => false).delete
66
+ dataset.filter('price < ?', 100).update(:active => true)
67
+
68
+ == Datasets are Enumerable
69
+
70
+ dataset.map {|r| r[:name]}
71
+ dataset.map(:name) # same effect as above
72
+
73
+ dataset.inject(0){|sum, r| sum + r[:value]}
74
+
75
+ == Filtering (see also doc/dataset_filtering.rdoc)
76
+
77
+ dataset.filter(:name => 'abc')
78
+ dataset.filter('name = ?', 'abc')
79
+ dataset.filter{|o| o.value > 100}
80
+ dataset.exclude{|o| o.value <= 100}
81
+
82
+ dataset.filter(:value => 50..100)
83
+ dataset.where{|o| (o.value >= 50) & (o.value <= 100)}
84
+
85
+ dataset.where('value IN ?', [50,75,100])
86
+
87
+ # Get the first record that matches a condition
88
+ dataset[:name => 'abc'] # Same as:
89
+ dataset.filter(:name => 'abc').first
90
+
91
+ # Filter using a subquery
92
+ dataset.filter('price > ?', dataset.select('AVG(price) + 100'))
93
+
94
+ === Advanced filtering using ruby expressions
95
+
96
+ DB[:items].filter{|o| o.price < 100}.sql
97
+ #=> "SELECT * FROM items WHERE (price < 100)"
98
+
99
+ DB[:items].filter(:name.like('AL%')).sql
100
+ #=> "SELECT * FROM items WHERE (name LIKE 'AL%')"
101
+
102
+ There's support for nested expressions with AND, OR and NOT:
103
+
104
+ DB[:items].filter{|o| (o.x > 5) & (o.y > 10)}.sql
105
+ #=> "SELECT * FROM items WHERE ((x > 5) AND (y > 10))"
106
+
107
+ DB[:items].filter({:x => 1, :y => 2}.sql_or & ~{:z => 3}).sql
108
+ #=> "SELECT * FROM items WHERE (((x = 1) OR (y = 2)) AND (z != 3))"
109
+
110
+ You can use arithmetic operators and specify SQL functions:
111
+
112
+ DB[:items].filter((:x + :y) > :z).sql
113
+ #=> "SELECT * FROM items WHERE ((x + y) > z)"
114
+
115
+ DB[:items].filter{|o| :price - 100 < o.AVG(:price)}.sql
116
+ #=> "SELECT * FROM items WHERE ((price - 100) < AVG(price))"
117
+
118
+ == Ordering
119
+
120
+ dataset.order(:kind)
121
+ dataset.reverse_order(:kind)
122
+ dataset.order(:kind.desc, :name)
123
+
124
+ == Row ranges
125
+
126
+ dataset.limit(30) # LIMIT 30
127
+ dataset.limit(30, 10) # LIMIT 30 OFFSET 10
128
+
129
+ == Pagination
130
+
131
+ paginated = dataset.paginate(1, 10) # first page, 10 rows per page
132
+ paginated.page_count #=> number of pages in dataset
133
+ paginated.current_page #=> 1
134
+ paginated.next_page #=> next page number or nil
135
+ paginated.prev_page #=> previous page number or nil
136
+ paginated.first_page? #=> true if page number = 1
137
+ paginated.last_page? #=> true if page number = page_count
138
+
139
+ == Joins
140
+
141
+ DB[:items].left_outer_join(:categories, :id => :category_id).sql #=>
142
+ "SELECT * FROM items LEFT OUTER JOIN categories ON categories.id = items.category_id"
143
+
144
+ == Summarizing
145
+
146
+ dataset.count #=> record count
147
+ dataset.max(:price)
148
+ dataset.min(:price)
149
+ dataset.avg(:price)
150
+ dataset.sum(:stock)
151
+
152
+ dataset.group(:category).select(:category, :AVG.sql_function(:price))
153
+
154
+ == SQL Functions / Literals
155
+
156
+ dataset.update(:updated_at => :NOW.sql_function)
157
+ dataset.update(:updated_at => 'NOW()'.lit)
158
+
159
+ dataset.update(:updated_at => "DateValue('1/1/2001')".lit)
160
+ dataset.update(:updated_at => :DateValue.sql_function('1/1/2001'))
161
+
162
+ == Schema Manipulation
163
+
164
+ DB.create_table :items do
165
+ primary_key :id
166
+ String :name, :unique => true, :null => false
167
+ boolean :active, :default => true
168
+ foreign_key :category_id, :categories
169
+ Time :created_at
170
+
171
+ index :grade
172
+ end
173
+
174
+ DB.drop_table :items
175
+
176
+ DB.create_table :test do
177
+ String :zipcode, :size => 10
178
+ enum :system, :elements => ['mac', 'linux', 'windows']
179
+ end
180
+
181
+ == Aliasing
182
+
183
+ DB[:items].select(:name.as(:item_name))
184
+ DB[:items].select(:name___item_name)
185
+ DB[:items___items_table].select(:items_table__name___item_name)
186
+ # => "SELECT items_table.name AS item_name FROM items AS items_table"
187
+
188
+ == Transactions
189
+
190
+ DB.transaction do
191
+ dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
192
+ dataset << {:first_name => 'Farm', :last_name => 'Boy'}
193
+ end # Either both are inserted or neither are inserted
194
+
195
+ Database#transaction is re-entrant:
196
+
197
+ DB.transaction do # BEGIN issued only here
198
+ DB.transaction
199
+ dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
200
+ end
201
+ end # COMMIT issued only here
202
+
203
+ Transactions are aborted if an error is raised:
204
+
205
+ DB.transaction do
206
+ raise "some error occurred"
207
+ end # ROLLBACK issued and the error is re-raised
208
+
209
+ Transactions can also be aborted by raising Sequel::Error::Rollback:
210
+
211
+ DB.transaction do
212
+ raise(Sequel::Error::Rollback) if something_bad_happened
213
+ end # ROLLBACK issued and no error raised
214
+
215
+ Miscellaneous:
216
+
217
+ dataset.sql #=> "SELECT * FROM items"
218
+ dataset.delete_sql #=> "DELETE FROM items"
219
+ dataset.where(:name => 'sequel').exists #=> "EXISTS ( SELECT 1 FROM items WHERE name = 'sequel' )"
220
+ dataset.print #=> pretty table print to $stdout
221
+ dataset.columns #=> array of columns in the result set, does a SELECT
222
+ DB.schema(:items) => [[:id, {:type=>:integer, ...}], [:name, {:type=>:string, ...}], ...]
223
+ # Works on PostgreSQL, MySQL, SQLite, and JDBC
@@ -0,0 +1,158 @@
1
+ = Dataset Filtering
2
+
3
+ Sequel offers unparalleled flexibility when it comes to filtering records. You can specify your conditions as a custom string, as a string with parameters, as a hash of values to compare against, or as ruby code that Sequel translates into SQL expressions.
4
+
5
+ == Filtering using a custom filter string
6
+
7
+ If you do not wish to lose control over your SQL WHERE clauses, you can just supply it to the dataset's #filter method:
8
+
9
+ items.filter('x < 10').sql
10
+ #=> "SELECT * FROM items WHERE x < 10"
11
+
12
+ In order to prevent SQL injection, you can replace literal values with question marks and supply the values as additional arguments:
13
+
14
+ items.filter('category = ?', 'ruby').sql
15
+ #=> "SELECT * FROM items WHERE category = 'ruby'"
16
+
17
+ === Specifying SQL functions
18
+
19
+ Sequel also allows you to specify functions by using the Symbol#sql_function method (and the Symbol#[] method on ruby 1.8):
20
+
21
+ items.literal(:avg.sql_function(:price)) #=> "avg(price)"
22
+
23
+ == Filtering using a hash
24
+
25
+ If you just need to compare records against values, you can supply a hash:
26
+
27
+ items.filter(:category => 'ruby').sql
28
+ #=> "SELECT * FROM items WHERE (category = 'ruby')"
29
+
30
+ Sequel can check for null values:
31
+
32
+ items.filter(:category => nil).sql
33
+ #=> "SELECT * FROM items WHERE (category IS NULL)"
34
+
35
+ Or compare two columns:
36
+
37
+ items.filter(:x => :some_table__y).sql
38
+ #=> "SELECT * FROM items WHERE (x = some_table.y)"
39
+
40
+ And also compare against multiple values:
41
+
42
+ items.filter(:category => ['ruby', 'perl']).sql
43
+ #=> "SELECT * FROM items WHERE (category IN ('ruby', 'perl'))"
44
+
45
+ Ranges (both inclusive and exclusive) can also be used:
46
+
47
+ items.filter(:price => 100..200).sql
48
+ #=> "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
49
+
50
+ items.filter(:price => 100...200).sql
51
+ #=> "SELECT * FROM items WHERE (price >= 100 AND price < 200)"
52
+
53
+ == Filtering using expressions
54
+
55
+ Sequel allows you to use ruby expressions directly in the call to filter, without using a block:
56
+
57
+ items.filter(:price * 2 < 50).sql
58
+ #=> "SELECT * FROM items WHERE ((price * 2) < 50)
59
+
60
+ This works for the standard inequality and arithmetic operators:
61
+
62
+ items.filter(:price + 100 < 200).sql
63
+ #=> "SELECT * FROM items WHERE ((price + 100) < 200)
64
+
65
+ items.filter(:price - 100 > 200).sql
66
+ #=> "SELECT * FROM items WHERE ((price - 100) > 200)
67
+
68
+ items.filter(:price * 100 <= 200).sql
69
+ #=> "SELECT * FROM items WHERE ((price * 100) <= 200)
70
+
71
+ items.filter(:price / 100 >= 200).sql
72
+ #=> "SELECT * FROM items WHERE ((price / 100) >= 200)
73
+
74
+ You use the overloaded bitwise and (&) and or (|) operators to combine expressions:
75
+
76
+ items.filter((:price + 100 < 200) & (:price * 100 <= 200)).sql
77
+ #=> "SELECT * FROM items WHERE (((price + 100) < 200) AND ((price * 100) <= 200))
78
+
79
+ items.filter((:price - 100 > 200) | (:price / 100 >= 200)).sql
80
+ #=> "SELECT * FROM items WHERE (((price - 100) > 200) OR ((price / 100) >= 200))
81
+
82
+ To filter by equality, you use the standard hash, which can be combined with other operators:
83
+
84
+ items.filter({:category => 'ruby'} & (:price + 100 < 200)).sql
85
+ #=> "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
86
+
87
+ This works with other hash values, such as arrays and ranges:
88
+
89
+ items.filter({:category => ['ruby', 'other']} | (:price - 100 > 200)).sql
90
+ #=> "SELECT * FROM items WHERE ((category IN ('ruby', 'other')) OR ((price - 100) <= 200))"
91
+
92
+ items.filter({:price => (100..200)} & :active).sql
93
+ #=> "SELECT * FROM items WHERE ((price >= 100 AND price <= 200) AND active)"
94
+
95
+ === Negating conditions
96
+
97
+ You can use the negation operator (~) in most cases:
98
+
99
+ items.filter(~{:category => 'ruby'}).sql
100
+ #=> "SELECT * FROM items WHERE (category != 'ruby')"
101
+
102
+ items.filter {~:active}.sql
103
+ #=> "SELECT * FROM items WHERE NOT active"
104
+
105
+ items.filter(~(:price / 100 >= 200)).sql
106
+ #=> "SELECT * FROM items WHERE ((price / 100) < 200)
107
+
108
+ === Comparing against column references
109
+
110
+ You can also compare against other columns:
111
+
112
+ items.filter{|o| o.credit > :debit}.sql
113
+ #=> "SELECT * FROM items WHERE (credit > debit)
114
+
115
+ Or against SQL functions:
116
+
117
+ items.filter{|o| :price - 100 < o.max(:price)}.sql
118
+ #=> "SELECT * FROM items WHERE ((price - 100) < max(price))"
119
+
120
+ == String search functions
121
+
122
+ You can search SQL strings using the #like method:
123
+
124
+ items.filter(:name.like('Acme%')).sql
125
+ #=> "SELECT * FROM items WHERE (name LIKE 'Acme%')"
126
+
127
+ You can specify a Regexp as a like argument, but this will probably only work
128
+ on PostgreSQL and MySQL:
129
+
130
+ items.filter(:name.like(/Acme.*/)).sql
131
+ #=> "SELECT * FROM items WHERE (name ~ 'Acme.*')"
132
+
133
+ Like can also take more than one argument:
134
+
135
+ items.filter(:name.like('Acme%', /Beta.*/)).sql
136
+ #=> "SELECT * FROM items WHERE ((name LIKE 'Acme%') OR (name ~ 'Beta.*'))"
137
+
138
+ == String concatenation
139
+
140
+ You can concatenate SQL strings using Array#sql_string_join:
141
+
142
+ items.filter([:name, :comment].sql_string_join.like('%acme%')).sql
143
+ #=> "SELECT * FROM items WHERE ((name || comment) LIKE 'Acme%')"
144
+
145
+ Array#sql_string_join also takes a join argument:
146
+
147
+ items.filter([:name, :comment].sql_string_join(' ').like('%acme%')).sql
148
+ #=> "SELECT * FROM items WHERE ((name || ' ' || comment) LIKE 'Acme%')"
149
+
150
+ == Filtering using sub-queries
151
+
152
+ One of the best features of Sequel is the ability to use datasets as sub-queries. Sub-queries can be very useful for filtering records, and many times provide a simpler alternative to table joins. Sub-queries can be used in all forms of filters:
153
+
154
+ refs = consumer_refs.filter(:logged_in => true).select(:consumer_id)
155
+ consumers.filter(:id => refs).sql
156
+ #=> "SELECT * FROM consumers WHERE (id IN (SELECT consumer_id FROM consumer_refs WHERE (logged_in = 't')))"
157
+
158
+ Note that if you compare against a sub-query, you must select a single column in the sub-query.
@@ -0,0 +1,104 @@
1
+ = Prepared Statements and Bound Variables
2
+
3
+ Starting with version 2.4.0, Sequel has support for prepared statements and
4
+ bound variables. No matter which database you are using, the Sequel prepared
5
+ statement/bound variable API remains exactly the same. There is native support
6
+ for prepared statements/bound variables on the following databases:
7
+
8
+ * PostgreSQL (using the pg driver, requires type specifiers)
9
+ * MySQL (prepared statements only, as the ruby mysql driver doesn't support
10
+ bound variables)
11
+ * SQLite (a new native prepared statement is used for each call, though)
12
+ * JDBC (using the postgresql, mysql, or sqlite databases, and possibly others)
13
+
14
+ Support on other databases is emulated via the usual string interpolation.
15
+
16
+ == Placeholders
17
+
18
+ Generally, when using prepared statements (and certainly when using bound
19
+ variables), you need to put placeholders in your SQL to indicate where you
20
+ want your bound arguments to appear. Database support and syntax vary
21
+ significantly for placeholders (e.g. :name, $1, ?). Sequel abstracts all of
22
+ that and allows you to specify placeholders by using the :$name format for
23
+ placeholders, e.g.:
24
+
25
+ ds = DB[:items].filter(:name=>:$name)
26
+
27
+ == Bound Variables
28
+
29
+ Using bound variables for this query is simple:
30
+
31
+ ds.call(:select, :name=>'Jim')
32
+
33
+ This will do the equivalent of selecting records that have the name 'Jim'. It
34
+ returns all records, and can take a block that is passed to Dataset#all.
35
+
36
+ Deleting or returning the first record works similarly:
37
+
38
+ ds.call(:first, :name=>'Jim') # First record with name 'Jim'
39
+ ds.call(:delete, :name=>'Jim') # Delete records with name 'Jim'
40
+
41
+ For inserting/updating records, you should also specify a value hash, which
42
+ may itself contain placeholders:
43
+
44
+ # Insert record with 'Jim', note that the previous filter is ignored
45
+ ds.call(:insert, {:name=>'Jim'}, :name=>:$name)
46
+ # Change name to 'Bob' for all records with name of 'Jim'
47
+ ds.call(:update, {:name=>'Jim', :new_name=>'Bob'}, :name=>$:new_name)
48
+
49
+ == Prepared Statements
50
+
51
+ Prepared statement support is similar to bound variable support, but you
52
+ use Dataset#prepare with a name, and Dataset#call later with the values:
53
+
54
+ ds = DB[:items].filter(:name=>:$name)
55
+ ps = ds.prepare(:select, :select_by_name)
56
+ ps.call(:name=>'Jim')
57
+ DB.call(:select_by_name, :name=>'Jim') # same as above
58
+
59
+ The Dataset#prepare method returns a prepared statement, and also stores a
60
+ copy of the prepared statement in the database for later use. For insert
61
+ and update queries, the hash to insert/update is passed to prepare:
62
+
63
+ ps1 = DB[:items].prepare(:insert, :insert_with_name, :name=>:$name)
64
+ ps1.call(:name=>'Jim')
65
+ DB.call(:insert_with_name, :name=>'Jim') # same as above
66
+ ds = DB[:items].filter(:name=>:$name)
67
+ ps2 = ds.prepare(:update, :update_name, :name=>:$new_name)
68
+ ps2.call(:name=>'Jim', :new_name=>'Bob')
69
+ DB.call(:update_name, :name=>'Jim', :new_name=>'Bob') # same as above
70
+
71
+ == Database support
72
+
73
+ === PostgreSQL
74
+
75
+ If you are using the ruby-postgres or postgres-pr driver, PostgreSQL uses the
76
+ default emulated support. If you are using ruby-pg, there is native support,
77
+ but it requires type specifiers most of the time. This is easy if you have
78
+ direct control over the SQL string, but since Sequel abstracts that, the types
79
+ have to be specified another way. This is done by adding a __* suffix to the
80
+ placeholder symbol (e.g. :$name__text, which will be compiled to "$1::text"
81
+ in the SQL). Prepared statements are always server side.
82
+
83
+ === SQLite
84
+
85
+ SQLite supports bound variables and prepared statements exactly the same, since
86
+ a new native prepared statement is created and executed for each call.
87
+
88
+ === MySQL
89
+
90
+ The MySQL ruby driver does not support bound variables, so the the bound
91
+ variable methods fall back to string interpolation. It uses server side
92
+ prepared statements.
93
+
94
+ === JDBC
95
+
96
+ JDBC supports both prepared statements and bound variables. Whether these
97
+ are server side or client side depends on the JDBC driver. For PostgreSQL
98
+ over JDBC, you can add the prepareThreshold=N parameter to the connection
99
+ string, which will use a server side prepared statement after N calls to
100
+ the prepared statement.
101
+
102
+ === All Others
103
+
104
+ Support is emulated using interpolation.
@@ -0,0 +1,38 @@
1
+ === New code organization
2
+
3
+ Sequel is now divided into two parts: sequel_core and sequel_model.
4
+ These two parts are distributed as two separate gems. The sequel gem
5
+ bundles sequel_core and sequel_model together. If you don't use
6
+ Sequel::Model in your code, you can just install and use sequel_core.
7
+
8
+ === New model hooks implementation
9
+
10
+ The hooks implementation have been rewritten from scratch, is much
11
+ more robust and offers a few new features:
12
+
13
+ * More ways to define hooks: hooks can now be defined by supplying a
14
+ block or a method name, or by overriding the hook instance method.
15
+
16
+ * Inheritable hooks: Hooks can now be inherited, which means that you
17
+ can define general hooks in a model superclass, and use them in
18
+ subclasses. You can also define global hooks on Sequel::Model that
19
+ will be invoked for all model classes.
20
+
21
+ * Hook chains can be broken by returning false from within the hook.
22
+
23
+ * New after_initialize hook, invoked after instance initialization.
24
+
25
+ * The hook invocation order can no longer be changed. Hooks are
26
+ invoked in order of definition, from the top of the class hierarchy
27
+ (that is, from Sequel::Model) down to the specific class.
28
+
29
+ === Miscellanea
30
+
31
+ * Removed deprecated adapter stubs, and all other deprecations in both
32
+ sequel_core and sequel_model.
33
+
34
+ * Fixed String#to_time to raise error correctly for invalid time
35
+ stamps.
36
+
37
+ * Fixed error behavior when parse_tree or ruby2ruby are not available.
38
+
@@ -0,0 +1,143 @@
1
+ === DRY Sequel models
2
+
3
+ With the new Sequel release you no longer need to explicitly specify
4
+ the table
5
+ name for each model class, assuming your model name is the singular of
6
+ the
7
+ table name (just like in ActiveRecord or DataMapper):
8
+
9
+ class UglyBug < Sequel::Model
10
+ end
11
+
12
+ UglyBug.table_name #=> :ugly_bugs
13
+
14
+ === New model validations and support for virtual attributes
15
+
16
+ Sequel model now include validation functionality which largly follows
17
+ the
18
+ validations offered in ActiveRecord. Validations can be checked
19
+ anytime by
20
+ calling Model#valid?, with validation errors accessible through
21
+ Model#errors:
22
+
23
+ class Item < Sequel::Model
24
+ validates_presence_of :name
25
+ end
26
+
27
+ my_item = Item.new
28
+ my_item.valid? #=> false
29
+ my_item.errors.full_messages #=> ["name is not present"]
30
+
31
+ The Model#save method has been changed to check for validity before
32
+ saving. If
33
+ the model instance is not valid, the #save method returns false
34
+ without saving
35
+ the instance. You can also bypass the validity test by calling
36
+ Model#save!
37
+ instead.
38
+
39
+ Model classes also now support virtual attributes, letting you assign
40
+ values to
41
+ any attribute (virtual or persistent) at initialization time:
42
+
43
+ class User < Sequel::Model
44
+ attr_accessor :password
45
+ end
46
+
47
+ u = User.new(:password => 'blah', ...)
48
+ u.password #=> 'blah'
49
+
50
+ Also, virtual attributes can be validated just like persistent
51
+ attributes.
52
+
53
+ === Other changes (long list!)
54
+
55
+ * Added Model#reload as alias to Model#refresh.
56
+
57
+ * Changed Model.create to accept a block (#126).
58
+
59
+ * Fixed Model#initialize to accept nil values (#115).
60
+
61
+ * Added Model#update_with_params method with support for virtual
62
+ attributes and auto-filtering of unrelated parameters, and changed
63
+ Model.create_with_params to support virtual attributes (#128).
64
+
65
+ * Fixed Model.dataset to correctly set the dataset if using implicit
66
+ naming or inheriting the superclass dataset (thanks celldee).
67
+
68
+ * Finalized support for virtual attributes.
69
+
70
+ * Fixed Model#set to work with string keys (#143).
71
+
72
+ * Fixed Model.create to correctly initialize instances marked as new
73
+ (#135).
74
+
75
+ * Fixed Model#initialize to convert string keys into symbol keys. This
76
+ also fixes problem with validating objects initialized with string
77
+ keys (#136).
78
+
79
+ * Added Dataset#table_exists? convenience method.
80
+
81
+ * Changed Dataset#group_and_count to accept multiple columns (#134).
82
+
83
+ * Added Dataset#select_all method.
84
+
85
+ * Added Dataset#select_more, Dataset#order_more methods (#129).
86
+
87
+ * Fixed Dataset#count to work correctly for grouped datasets (#144).
88
+
89
+ * Fixed joining datasets using aliased tables (#140).
90
+
91
+ * Added support for UNSIGNED constraint, used in MySQL? (#127).
92
+
93
+ * Implemented constraint definitions inside Database#create_table.
94
+
95
+ * Enhanced Database.connect to accept options with string keys, so it
96
+ can now accept options loaded from YAML files. Database.connect also
97
+ automatically converts :username option into :user for compatibility
98
+ with existing YAML configuration files for AR and DataMapper.
99
+
100
+ * Changed ODBC::Database to support connection using driver and
101
+ database name, also added support for untitled columns in
102
+ ODBC::Dataset (thanks Leonid Borisenko).
103
+
104
+ * Changed MySQL adapter to support specifying socket option.
105
+
106
+ * Fixed MySQL adapter to correctly format foreign key definitions
107
+ (#123).
108
+
109
+ * Changed MySQL::Dataset to allow HAVING clause on ungrouped datasets,
110
+ and put HAVING clause before ORDER BY clause (#133).
111
+
112
+ * Changed mysql adapter to default to localhost if :host option is not
113
+ specified (#114).
114
+
115
+ * Added String#to_date. Updated mysql adapter to use String#to_date
116
+ for mysql date types (thanks drfreeze).
117
+
118
+ * Fixed postgres adapter to define PGconn#async_exec as alias to #exec
119
+ if not defined (for pure-ruby postgres driver).
120
+
121
+ * Changed postgres adapter to quote column references using double
122
+ quotes.
123
+
124
+ * Applied patch for oracle adapter: fix behavior of limit and offset,
125
+ transactions, #table_exists?, #tables and additional specs (thanks
126
+ Liming Lian #122).
127
+
128
+ * Added support additional field types in postgresql adapter (#146).
129
+
130
+ * Added support for date field types in postgresql adapter (#145).
131
+
132
+ * Added support for limiting and paginating datasets with fixed SQL,
133
+ e.g. using Database#fetch.
134
+
135
+ * Added new Dataset#from_self method that returns a dataset selecting
136
+ from the original dataset.
137
+
138
+ * Allow for additional filters on a grouped dataset (#119 and #120)
139
+
140
+ * Refactored Sequelizer to use Proc#to_sexp (method provided by r2r).
141
+
142
+ * Fixed bin/sequel to require sequel_model if available.
143
+