sequel 3.21.0 → 3.39.0

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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -0,0 +1,263 @@
1
+ # The pg_array_ops extension adds support to Sequel's DSL to make
2
+ # it easier to call PostgreSQL array functions and operators.
3
+ #
4
+ # To load the extension:
5
+ #
6
+ # Sequel.extension :pg_array_ops
7
+ #
8
+ # The most common usage is passing an expression to Sequel.pg_array_op:
9
+ #
10
+ # ia = Sequel.pg_array_op(:int_array_column)
11
+ #
12
+ # If you have also loaded the pg_array extension, you can use
13
+ # Sequel.pg_array as well:
14
+ #
15
+ # ia = Sequel.pg_array(:int_array_column)
16
+ #
17
+ # Also, on most Sequel expression objects, you can call the pg_array
18
+ # method:
19
+ #
20
+ # ia = Sequel.expr(:int_array_column).pg_array
21
+ #
22
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
23
+ # you can also call Symbol#pg_array:
24
+ #
25
+ # ia = :int_array_column.pg_array
26
+ #
27
+ # This creates a Sequel::Postgres::ArrayOp object that can be used
28
+ # for easier querying:
29
+ #
30
+ # ia[1] # int_array_column[1]
31
+ # ia[1][2] # int_array_column[1][2]
32
+ #
33
+ # ia.contains(:other_int_array_column) # @>
34
+ # ia.contained_by(:other_int_array_column) # <@
35
+ # ia.overlaps(:other_int_array_column) # &&
36
+ # ia.concat(:other_int_array_column) # ||
37
+ #
38
+ # ia.push(1) # int_array_column || 1
39
+ # ia.unshift(1) # 1 || int_array_column
40
+ #
41
+ # ia.any # ANY(int_array_column)
42
+ # ia.all # ALL(int_array_column)
43
+ # ia.dims # array_dims(int_array_column)
44
+ # ia.length # array_length(int_array_column, 1)
45
+ # ia.length(2) # array_length(int_array_column, 2)
46
+ # ia.lower # array_lower(int_array_column, 1)
47
+ # ia.lower(2) # array_lower(int_array_column, 2)
48
+ # ia.join # array_to_string(int_array_column, '', NULL)
49
+ # ia.join(':') # array_to_string(int_array_column, ':', NULL)
50
+ # ia.join(':', ' ') # array_to_string(int_array_column, ':', ' ')
51
+ # ia.unnest # unnest(int_array_column)
52
+ #
53
+ # See the PostgreSQL array function and operator documentation for more
54
+ # details on what these functions and operators do.
55
+ #
56
+ # If you are also using the pg_array extension, you should load it before
57
+ # loading this extension. Doing so will allow you to use PGArray#op to get
58
+ # an ArrayOp, allowing you to perform array operations on array literals.
59
+ module Sequel
60
+ module Postgres
61
+ # The ArrayOp class is a simple container for a single object that
62
+ # defines methods that yield Sequel expression objects representing
63
+ # PostgreSQL array operators and functions.
64
+ #
65
+ # In the method documentation examples, assume that:
66
+ #
67
+ # array_op = :array.pg_array
68
+ class ArrayOp < Sequel::SQL::Wrapper
69
+ CONCAT = ["(".freeze, " || ".freeze, ")".freeze].freeze
70
+ CONTAINS = ["(".freeze, " @> ".freeze, ")".freeze].freeze
71
+ CONTAINED_BY = ["(".freeze, " <@ ".freeze, ")".freeze].freeze
72
+ OVERLAPS = ["(".freeze, " && ".freeze, ")".freeze].freeze
73
+
74
+ # Access a member of the array, returns an SQL::Subscript instance:
75
+ #
76
+ # array_op[1] # array[1]
77
+ def [](key)
78
+ Sequel::SQL::Subscript.new(self, [key])
79
+ end
80
+
81
+ # Call the ALL function:
82
+ #
83
+ # array_op.all # ALL(array)
84
+ #
85
+ # Usually used like:
86
+ #
87
+ # dataset.where(1=>array_op.all)
88
+ # # WHERE (1 = ALL(array))
89
+ def all
90
+ function(:ALL)
91
+ end
92
+
93
+ # Call the ANY function:
94
+ #
95
+ # array_op.all # ANY(array)
96
+ #
97
+ # Usually used like:
98
+ #
99
+ # dataset.where(1=>array_op.any)
100
+ # # WHERE (1 = ANY(array))
101
+ def any
102
+ function(:ANY)
103
+ end
104
+
105
+ # Use the contains (@>) operator:
106
+ #
107
+ # array_op.contains(:a) # (array @> a)
108
+ def contains(other)
109
+ bool_op(CONTAINS, wrap_array(other))
110
+ end
111
+
112
+ # Use the contained by (<@) operator:
113
+ #
114
+ # array_op.contained_by(:a) # (array <@ a)
115
+ def contained_by(other)
116
+ bool_op(CONTAINED_BY, wrap_array(other))
117
+ end
118
+
119
+ # Call the array_dims method:
120
+ #
121
+ # array_op.dims # array_dims(array)
122
+ def dims
123
+ function(:array_dims)
124
+ end
125
+
126
+ # Call the array_length method:
127
+ #
128
+ # array_op.length # array_length(array, 1)
129
+ # array_op.length(2) # array_length(array, 2)
130
+ def length(dimension = 1)
131
+ function(:array_length, dimension)
132
+ end
133
+
134
+ # Call the array_lower method:
135
+ #
136
+ # array_op.lower # array_lower(array, 1)
137
+ # array_op.lower(2) # array_lower(array, 2)
138
+ def lower(dimension = 1)
139
+ function(:array_lower, dimension)
140
+ end
141
+
142
+ # Use the overlaps (&&) operator:
143
+ #
144
+ # array_op.overlaps(:a) # (array && a)
145
+ def overlaps(other)
146
+ bool_op(OVERLAPS, wrap_array(other))
147
+ end
148
+
149
+ # Use the concatentation (||) operator:
150
+ #
151
+ # array_op.push(:a) # (array || a)
152
+ # array_op.concat(:a) # (array || a)
153
+ def push(other)
154
+ array_op(CONCAT, [self, wrap_array(other)])
155
+ end
156
+ alias concat push
157
+
158
+ # Return the receiver.
159
+ def pg_array
160
+ self
161
+ end
162
+
163
+ # Call the array_to_string method:
164
+ #
165
+ # array_op.join # array_to_string(array, '', NULL)
166
+ # array_op.to_string # array_to_string(array, '', NULL)
167
+ # array_op.join(":") # array_to_string(array, ':', NULL)
168
+ # array_op.join(":", "*") # array_to_string(array, ':', '*')
169
+ def to_string(joiner="", null=nil)
170
+ function(:array_to_string, joiner, null)
171
+ end
172
+ alias join to_string
173
+
174
+ # Call the unnest method:
175
+ #
176
+ # array_op.unnest # unnest(array)
177
+ def unnest
178
+ function(:unnest)
179
+ end
180
+
181
+ # Use the concatentation (||) operator, reversing the order:
182
+ #
183
+ # array_op.unshift(:a) # (a || array)
184
+ def unshift(other)
185
+ array_op(CONCAT, [wrap_array(other), self])
186
+ end
187
+
188
+ private
189
+
190
+ # Return a placeholder literal with the given str and args, wrapped
191
+ # in an ArrayOp, used by operators that return arrays.
192
+ def array_op(str, args)
193
+ ArrayOp.new(Sequel::SQL::PlaceholderLiteralString.new(str, args))
194
+ end
195
+
196
+ # Return a placeholder literal with the given str and args, wrapped
197
+ # in a boolean expression, used by operators that return booleans.
198
+ def bool_op(str, other)
199
+ Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(str, [value, other]))
200
+ end
201
+
202
+ # Return a function with the given name, and the receiver as the first
203
+ # argument, with any additional arguments given.
204
+ def function(name, *args)
205
+ SQL::Function.new(name, self, *args)
206
+ end
207
+
208
+ # Automatically wrap argument in a PGArray if it is a plain Array.
209
+ # Requires that the pg_array extension has been loaded to work.
210
+ def wrap_array(arg)
211
+ if arg.instance_of?(Array)
212
+ Sequel.pg_array(arg)
213
+ else
214
+ arg
215
+ end
216
+ end
217
+ end
218
+
219
+ module ArrayOpMethods
220
+ # Wrap the receiver in an ArrayOp so you can easily use the PostgreSQL
221
+ # array functions and operators with it.
222
+ def pg_array
223
+ ArrayOp.new(self)
224
+ end
225
+ end
226
+
227
+ if defined?(PGArray)
228
+ class PGArray
229
+ # Wrap the PGArray instance in an ArrayOp, allowing you to easily use
230
+ # the PostgreSQL array functions and operators with literal arrays.
231
+ def op
232
+ ArrayOp.new(self)
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ module SQL::Builders
239
+ # Return the object wrapped in an Postgres::ArrayOp.
240
+ def pg_array_op(v)
241
+ case v
242
+ when Postgres::ArrayOp
243
+ v
244
+ else
245
+ Postgres::ArrayOp.new(v)
246
+ end
247
+ end
248
+ end
249
+
250
+ class SQL::GenericExpression
251
+ include Sequel::Postgres::ArrayOpMethods
252
+ end
253
+
254
+ class LiteralString
255
+ include Sequel::Postgres::ArrayOpMethods
256
+ end
257
+ end
258
+
259
+ if Sequel.core_extensions?
260
+ class Symbol
261
+ include Sequel::Postgres::ArrayOpMethods
262
+ end
263
+ end
@@ -0,0 +1,175 @@
1
+ # This extension allows Sequel's postgres adapter to automatically
2
+ # parameterize all common queries. Sequel's default behavior has always
3
+ # been to literalize all arguments unless specifically using
4
+ # parameters (via :$arg placeholders and the prepare/call methods).
5
+ # This extension makes Sequel take all string, numeric, date, and
6
+ # time types and automatically turn them into parameters. Example:
7
+ #
8
+ # # Default
9
+ # DB[:test].where(:a=>1)
10
+ # # SQL: SELECT * FROM test WHERE a = 1
11
+ #
12
+ # DB.extension :pg_auto_parameterize
13
+ # DB[:test].where(:a=>1)
14
+ # # SQL: SELECT * FROM test WHERE a = $1 (args: [1])
15
+ #
16
+ # This extension is not necessarily faster or more safe than the
17
+ # default behavior. In some cases it is faster, such as when using
18
+ # large strings. However, there are also some known issues with
19
+ # this approach:
20
+ #
21
+ # 1. Because of the way it operates, it has no context to make a
22
+ # determination about whether to literalize an object or not.
23
+ # For example, if it comes across an integer, it will turn it
24
+ # into a parameter. That breaks code such as:
25
+ #
26
+ # DB[:table].select(:a, :b).order(2, 1)
27
+ #
28
+ # Since it will use the following SQL (which isn't valid):
29
+ #
30
+ # SELECT a, b FROM table ORDER BY $1, $2
31
+ #
32
+ # To work around this, you can either specify the columns
33
+ # manually or use a literal string:
34
+ #
35
+ # DB[:table].select(:a, :b).order(:b, :a)
36
+ # DB[:table].select(:a, :b).order(Sequel.lit('2, 1'))
37
+ #
38
+ # 2. In order to avoid many type errors, it attempts to guess the
39
+ # appropriate type and automatically casts all placeholders.
40
+ # Unfortunately, if the type guess is incorrect, the query will
41
+ # be rejected. For example, the following works without
42
+ # automatic parameterization, but fails with it:
43
+ #
44
+ # DB[:table].insert(:interval=>'1 day')
45
+ #
46
+ # To work around this, you can just add the necessary casts
47
+ # manually:
48
+ #
49
+ # DB[:table].insert(:interval=>'1 day'.cast(:interval))
50
+ #
51
+ # You can also work around any issues that come up by disabling automatic
52
+ # parameterization by calling the no_auto_parameterize method on the
53
+ # dataset (which returns a clone of the dataset).
54
+ #
55
+ # It is likely there are other corner cases I am not yet aware of
56
+ # when using this extension, so use this extension with caution.
57
+ #
58
+ # This extension is only compatible when using the pg driver, not
59
+ # when using the old postgres driver or the postgres-pr driver.
60
+
61
+ module Sequel
62
+ module Postgres
63
+ # Enable automatically parameterizing queries by hijacking the
64
+ # SQL query string that Sequel builds to also hold the array
65
+ # of parameters.
66
+ module AutoParameterize
67
+ # String that holds an array of parameters
68
+ class StringWithArray < ::String
69
+ PLACEHOLDER = '$'.freeze
70
+ CAST = '::'.freeze
71
+
72
+ # The array of parameters used by this query.
73
+ attr_reader :args
74
+
75
+ # Add a new parameter to this query, which adds
76
+ # the parameter to the array of parameters, and an
77
+ # SQL placeholder to the query itself.
78
+ def add_arg(s, type=nil)
79
+ @args ||= []
80
+ @args << s
81
+ self << PLACEHOLDER << @args.length.to_s
82
+ self << CAST << type.to_s if type
83
+ end
84
+
85
+ # Show args when the string is inspected
86
+ def inspect
87
+ @args ? "#{self}; #{@args.inspect}".inspect : super
88
+ end
89
+ end
90
+
91
+ module DatabaseMethods
92
+ # Extend the database's datasets with the necessary code.
93
+ def self.extended(db)
94
+ db.extend_datasets(DatasetMethods)
95
+ end
96
+
97
+ # If the sql string has an embedded parameter array,
98
+ # extract the arguments from that.
99
+ def execute(sql, opts={})
100
+ if sql.is_a?(StringWithArray) && (args = sql.args)
101
+ opts = opts.merge(:arguments=>args)
102
+ end
103
+ super
104
+ end
105
+ end
106
+
107
+ module DatasetMethods
108
+ # Return a clone of the dataset that will not do
109
+ # automatic parameterization.
110
+ def no_auto_parameterize
111
+ clone(:no_auto_parameterize=>true)
112
+ end
113
+
114
+ # For strings, numeric arguments, and date/time arguments, add
115
+ # them as parameters to the query instead of literalizing them
116
+ # into the SQL.
117
+ def literal_append(sql, v)
118
+ if sql.is_a?(StringWithArray)
119
+ case v
120
+ when String
121
+ case v
122
+ when LiteralString
123
+ super
124
+ when Sequel::SQL::Blob
125
+ sql.add_arg(v, :bytea)
126
+ else
127
+ sql.add_arg(v)
128
+ end
129
+ when Bignum
130
+ sql.add_arg(v, :int8)
131
+ when Fixnum
132
+ sql.add_arg(v, :int4)
133
+ when Float
134
+ sql.add_arg(v, :"double precision")
135
+ when BigDecimal
136
+ sql.add_arg(v, :numeric)
137
+ when Sequel::SQLTime
138
+ sql.add_arg(v, :time)
139
+ when Time, DateTime
140
+ sql.add_arg(v, :timestamp)
141
+ when Date
142
+ sql.add_arg(v, :date)
143
+ else
144
+ super
145
+ end
146
+ else
147
+ super
148
+ end
149
+ end
150
+
151
+ def use_cursor(*)
152
+ super.no_auto_parameterize
153
+ end
154
+
155
+ protected
156
+
157
+ # Disable automatic parameterization for prepared statements,
158
+ # since they will use manual parameterization.
159
+ def to_prepared_statement(*a)
160
+ opts[:no_auto_parameterize] ? super : no_auto_parameterize.to_prepared_statement(*a)
161
+ end
162
+
163
+ private
164
+
165
+ # Unless auto parameterization is turned off, use a string that
166
+ # can store the parameterized arguments.
167
+ def sql_string_origin
168
+ opts[:no_auto_parameterize] ? super : StringWithArray.new
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ Database.register_extension(:pg_auto_parameterize, Postgres::AutoParameterize::DatabaseMethods)
175
+ end