sqlpostgres 1.2.4

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.
Files changed (207) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +22 -0
  3. data/LICENSE.md +23 -0
  4. data/README.rdoc +59 -0
  5. data/Rakefile +32 -0
  6. data/VERSION +1 -0
  7. data/doc/BUGS +2 -0
  8. data/doc/examples/README +6 -0
  9. data/doc/examples/connection.rb +16 -0
  10. data/doc/examples/connection_auto.rb +22 -0
  11. data/doc/examples/connection_ctor.rb +18 -0
  12. data/doc/examples/connection_default.rb +15 -0
  13. data/doc/examples/connection_exec.rb +18 -0
  14. data/doc/examples/connection_manual.rb +12 -0
  15. data/doc/examples/connection_wrapped_new.rb +13 -0
  16. data/doc/examples/connection_wrapped_open.rb +13 -0
  17. data/doc/examples/cursor.rb +38 -0
  18. data/doc/examples/include_module.rb +9 -0
  19. data/doc/examples/include_module2.rb +12 -0
  20. data/doc/examples/insert.rb +30 -0
  21. data/doc/examples/insert2.rb +36 -0
  22. data/doc/examples/insert_bytea.rb +16 -0
  23. data/doc/examples/insert_bytea_array.rb +17 -0
  24. data/doc/examples/insert_default_values.rb +16 -0
  25. data/doc/examples/insert_insert.rb +16 -0
  26. data/doc/examples/insert_insert_default.rb +16 -0
  27. data/doc/examples/insert_insert_select.rb +20 -0
  28. data/doc/examples/insert_select.rb +20 -0
  29. data/doc/examples/interval.rb +17 -0
  30. data/doc/examples/savepoint.rb +38 -0
  31. data/doc/examples/select.rb +33 -0
  32. data/doc/examples/select2.rb +36 -0
  33. data/doc/examples/select_cross_join.rb +18 -0
  34. data/doc/examples/select_distinct.rb +18 -0
  35. data/doc/examples/select_distinct_on +19 -0
  36. data/doc/examples/select_for_update.rb +18 -0
  37. data/doc/examples/select_from.rb +17 -0
  38. data/doc/examples/select_from_subselect.rb +20 -0
  39. data/doc/examples/select_group_by.rb +19 -0
  40. data/doc/examples/select_having.rb +20 -0
  41. data/doc/examples/select_join_on.rb +18 -0
  42. data/doc/examples/select_join_using.rb +18 -0
  43. data/doc/examples/select_limit.rb +19 -0
  44. data/doc/examples/select_natural_join.rb +18 -0
  45. data/doc/examples/select_offset.rb +19 -0
  46. data/doc/examples/select_order_by.rb +20 -0
  47. data/doc/examples/select_select.rb +30 -0
  48. data/doc/examples/select_select_alias.rb +30 -0
  49. data/doc/examples/select_select_expression.rb +31 -0
  50. data/doc/examples/select_select_literal.rb +24 -0
  51. data/doc/examples/select_union.rb +21 -0
  52. data/doc/examples/select_where_array.rb +18 -0
  53. data/doc/examples/select_where_in.rb +18 -0
  54. data/doc/examples/select_where_string.rb +18 -0
  55. data/doc/examples/simple.rb +34 -0
  56. data/doc/examples/transaction.rb +30 -0
  57. data/doc/examples/transaction_abort.rb +30 -0
  58. data/doc/examples/transaction_commit.rb +34 -0
  59. data/doc/examples/translate_substitute_values.rb +17 -0
  60. data/doc/examples/update.rb +32 -0
  61. data/doc/examples/update2.rb +44 -0
  62. data/doc/examples/update_only.rb +17 -0
  63. data/doc/examples/update_set.rb +17 -0
  64. data/doc/examples/update_set_array.rb +16 -0
  65. data/doc/examples/update_set_bytea.rb +16 -0
  66. data/doc/examples/update_set_expression.rb +16 -0
  67. data/doc/examples/update_set_subselect.rb +20 -0
  68. data/doc/examples/update_where.rb +17 -0
  69. data/doc/examples/use_prefix.rb +8 -0
  70. data/doc/examples/use_prefix2.rb +11 -0
  71. data/doc/index.html +31 -0
  72. data/doc/insertexamples.rb +9 -0
  73. data/doc/makemanual +4 -0
  74. data/doc/makerdoc +5 -0
  75. data/doc/manual.dbk +622 -0
  76. data/lib/sqlpostgres/Connection.rb +198 -0
  77. data/lib/sqlpostgres/Cursor.rb +157 -0
  78. data/lib/sqlpostgres/Delete.rb +67 -0
  79. data/lib/sqlpostgres/Exceptions.rb +15 -0
  80. data/lib/sqlpostgres/Insert.rb +279 -0
  81. data/lib/sqlpostgres/NullConnection.rb +22 -0
  82. data/lib/sqlpostgres/PgBit.rb +73 -0
  83. data/lib/sqlpostgres/PgBox.rb +37 -0
  84. data/lib/sqlpostgres/PgCidr.rb +21 -0
  85. data/lib/sqlpostgres/PgCircle.rb +75 -0
  86. data/lib/sqlpostgres/PgInet.rb +21 -0
  87. data/lib/sqlpostgres/PgInterval.rb +208 -0
  88. data/lib/sqlpostgres/PgLineSegment.rb +37 -0
  89. data/lib/sqlpostgres/PgMacAddr.rb +21 -0
  90. data/lib/sqlpostgres/PgPath.rb +64 -0
  91. data/lib/sqlpostgres/PgPoint.rb +65 -0
  92. data/lib/sqlpostgres/PgPolygon.rb +56 -0
  93. data/lib/sqlpostgres/PgTime.rb +77 -0
  94. data/lib/sqlpostgres/PgTimeWithTimeZone.rb +98 -0
  95. data/lib/sqlpostgres/PgTimestamp.rb +93 -0
  96. data/lib/sqlpostgres/PgTwoPoints.rb +54 -0
  97. data/lib/sqlpostgres/PgType.rb +34 -0
  98. data/lib/sqlpostgres/PgWrapper.rb +41 -0
  99. data/lib/sqlpostgres/Savepoint.rb +98 -0
  100. data/lib/sqlpostgres/Select.rb +855 -0
  101. data/lib/sqlpostgres/Transaction.rb +120 -0
  102. data/lib/sqlpostgres/Translate.rb +436 -0
  103. data/lib/sqlpostgres/Update.rb +188 -0
  104. data/lib/sqlpostgres.rb +67 -0
  105. data/test/Assert.rb +72 -0
  106. data/test/Connection.test.rb +246 -0
  107. data/test/Cursor.test.rb +190 -0
  108. data/test/Delete.test.rb +68 -0
  109. data/test/Insert.test.rb +123 -0
  110. data/test/MockPGconn.rb +62 -0
  111. data/test/NullConnection.test.rb +32 -0
  112. data/test/PgBit.test.rb +98 -0
  113. data/test/PgBox.test.rb +108 -0
  114. data/test/PgCidr.test.rb +61 -0
  115. data/test/PgCircle.test.rb +107 -0
  116. data/test/PgInet.test.rb +61 -0
  117. data/test/PgInterval.test.rb +180 -0
  118. data/test/PgLineSegment.test.rb +108 -0
  119. data/test/PgMacAddr.test.rb +61 -0
  120. data/test/PgPath.test.rb +106 -0
  121. data/test/PgPoint.test.rb +100 -0
  122. data/test/PgPolygon.test.rb +95 -0
  123. data/test/PgTime.test.rb +120 -0
  124. data/test/PgTimeWithTimeZone.test.rb +117 -0
  125. data/test/PgTimestamp.test.rb +134 -0
  126. data/test/RandomThings.rb +25 -0
  127. data/test/Savepoint.test.rb +286 -0
  128. data/test/Select.test.rb +930 -0
  129. data/test/Test.rb +62 -0
  130. data/test/TestConfig.rb +21 -0
  131. data/test/TestSetup.rb +13 -0
  132. data/test/TestUtil.rb +92 -0
  133. data/test/Transaction.test.rb +275 -0
  134. data/test/Translate.test.rb +354 -0
  135. data/test/Update.test.rb +227 -0
  136. data/test/roundtrip.test.rb +565 -0
  137. data/test/test +34 -0
  138. data/tools/exampleinserter/ExampleInserter.rb +177 -0
  139. data/tools/rdoc/ChangeLog +796 -0
  140. data/tools/rdoc/EXAMPLE.rb +48 -0
  141. data/tools/rdoc/MANIFEST +58 -0
  142. data/tools/rdoc/Makefile +27 -0
  143. data/tools/rdoc/NEW_FEATURES +226 -0
  144. data/tools/rdoc/README +390 -0
  145. data/tools/rdoc/ToDo +6 -0
  146. data/tools/rdoc/contrib/Index +6 -0
  147. data/tools/rdoc/contrib/xslfo/ChangeLog +181 -0
  148. data/tools/rdoc/contrib/xslfo/README +106 -0
  149. data/tools/rdoc/contrib/xslfo/TODO +10 -0
  150. data/tools/rdoc/contrib/xslfo/convert.xsl +151 -0
  151. data/tools/rdoc/contrib/xslfo/demo/README +21 -0
  152. data/tools/rdoc/contrib/xslfo/demo/rdocfo +99 -0
  153. data/tools/rdoc/contrib/xslfo/fcm.xsl +54 -0
  154. data/tools/rdoc/contrib/xslfo/files.xsl +62 -0
  155. data/tools/rdoc/contrib/xslfo/labeled-lists.xsl +66 -0
  156. data/tools/rdoc/contrib/xslfo/lists.xsl +44 -0
  157. data/tools/rdoc/contrib/xslfo/modules.xsl +152 -0
  158. data/tools/rdoc/contrib/xslfo/rdoc.xsl +75 -0
  159. data/tools/rdoc/contrib/xslfo/source.xsl +66 -0
  160. data/tools/rdoc/contrib/xslfo/styles.xsl +69 -0
  161. data/tools/rdoc/contrib/xslfo/tables.xsl +67 -0
  162. data/tools/rdoc/contrib/xslfo/utils.xsl +21 -0
  163. data/tools/rdoc/debian/changelog +33 -0
  164. data/tools/rdoc/debian/compat +1 -0
  165. data/tools/rdoc/debian/control +20 -0
  166. data/tools/rdoc/debian/copyright +10 -0
  167. data/tools/rdoc/debian/dirs +2 -0
  168. data/tools/rdoc/debian/docs +2 -0
  169. data/tools/rdoc/debian/rdoc.1 +252 -0
  170. data/tools/rdoc/debian/rdoc.manpages +1 -0
  171. data/tools/rdoc/debian/rdoc.pod +149 -0
  172. data/tools/rdoc/debian/rules +9 -0
  173. data/tools/rdoc/dot/dot.rb +255 -0
  174. data/tools/rdoc/etc/rdoc.dtd +203 -0
  175. data/tools/rdoc/install.rb +137 -0
  176. data/tools/rdoc/markup/install.rb +43 -0
  177. data/tools/rdoc/markup/sample/sample.rb +42 -0
  178. data/tools/rdoc/markup/simple_markup/fragments.rb +323 -0
  179. data/tools/rdoc/markup/simple_markup/inline.rb +348 -0
  180. data/tools/rdoc/markup/simple_markup/lines.rb +147 -0
  181. data/tools/rdoc/markup/simple_markup/preprocess.rb +68 -0
  182. data/tools/rdoc/markup/simple_markup/to_html.rb +281 -0
  183. data/tools/rdoc/markup/simple_markup.rb +474 -0
  184. data/tools/rdoc/markup/test/AllTests.rb +2 -0
  185. data/tools/rdoc/markup/test/TestInline.rb +151 -0
  186. data/tools/rdoc/markup/test/TestParse.rb +411 -0
  187. data/tools/rdoc/rdoc/code_objects.rb +536 -0
  188. data/tools/rdoc/rdoc/diagram.rb +331 -0
  189. data/tools/rdoc/rdoc/generators/chm_generator.rb +112 -0
  190. data/tools/rdoc/rdoc/generators/html_generator.rb +1268 -0
  191. data/tools/rdoc/rdoc/generators/template/chm/chm.rb +86 -0
  192. data/tools/rdoc/rdoc/generators/template/html/html.rb +705 -0
  193. data/tools/rdoc/rdoc/generators/template/html/kilmer.rb +377 -0
  194. data/tools/rdoc/rdoc/generators/template/xml/rdf.rb +110 -0
  195. data/tools/rdoc/rdoc/generators/template/xml/xml.rb +110 -0
  196. data/tools/rdoc/rdoc/generators/xml_generator.rb +130 -0
  197. data/tools/rdoc/rdoc/options.rb +451 -0
  198. data/tools/rdoc/rdoc/parsers/parse_c.rb +287 -0
  199. data/tools/rdoc/rdoc/parsers/parse_f95.rb +118 -0
  200. data/tools/rdoc/rdoc/parsers/parse_rb.rb +2311 -0
  201. data/tools/rdoc/rdoc/parsers/parse_simple.rb +37 -0
  202. data/tools/rdoc/rdoc/parsers/parserfactory.rb +75 -0
  203. data/tools/rdoc/rdoc/rdoc.rb +219 -0
  204. data/tools/rdoc/rdoc/template.rb +234 -0
  205. data/tools/rdoc/rdoc/tokenstream.rb +25 -0
  206. data/tools/rdoc/rdoc.rb +9 -0
  207. metadata +291 -0
@@ -0,0 +1,855 @@
1
+ require 'date'
2
+
3
+ module SqlPostgres
4
+
5
+ # This class creates and executes an SQL select statement.
6
+ #
7
+ # Example (assuming the values 1, 2 and null are in the database):
8
+ #
9
+ #** Example: select
10
+ # select = Select.new(connection)
11
+ # select.select('i')
12
+ # select.from('foo')
13
+ # select.order_by('i')
14
+ # p select.statement # "select i from foo order by i"
15
+ # p select.exec # [{"i"=>1}, {"i"=>2}, {"i"=>nil}]
16
+ #**
17
+
18
+ class Select
19
+
20
+ # Constructor. If no connection is given, uses the default.
21
+
22
+ def initialize(connection = Connection.default)
23
+ @connection = connection
24
+ @tables = []
25
+ @columns = []
26
+ @joins = []
27
+ @order_by = []
28
+ @where = []
29
+ @group_by = []
30
+ @having = []
31
+ @limit = nil
32
+ @offset = nil
33
+ @distinct = false
34
+ @distinct_on = []
35
+ @set_ops = []
36
+ @for_update = false
37
+ end
38
+
39
+ # Add an expression (usually just a simple column name)
40
+ # to the select statement.
41
+ #
42
+ # [expression]
43
+ # The expression to put in the select statement. Should be one of:
44
+ # [An instance of Select] The Select's SQL statement is put in
45
+ # parentheses and added to this statement.
46
+ # [String or Array] Converted by #substitute_values
47
+ # [as]
48
+ # The alias name to put in the statement and to use as the hash key
49
+ # in the result. If nil, then no alias name appears in the statement
50
+ # and the expression is used as the hash key.
51
+ #
52
+ # Example:
53
+ #** Example: select_select
54
+ # select = Select.new(connection)
55
+ # select.select('i')
56
+ # select.from('foo')
57
+ # p select.statement # "select i from foo"
58
+ # p select.exec # [{"i"=>1}]
59
+ #**
60
+ #
61
+ # Example (alias)
62
+ #** Example: select_select_alias
63
+ # select = Select.new(connection)
64
+ # select.select('i', 'number')
65
+ # select.from('foo')
66
+ # p select.statement # "select i as number from foo"
67
+ # p select.exec # [{"number"=>1}]
68
+ #**
69
+ #
70
+ # Example (expression)
71
+ #** Example: select_select_expression
72
+ # pi = 3.14
73
+ # select = Select.new(connection)
74
+ # select.select(['d * %s', pi], 'c')
75
+ # select.from('circles')
76
+ # p select.statement # "select d * 3.14 as c from circles"
77
+ # p select.exec # [{"c"=>6.28}]
78
+ #**
79
+
80
+ def select(expression, as = nil, &converter)
81
+ converter ||= AutoConverter
82
+ expression = if expression.is_a?(Select)
83
+ "(#{expression.statement})"
84
+ else
85
+ Translate.substitute_values(expression)
86
+ end
87
+ @columns << Column.new(expression, as, converter)
88
+ end
89
+
90
+ # Select a literal, automatically selecting its result type.
91
+ #
92
+ # [value]
93
+ # The value to put in the statement. This can be any of these types:
94
+ # * nil
95
+ # * Integer
96
+ # * Float
97
+ # * String
98
+ # * true or false
99
+ # * #PgTime
100
+ # * #PgInterval
101
+ # * #PgTimeWithTimeZone
102
+ # * #PgTimestamp
103
+ # * #PgPoint
104
+ # * #PgLineSegment
105
+ # * #PgBox
106
+ # * #PgPath
107
+ # * #PgPolygon
108
+ # * #PgCircle
109
+ # * #PgBit
110
+ # * #PgInet
111
+ # * #PgCidr
112
+ # * #PgMacAddr
113
+ #
114
+ # [as]
115
+ # The alias name to put in the statement and to use as the hash
116
+ # key in the result. If nil, then no alias name appears in the
117
+ # statement and the value itself is used as the hash key.
118
+ #
119
+ # Example:
120
+ #** Example: select_select_literal
121
+ # select = Select.new(connection)
122
+ # select.select_literal(2, 'n')
123
+ # select.select_literal('foo', 't')
124
+ # p select.statement # "select 2 as n, E'foo' as t"
125
+ # p select.exec # [{"n"=>2, "t"=>"foo"}]
126
+ #**
127
+
128
+ def select_literal(value, as = nil)
129
+ select(["%s", value], as)
130
+ end
131
+
132
+ # Add "distinct" to this statement.
133
+ #
134
+ # Example:
135
+ #** Example: select_distinct
136
+ # select = Select.new
137
+ # select.distinct
138
+ # select.select('i')
139
+ # select.from('foo')
140
+ # p select.statement # "select distinct i from foo"
141
+ #**
142
+
143
+ def distinct
144
+ @distinct = true
145
+ end
146
+
147
+ # Add "distinct on" to this statement.
148
+ # "distinct on" is a postgres extension.
149
+ #
150
+ # Example:
151
+ #** Example: select_distinct_on
152
+ # select = Select.new
153
+ # select.distinct_on('i')
154
+ # select.select('integer', 'i')
155
+ # select.select('integer', 'j')
156
+ # select.from('foo')
157
+ # p select.statement # "select distinct on (i) i, j from
158
+ # # foo"
159
+ #**
160
+
161
+ def distinct_on(expression)
162
+ @distinct_on << expression
163
+ end
164
+
165
+ # Add the "from" clause to the statement.
166
+ #
167
+ # [table]
168
+ # What's being selected from, which is either
169
+ # * A table name, or
170
+ # * a Select statement
171
+ # [as]
172
+ # The alias name, or nil if there isn't one
173
+ #
174
+ # Table example:
175
+ #** Example: select_from
176
+ # select = Select.new
177
+ # select.select('i')
178
+ # select.from('foo')
179
+ # p select.statement # "select i from foo"
180
+ #**
181
+ #
182
+ # Subselect example:
183
+ #** Example: select_from_subselect
184
+ # subselect = Select.new
185
+ # subselect.select('i')
186
+ # subselect.from('foo')
187
+ # select = Select.new
188
+ # select.select('i')
189
+ # select.from(subselect, 'bar')
190
+ # p select.statement # "select i from (select i from foo) as bar"
191
+ #**
192
+
193
+ def from(table, as = nil)
194
+ table = "(#{table.statement})" if table.is_a?(Select)
195
+ @tables << [table, as].compact.join(' as ')
196
+ end
197
+
198
+ # Add the "union" set operation to this statement.
199
+ # You may call this multiple times.
200
+ #
201
+ # The right-hand-side of the union is put in parentheses.
202
+ # This makes it possible to force the order when doing multiple
203
+ # set operations.
204
+ #
205
+ # Example
206
+ #** Example: select_union
207
+ # select2 = Select.new
208
+ # select2.select('i')
209
+ # select2.from('bar')
210
+ # select1 = Select.new
211
+ # select1.select('i')
212
+ # select1.from('foo')
213
+ # select1.union(select2)
214
+ # p select1.statement # "select i from foo union (select i from
215
+ # # bar)"
216
+ #**
217
+
218
+ def union(select)
219
+ add_set_op('union', select)
220
+ end
221
+
222
+ # Add the "union all" set operation to this statement.
223
+ # See #union.
224
+
225
+ def union_all(select)
226
+ add_set_op('union all', select)
227
+ end
228
+
229
+ # Add the "intersect" set operation to this statement.
230
+ # See #union.
231
+
232
+ def intersect(select)
233
+ add_set_op('intersect', select)
234
+ end
235
+
236
+ # Add the "intersect all" set operation to this statement.
237
+ # See #union.
238
+
239
+ def intersect_all(select)
240
+ add_set_op('intersect all', select)
241
+ end
242
+
243
+ # Add the "except" set operation to this statement.
244
+ # See #union.
245
+
246
+ def except(select)
247
+ add_set_op('except', select)
248
+ end
249
+
250
+ # Add the "except all" set operation to this statement.
251
+ # See #union.
252
+
253
+ def except_all(select)
254
+ add_set_op('except all', select)
255
+ end
256
+
257
+ # Add a natural join to this statement.
258
+ #
259
+ # Example:
260
+ #** Example: select_natural_join
261
+ # select = Select.new
262
+ # select.select('i')
263
+ # select.from('foo')
264
+ # select.natural_join('bar')
265
+ # p select.statement # "select i from foo natural join bar"
266
+ #**
267
+
268
+ def natural_join(table)
269
+ @joins << "natural join #{table}"
270
+ end
271
+
272
+ # Add a "join using" to this statement.
273
+ #
274
+ # [joinType]
275
+ # One of:
276
+ # * 'inner'
277
+ # * 'left outer'
278
+ # * 'right outer'
279
+ # * 'full outer'
280
+ # [table]
281
+ # The table being joined
282
+ # [*columns]
283
+ # One or more column names.
284
+ #
285
+ # Example:
286
+ #** Example: select_join_using
287
+ # select = Select.new
288
+ # select.select('i')
289
+ # select.from('foo')
290
+ # select.join_using('inner', 'bar', 'i', 'j')
291
+ # p select.statement # "select i from foo inner join bar using (i, j)"
292
+ #**
293
+
294
+ def join_using(joinType, table, *columns)
295
+ @joins << "#{joinType} join #{table} using (#{columns.join(', ')})"
296
+ end
297
+
298
+ # Add a "join on" to this statement.
299
+ #
300
+ # [joinType]
301
+ # One of:
302
+ # * 'inner'
303
+ # * 'left outer'
304
+ # * 'right outer'
305
+ # * 'full outer'
306
+ # [table]
307
+ # The table being joined
308
+ # [condition]
309
+ # A string or array that will be converted by #substitute_values
310
+ # and inserted into the statement.
311
+ #
312
+ # Example:
313
+ #** Example: select_join_on
314
+ # select = Select.new
315
+ # select.select('i')
316
+ # select.from('foo')
317
+ # select.join_on('inner', 'bar', 'foo.i = bar.j')
318
+ # p select.statement # "select i from foo inner join bar on (foo.i =
319
+ # # bar.j)"
320
+ #**
321
+
322
+ def join_on(joinType, table, condition)
323
+ @joins << ("#{joinType} join #{table} on "\
324
+ "(#{Translate.substitute_values(condition)})")
325
+ end
326
+
327
+ # Add a "cross join" to this statement.
328
+ #
329
+ # Example:
330
+ #** Example: select_cross_join
331
+ # select = Select.new
332
+ # select.select('i')
333
+ # select.from('foo')
334
+ # select.cross_join('bar')
335
+ # p select.statement # "select i from foo cross join bar"
336
+ #**
337
+
338
+ def cross_join(table)
339
+ @joins << "cross join #{table}"
340
+ end
341
+
342
+ # Add an "order by" to this statement. You can call this as many
343
+ # times as you need.
344
+ #
345
+ # [expression]
346
+ # A string or array that will be converted by #substitute_values
347
+ # and inserted into the statement.
348
+ # [ordering]
349
+ # One of:
350
+ # 'asc':: ascending
351
+ # 'desc':: descending
352
+ # nil:: default ordering, which is ascending
353
+ #
354
+ # Example:
355
+ #** Example: select_order_by
356
+ # select = Select.new
357
+ # select.select('i')
358
+ # select.select('j')
359
+ # select.from('foo')
360
+ # select.order_by('i')
361
+ # select.order_by('j', 'desc')
362
+ # p select.statement # "select i, j from foo order by i, j desc"
363
+ #**
364
+
365
+ def order_by(expression, ordering = nil)
366
+ @order_by <<
367
+ [Translate.substitute_values(expression), ordering].compact.join(' ')
368
+ end
369
+
370
+ # Add a "where" condition to this statement.
371
+ #
372
+ # [expression]
373
+ # The condition. Should be one of:
374
+ # [A string] The expression
375
+ # [An array] An expression converted using #substitute_values
376
+ #
377
+ # Example (string)
378
+ #** Example: select_where_string
379
+ # select = Select.new
380
+ # select.select('i')
381
+ # select.from('foo')
382
+ # select.where('i > 0')
383
+ # p select.statement # "select i from foo where i > 0"
384
+ #**
385
+ #
386
+ # Example (array)
387
+ #** Example: select_where_array
388
+ # select = Select.new
389
+ # select.select('age')
390
+ # select.from('person')
391
+ # select.where(['name = %s', 'Smith'])
392
+ # p select.statement # "select age from person where name =
393
+ # # E'Smith'"
394
+ #**
395
+ #
396
+ # Example (in)
397
+ #** Example: select_where_in
398
+ # select = Select.new
399
+ # select.select('s')
400
+ # select.from('foo')
401
+ # select.where(['s in %s', [:in, 'foo', 'bar']])
402
+ # p select.statement # "select s from foo where s in (E'foo',
403
+ # # E'bar')"
404
+ #**
405
+
406
+ def where(expression)
407
+ @where << Translate.substitute_values(expression)
408
+ end
409
+
410
+ # Add a "group by" to this statement.
411
+ #
412
+ # [expression]
413
+ # A string or array that will be converted by #substitute_values
414
+ # and inserted into the statement.
415
+ #
416
+ # Example
417
+ #** Example: select_group_by
418
+ # select = Select.new
419
+ # select.select('i')
420
+ # select.select('count(*)', 'count')
421
+ # select.from('foo')
422
+ # select.group_by('i')
423
+ # p select.statement # "select i, count(*) as count from foo group
424
+ # # by i"
425
+ #**
426
+
427
+ def group_by(expression)
428
+ @group_by << Translate.substitute_values(expression)
429
+ end
430
+
431
+ # Add a "having" clause to this statement.
432
+ #
433
+ # [expression]
434
+ # A string or array that will be converted by #substitute_values
435
+ # and inserted into the statement.
436
+ #
437
+ # Example
438
+ #** Example: select_having
439
+ # select = Select.new
440
+ # select.select('i')
441
+ # select.select('count(*)', 'count')
442
+ # select.from('foo')
443
+ # select.group_by('i')
444
+ # select.having('i < 10')
445
+ # p select.statement # "select i, count(*) as count from foo
446
+ # # group by i having i < 10"
447
+ #**
448
+
449
+ def having(expression)
450
+ @having << Translate.substitute_values(expression)
451
+ end
452
+
453
+ # Add a "limit" clause to the statement.
454
+ #
455
+ # Example:
456
+ #** Example: select_limit
457
+ # select = Select.new
458
+ # select.select('i')
459
+ # select.from('foo')
460
+ # select.order_by('i')
461
+ # select.limit(1)
462
+ # p select.statement # "select i from foo order by i limit 1"
463
+ #**
464
+
465
+ def limit(value)
466
+ @limit = value
467
+ end
468
+
469
+ # Add an "offset" clause to the statement.
470
+ #
471
+ # Example:
472
+ #** Example: select_offset
473
+ # select = Select.new
474
+ # select.select('i')
475
+ # select.from('foo')
476
+ # select.order_by('i')
477
+ # select.offset(1)
478
+ # p select.statement # "select i from foo order by i offset 1"
479
+ #**
480
+
481
+ def offset(value)
482
+ @offset = value
483
+ end
484
+
485
+ # Add "for update" to the statement.
486
+ #
487
+ # Example:
488
+ #** Example: select_for_update
489
+ # select = Select.new
490
+ # select.select('i')
491
+ # select.from('foo')
492
+ # select.for_update
493
+ # p select.statement # "select i from foo for update"
494
+ #**
495
+
496
+ def for_update
497
+ @for_update = true
498
+ end
499
+
500
+ # Return the SQL statement.
501
+
502
+ def statement
503
+ "select#{distinct_clause} #{expression_list}"\
504
+ "#{tableExpression}#{join_clause}"\
505
+ "#{where_clause}#{set_ops_clause}#{group_by_clause}#{having_clause}#{order_by_clause}"\
506
+ "#{limit_clause}#{offset_clause}#{for_update_clause}"
507
+ end
508
+
509
+ # Execute the statement and return an array of hashes with the result.
510
+ #
511
+ # [connection]
512
+ # If present, the connection to use.
513
+ # If nil, uses the connection passed to new or, if no connection was
514
+ # passed to new, uses the default connection.
515
+
516
+ def exec(connection = @connection)
517
+ query_and_translate(connection, statement)
518
+ end
519
+
520
+ # Fetch a row or rows from the cursor. Not intended for consumer
521
+ # use; it's hanging out here in public for the use of the Cursor
522
+ # class.
523
+ #
524
+ # [cursor_name]
525
+ # The cursor's name
526
+ # [direction]
527
+ # A string specifying which row or rows to fetch (see Cursor.fetch)
528
+ # [connection]
529
+ # If present, the connection to use.
530
+ # If nil, uses the connection passed to new or, if no connection was
531
+ # passed to new, uses the default connection.
532
+
533
+ def fetch_by_cursor(cursor_name, direction, connection = @connection)
534
+ statement = "fetch #{direction} from #{cursor_name}"
535
+ query_and_translate(connection, statement)
536
+ end
537
+
538
+ private
539
+
540
+ Column = Struct.new(:value, :as, :converter)
541
+
542
+ # OIDs for Postgresql data types. These must match
543
+ # postgresql/server/catalog/pg_type.h.
544
+
545
+ module Types
546
+ BOOLEAN = 16
547
+ BYTEA = 17
548
+ QCHAR = 18
549
+ NAME = 19
550
+ BIGINT = INT8 = BIGSERIAL = SERIAL8 = 20
551
+ SMALLINT = 21
552
+ INTEGER = INT = INT4 = SERIAL = 23
553
+ TEXT = 25
554
+ OID = 26
555
+ POINT = 600
556
+ LSEG = 601
557
+ PATH = 602
558
+ BOX = 603
559
+ POLYGON = 604
560
+ CIDR = 650
561
+ ARRAY_CIDR = 651
562
+ REAL = 700
563
+ DOUBLE_PRECISION = FLOAT8 = 701
564
+ UNKNOWN = 705
565
+ CIRCLE = 718
566
+ ARRAY_CIRCLE = 719
567
+ MACADDR = 829
568
+ INET = 869
569
+ ARRAY_BOOLEAN = 1000
570
+ ARRAY_BYTEA = 1001
571
+ ARRAY_QCHAR = 1002
572
+ ARRAY_NAME = 1003
573
+ ARRAY_SMALLINT = 1005
574
+ ARRAY_INTEGER = 1007
575
+ ARRAY_TEXT = 1009
576
+ ARRAY_CHARACTER = 1014
577
+ ARRAY_VARCHAR = 1015
578
+ ARRAY_BIGINT = 1016
579
+ ARRAY_POINT = 1017
580
+ ARRAY_LSEG = 1018
581
+ ARRAY_PATH = 1019
582
+ ARRAY_BOX = 1020
583
+ ARRAY_REAL = 1021
584
+ ARRAY_DOUBLE_PRECISION = 1022
585
+ ARRAY_POLYGON = 1027
586
+ ARRAY_MACADDR = 1040
587
+ ARRAY_INET = 1041
588
+ CHARACTER = CHAR = 1042
589
+ VARCHAR = CHARACTER_VARYING = 1043
590
+ DATE = 1082
591
+ TIME = 1083
592
+ TIMESTAMP = TIMESTAMP_WITHOUT_TIME_ZONE = 1114
593
+ ARRAY_TIMESTAMP = 1115
594
+ ARRAY_DATE = 1182
595
+ ARRAY_TIME = 1183
596
+ TIMESTAMP_WITH_TIME_ZONE = 1184
597
+ ARRAY_TIMESTAMP_WITH_TIME_ZONE = 1185
598
+ INTERVAL = 1186
599
+ ARRAY_INTERVAL = 1187
600
+ ARRAY_NUMERIC = 1231
601
+ TIME_WITH_TIME_ZONE = 1266
602
+ ARRAY_TIME_WITH_TIME_ZONE = 1270
603
+ BIT = 1560
604
+ ARRAY_BIT = 1561
605
+ VARBIT = 1562
606
+ ARRAY_VARBIT = 1563
607
+ NUMERIC = DECIMAL = 1700
608
+ end
609
+
610
+ # Converters used to translate strings into Ruby types.
611
+
612
+ BitConverter = proc { |s| PgBit.from_sql(s) }
613
+ BooleanConverter = proc { |s| s == 't' }
614
+ BoxConverter = proc { |s| PgBox.from_sql(s) }
615
+ ByteaConverter = proc { |s| Translate.unescape_bytea(s) }
616
+ CidrConverter = proc { |s| PgCidr.from_sql(s) }
617
+ CircleConverter = proc { |s| PgCircle.from_sql(s) }
618
+ DateConverter = proc { |s| Translate.sql_to_date(s) }
619
+ FloatConverter = proc { |s| s.to_f }
620
+ InetConverter = proc { |s| PgInet.from_sql(s) }
621
+ IntegerConverter = proc { |s| s.to_i }
622
+ IntervalConverter = proc { |s| PgInterval.from_sql(s) }
623
+ LsegConverter = proc { |s| PgLineSegment.from_sql(s) }
624
+ MacAddrConverter = proc { |s| PgMacAddr.from_sql(s) }
625
+ PathConverter = proc { |s| PgPath.from_sql(s) }
626
+ PointConverter = proc { |s| PgPoint.from_sql(s) }
627
+ PolygonConverter = proc { |s| PgPolygon.from_sql(s) }
628
+ QCharConverter = proc { |s| Translate.unescape_qchar(s) }
629
+ StringConverter = proc { |s| s }
630
+ TimeConverter = proc { |s| PgTime.from_sql(s) }
631
+ TimeStringConverter = proc { |s| Time.local(*s.split(/:/)) }
632
+ TimeWithTimeZoneConverter = proc { |s| PgTimeWithTimeZone.from_sql(s) }
633
+ TimestampConverter = proc { |s| PgTimestamp.from_sql(s) }
634
+ TimestampTzConverter = proc { |s| Translate.sql_to_datetime(s) }
635
+
636
+ # Map each base (non-array) type to a converter.
637
+
638
+ CONVERTERS = {
639
+ Types::BIGINT => IntegerConverter,
640
+ Types::BIT => BitConverter,
641
+ Types::BOOLEAN => BooleanConverter,
642
+ Types::BOX => BoxConverter,
643
+ Types::BYTEA => ByteaConverter,
644
+ Types::CHARACTER => StringConverter,
645
+ Types::CIDR => CidrConverter,
646
+ Types::CIRCLE => CircleConverter,
647
+ Types::DATE => DateConverter,
648
+ Types::DOUBLE_PRECISION => FloatConverter,
649
+ Types::INET => InetConverter,
650
+ Types::INTEGER => IntegerConverter,
651
+ Types::INTERVAL => IntervalConverter,
652
+ Types::LSEG => LsegConverter,
653
+ Types::MACADDR => MacAddrConverter,
654
+ Types::NAME => StringConverter,
655
+ Types::NUMERIC => FloatConverter,
656
+ Types::OID => IntegerConverter,
657
+ Types::PATH => PathConverter,
658
+ Types::POINT => PointConverter,
659
+ Types::POLYGON => PolygonConverter,
660
+ Types::QCHAR => QCharConverter,
661
+ Types::REAL => FloatConverter,
662
+ Types::SMALLINT => IntegerConverter,
663
+ Types::TEXT => StringConverter,
664
+ Types::TIME => TimeConverter,
665
+ Types::TIMESTAMP => TimestampConverter,
666
+ Types::TIMESTAMP_WITH_TIME_ZONE => TimestampTzConverter,
667
+ Types::TIME_WITH_TIME_ZONE => TimeWithTimeZoneConverter,
668
+ Types::UNKNOWN => StringConverter,
669
+ Types::VARBIT => BitConverter,
670
+ Types::VARCHAR => StringConverter,
671
+ }
672
+
673
+ # Map each array type to its base type.
674
+
675
+ ARRAY_ELEMENT_TYPES = {
676
+ Types::ARRAY_BIGINT => Types::BIGINT,
677
+ Types::ARRAY_BIT => Types::BIT,
678
+ Types::ARRAY_BOOLEAN => Types::BOOLEAN,
679
+ Types::ARRAY_BOX => Types::BOX,
680
+ Types::ARRAY_BYTEA => Types::BYTEA,
681
+ Types::ARRAY_CHARACTER => Types::CHARACTER,
682
+ Types::ARRAY_INET => Types::INET,
683
+ Types::ARRAY_CIDR => Types::CIDR,
684
+ Types::ARRAY_CIRCLE => Types::CIRCLE,
685
+ Types::ARRAY_DATE => Types::DATE,
686
+ Types::ARRAY_DOUBLE_PRECISION => Types::DOUBLE_PRECISION,
687
+ Types::ARRAY_INTEGER => Types::INTEGER,
688
+ Types::ARRAY_INTERVAL => Types::INTERVAL,
689
+ Types::ARRAY_LSEG => Types::LSEG,
690
+ Types::ARRAY_MACADDR => Types::MACADDR,
691
+ Types::ARRAY_NAME => Types::NAME,
692
+ Types::ARRAY_NUMERIC => Types::NUMERIC,
693
+ Types::ARRAY_PATH => Types::PATH,
694
+ Types::ARRAY_POINT => Types::POINT,
695
+ Types::ARRAY_POLYGON => Types::POLYGON,
696
+ Types::ARRAY_QCHAR => Types::QCHAR,
697
+ Types::ARRAY_REAL => Types::REAL,
698
+ Types::ARRAY_SMALLINT => Types::SMALLINT,
699
+ Types::ARRAY_TEXT => Types::TEXT,
700
+ Types::ARRAY_TIME => Types::TIME,
701
+ Types::ARRAY_TIMESTAMP => Types::TIMESTAMP,
702
+ Types::ARRAY_TIMESTAMP_WITH_TIME_ZONE => Types::TIMESTAMP_WITH_TIME_ZONE,
703
+ Types::ARRAY_TIME_WITH_TIME_ZONE => Types::TIME_WITH_TIME_ZONE,
704
+ Types::ARRAY_VARBIT => Types::VARBIT,
705
+ Types::ARRAY_VARCHAR => Types::VARCHAR,
706
+ }
707
+
708
+ AutoConverter = proc { |s, type_code|
709
+ array_element_type = ARRAY_ELEMENT_TYPES[type_code]
710
+ if !array_element_type.nil?
711
+ s = Translate.sql_to_array(s)
712
+ type_code = array_element_type
713
+ end
714
+ converter = CONVERTERS[type_code] || StringConverter
715
+ Translate.deep_collect(s) do |e|
716
+ converter.call(e)
717
+ end
718
+ }
719
+
720
+ def data_type_for(value, as)
721
+ return nil if as.nil?
722
+ if value.is_a?(Float)
723
+ 'float'
724
+ elsif value.is_a?(Integer)
725
+ 'integer'
726
+ elsif value == true || value == false
727
+ 'boolean'
728
+ elsif value.is_a?(Time)
729
+ 'time'
730
+ else
731
+ 'string'
732
+ end
733
+ end
734
+
735
+ def distinct_clause
736
+ if @distinct
737
+ " distinct"
738
+ elsif !@distinct_on.empty?
739
+ " distinct on (#{@distinct_on.join(', ')})"
740
+ else
741
+ ""
742
+ end
743
+ end
744
+
745
+ def add_set_op(op, select)
746
+ @set_ops << [op, select.statement]
747
+ end
748
+
749
+ def expression_list
750
+ @columns.collect do |column|
751
+ [column.value, column.as].compact.join(' as ')
752
+ end.join(', ')
753
+ end
754
+
755
+ def tableExpression
756
+ if @tables.empty?
757
+ ""
758
+ else
759
+ " from #{@tables.join(', ')}"
760
+ end
761
+ end
762
+
763
+ def join_clause
764
+ if @joins.empty?
765
+ ""
766
+ else
767
+ " " + @joins.join(' ')
768
+ end
769
+ end
770
+
771
+ def set_ops_clause
772
+ if @set_ops.empty?
773
+ ""
774
+ else
775
+ ' ' + @set_ops.collect do |op, statement|
776
+ "#{op} (#{statement})"
777
+ end.join(' ')
778
+ end
779
+ end
780
+
781
+ def random_order?
782
+ ENV['RANDOM_SQL_ORDER'] && !@distinct && @distinct_on.empty? && @set_ops.empty?
783
+ end
784
+
785
+ def order_by_clause
786
+ order = @order_by.dup
787
+ order << 'random()' if random_order?
788
+ if order.empty?
789
+ ""
790
+ else
791
+ " order by " + order.join(', ')
792
+ end
793
+ end
794
+
795
+ def limit_clause
796
+ if @limit.nil?
797
+ ""
798
+ else
799
+ " limit #{@limit}"
800
+ end
801
+ end
802
+
803
+ def offset_clause
804
+ if @offset.nil?
805
+ ""
806
+ else
807
+ " offset #{@offset}"
808
+ end
809
+ end
810
+
811
+ def for_update_clause
812
+ if @for_update
813
+ " for update"
814
+ else
815
+ ""
816
+ end
817
+ end
818
+
819
+ def where_clause
820
+ if @where.empty?
821
+ ""
822
+ else
823
+ " where " + @where.join(' and ')
824
+ end
825
+ end
826
+
827
+ def group_by_clause
828
+ if @group_by.empty?
829
+ ""
830
+ else
831
+ " group by " + @group_by.join(', ')
832
+ end
833
+ end
834
+
835
+ def having_clause
836
+ if @having.empty?
837
+ ""
838
+ else
839
+ " having " + @having.join(' and ')
840
+ end
841
+ end
842
+
843
+ def query_and_translate(connection, statement)
844
+ connection.exec_and_translate(statement, @columns)
845
+ end
846
+
847
+ end
848
+
849
+ end
850
+
851
+ # Local Variables:
852
+ # tab-width: 2
853
+ # ruby-indent-level: 2
854
+ # indent-tabs-mode: nil
855
+ # End: