sqlpostgres 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
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: