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
@@ -1,6 +1,6 @@
1
- module Sequel
2
- Dataset::NON_SQL_OPTIONS << :disable_insert_returning
1
+ Sequel.require 'adapters/utils/pg_types'
3
2
 
3
+ module Sequel
4
4
  # Top level module for holding all PostgreSQL-related modules and classes
5
5
  # for Sequel. There are a few module level accessors that are added via
6
6
  # metaprogramming. These are:
@@ -33,10 +33,10 @@ module Sequel
33
33
  # Array of exceptions that need to be converted. JDBC
34
34
  # uses NativeExceptions, the native adapter uses PGError.
35
35
  CONVERTED_EXCEPTIONS = []
36
-
36
+
37
37
  @client_min_messages = :warning
38
38
  @force_standard_strings = true
39
-
39
+
40
40
  class << self
41
41
  # By default, Sequel sets the minimum level of log messages sent to the client
42
42
  # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
@@ -45,25 +45,70 @@ module Sequel
45
45
  attr_accessor :client_min_messages
46
46
 
47
47
  # By default, Sequel forces the use of standard strings, so that
48
- # '\\' is interpreted as \\ and not \. While PostgreSQL defaults
49
- # to interpreting plain strings as extended strings, this will change
50
- # in a future version of PostgreSQL. Sequel assumes that SQL standard
51
- # strings will be used.
48
+ # '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
49
+ # to interpreting plain strings, newer versions use standard strings by
50
+ # default. Sequel assumes that SQL standard strings will be used. Setting
51
+ # this to false means Sequel will use the database's default.
52
52
  attr_accessor :force_standard_strings
53
53
  end
54
54
 
55
- # Methods shared by adapter/connection instances.
56
- module AdapterMethods
57
- attr_writer :db
58
-
59
- SELECT_CURRVAL = "SELECT currval('%s')".freeze
60
- SELECT_CUSTOM_SEQUENCE = proc do |schema, table| <<-end_sql
61
- SELECT '"' || name.nspname || '".' || CASE
62
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
63
- substr(split_part(def.adsrc, '''', 2),
64
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
65
- ELSE split_part(def.adsrc, '''', 2)
66
- END
55
+ class CreateTableGenerator < Sequel::Schema::Generator
56
+ # Add an exclusion constraint when creating the table. elements should be
57
+ # an array of 2 element arrays, with the first element being the column or
58
+ # expression the exclusion constraint is applied to, and the second element
59
+ # being the operator to use for the column/expression to check for exclusion.
60
+ #
61
+ # Example:
62
+ #
63
+ # exclusion_constraint([[:col1, '&&'], [:col2, '=']])
64
+ # # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
65
+ #
66
+ # Options supported:
67
+ #
68
+ # :name :: Name the constraint with the given name (useful if you may
69
+ # need to drop the constraint later)
70
+ # :using :: Override the index_method for the exclusion constraint (defaults to gist).
71
+ # :where :: Create a partial exclusion constraint, which only affects
72
+ # a subset of table rows, value should be a filter expression.
73
+ def exclude(elements, opts={})
74
+ constraints << {:type => :exclude, :elements => elements}.merge(opts)
75
+ end
76
+ end
77
+
78
+ class AlterTableGenerator < Sequel::Schema::AlterTableGenerator
79
+ # Adds an exclusion constraint to an existing table, see
80
+ # CreateTableGenerator#exclude.
81
+ def add_exclusion_constraint(elements, opts={})
82
+ @operations << {:op => :add_constraint, :type => :exclude, :elements => elements}.merge(opts)
83
+ end
84
+
85
+ # Validate the constraint with the given name, which should have
86
+ # been added previously with NOT VALID.
87
+ def validate_constraint(name)
88
+ @operations << {:op => :validate_constraint, :name => name}
89
+ end
90
+ end
91
+
92
+ # Methods shared by Database instances that connect to PostgreSQL.
93
+ module DatabaseMethods
94
+ EXCLUDE_SCHEMAS = /pg_*|information_schema/i
95
+ PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
96
+ RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
97
+ SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
98
+ FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'.freeze=>:no_action, 'r'.freeze=>:restrict, 'c'.freeze=>:cascade, 'n'.freeze=>:set_null, 'd'.freeze=>:set_default}.freeze
99
+ POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
100
+
101
+ # SQL fragment for custom sequences (ones not created by serial primary key),
102
+ # Returning the schema and literal form of the sequence name, by parsing
103
+ # the column defaults table.
104
+ SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
105
+ SELECT name.nspname AS "schema",
106
+ CASE
107
+ WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
108
+ substr(split_part(def.adsrc, '''', 2),
109
+ strpos(split_part(def.adsrc, '''', 2), '.')+1)
110
+ ELSE split_part(def.adsrc, '''', 2)
111
+ END AS "sequence"
67
112
  FROM pg_class t
68
113
  JOIN pg_namespace name ON (t.relnamespace = name.oid)
69
114
  JOIN pg_attribute attr ON (t.oid = attrelid)
@@ -71,26 +116,28 @@ module Sequel
71
116
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
72
117
  WHERE cons.contype = 'p'
73
118
  AND def.adsrc ~* 'nextval'
74
- #{"AND name.nspname = '#{schema}'" if schema}
75
- AND t.relname = '#{table}'
76
119
  end_sql
77
- end
78
- SELECT_PK = proc do |schema, table| <<-end_sql
79
- SELECT pg_attribute.attname
120
+ ).strip.gsub(/\s+/, ' ').freeze
121
+
122
+ # SQL fragment for determining primary key column for the given table. Only
123
+ # returns the first primary key if the table has a composite primary key.
124
+ SELECT_PK_SQL = (<<-end_sql
125
+ SELECT pg_attribute.attname AS pk
80
126
  FROM pg_class, pg_attribute, pg_index, pg_namespace
81
127
  WHERE pg_class.oid = pg_attribute.attrelid
82
128
  AND pg_class.relnamespace = pg_namespace.oid
83
129
  AND pg_class.oid = pg_index.indrelid
84
130
  AND pg_index.indkey[0] = pg_attribute.attnum
85
131
  AND pg_index.indisprimary = 't'
86
- #{"AND pg_namespace.nspname = '#{schema}'" if schema}
87
- AND pg_class.relname = '#{table}'
88
132
  end_sql
89
- end
90
- SELECT_SERIAL_SEQUENCE = proc do |schema, table| <<-end_sql
91
- SELECT '"' || name.nspname || '".' || seq.relname || ''
133
+ ).strip.gsub(/\s+/, ' ').freeze
134
+
135
+ # SQL fragment for getting sequence associated with table's
136
+ # primary key, assuming it was a serial primary key column.
137
+ SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
138
+ SELECT name.nspname AS "schema", seq.relname AS "sequence"
92
139
  FROM pg_class seq, pg_attribute attr, pg_depend dep,
93
- pg_namespace name, pg_constraint cons
140
+ pg_namespace name, pg_constraint cons, pg_class t
94
141
  WHERE seq.oid = dep.objid
95
142
  AND seq.relnamespace = name.oid
96
143
  AND seq.relkind = 'S'
@@ -98,69 +145,17 @@ module Sequel
98
145
  AND attr.attnum = dep.refobjsubid
99
146
  AND attr.attrelid = cons.conrelid
100
147
  AND attr.attnum = cons.conkey[1]
148
+ AND attr.attrelid = t.oid
101
149
  AND cons.contype = 'p'
102
- #{"AND name.nspname = '#{schema}'" if schema}
103
- AND seq.relname = '#{table}'
104
150
  end_sql
105
- end
106
-
107
- # Depth of the current transaction on this connection, used
108
- # to implement multi-level transactions with savepoints.
109
- attr_accessor :transaction_depth
110
-
111
- # Apply connection settings for this connection. Currently, turns
112
- # standard_conforming_strings ON if Postgres.force_standard_strings
113
- # is true.
114
- def apply_connection_settings
115
- if Postgres.force_standard_strings
116
- # This setting will only work on PostgreSQL 8.2 or greater
117
- # and we don't know the server version at this point, so
118
- # try it unconditionally and rescue any errors.
119
- execute("SET standard_conforming_strings = ON") rescue nil
120
- end
121
- if cmm = Postgres.client_min_messages
122
- execute("SET client_min_messages = '#{cmm.to_s.upcase}'")
123
- end
124
- end
151
+ ).strip.gsub(/\s+/, ' ').freeze
125
152
 
126
- # Get the last inserted value for the given sequence.
127
- def last_insert_id(sequence)
128
- sql = SELECT_CURRVAL % sequence
129
- execute(sql) do |r|
130
- val = single_value(r)
131
- return val.to_i if val
132
- end
133
- end
134
-
135
- # Get the primary key for the given table.
136
- def primary_key(schema, table)
137
- sql = SELECT_PK[schema, table]
138
- execute(sql) do |r|
139
- return single_value(r)
140
- end
141
- end
142
-
143
- # Get the primary key and sequence for the given table.
144
- def sequence(schema, table)
145
- sql = SELECT_SERIAL_SEQUENCE[schema, table]
146
- execute(sql) do |r|
147
- seq = single_value(r)
148
- return seq if seq
149
- end
150
-
151
- sql = SELECT_CUSTOM_SEQUENCE[schema, table]
152
- execute(sql) do |r|
153
- return single_value(r)
154
- end
155
- end
156
- end
157
-
158
- # Methods shared by Database instances that connect to PostgreSQL.
159
- module DatabaseMethods
160
- EXCLUDE_SCHEMAS = /pg_*|information_schema/i
161
- PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
162
- RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
163
- SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
153
+ # The Sequel extensions that require reseting of the conversion procs.
154
+ RESET_PROCS_EXTENSIONS = [:pg_array, :pg_hstore, :pg_inet, :pg_interval, :pg_json, :pg_range].freeze
155
+
156
+ # A hash of conversion procs, keyed by type integer (oid) and
157
+ # having callable values for the conversion proc for that type.
158
+ attr_reader :conversion_procs
164
159
 
165
160
  # Commit an existing prepared transaction with the given transaction
166
161
  # identifier string.
@@ -191,7 +186,7 @@ module Sequel
191
186
  def create_function(name, definition, opts={})
192
187
  self << create_function_sql(name, definition, opts)
193
188
  end
194
-
189
+
195
190
  # Create the procedural language in the database. Arguments:
196
191
  # * name : Name of the procedural language (e.g. plpgsql)
197
192
  # * opts : options hash:
@@ -202,7 +197,13 @@ module Sequel
202
197
  def create_language(name, opts={})
203
198
  self << create_language_sql(name, opts)
204
199
  end
205
-
200
+
201
+ # Create a schema in the database. Arguments:
202
+ # * name : Name of the schema (e.g. admin)
203
+ def create_schema(name)
204
+ self << create_schema_sql(name)
205
+ end
206
+
206
207
  # Create a trigger in the database. Arguments:
207
208
  # * table : the table on which this trigger operates
208
209
  # * name : the name of this trigger
@@ -216,7 +217,7 @@ module Sequel
216
217
  def create_trigger(table, name, function, opts={})
217
218
  self << create_trigger_sql(table, name, function, opts)
218
219
  end
219
-
220
+
220
221
  # PostgreSQL uses the :postgres database type.
221
222
  def database_type
222
223
  :postgres
@@ -231,7 +232,7 @@ module Sequel
231
232
  def drop_function(name, opts={})
232
233
  self << drop_function_sql(name, opts)
233
234
  end
234
-
235
+
235
236
  # Drops a procedural language from the database. Arguments:
236
237
  # * name : name of the procedural language to drop
237
238
  # * opts : options hash:
@@ -240,15 +241,14 @@ module Sequel
240
241
  def drop_language(name, opts={})
241
242
  self << drop_language_sql(name, opts)
242
243
  end
243
-
244
- # Remove the cached entries for primary keys and sequences when dropping a table.
245
- def drop_table(*names)
246
- names.each do |name|
247
- name = quote_schema_table(name)
248
- @primary_keys.delete(name)
249
- @primary_key_sequences.delete(name)
250
- end
251
- super
244
+
245
+ # Drops a schema from the database. Arguments:
246
+ # * name : name of the schema to drop
247
+ # * opts : options hash:
248
+ # * :cascade : Drop all objects in this schema.
249
+ # * :if_exists : Don't raise an error if the schema doesn't exist.
250
+ def drop_schema(name, opts={})
251
+ self << drop_schema_sql(name, opts)
252
252
  end
253
253
 
254
254
  # Drops a trigger from the database. Arguments:
@@ -260,7 +260,76 @@ module Sequel
260
260
  def drop_trigger(table, name, opts={})
261
261
  self << drop_trigger_sql(table, name, opts)
262
262
  end
263
-
263
+
264
+ # If any of the extensions that require reseting the conversion procs
265
+ # is loaded, reset them. This is done here so that if you load
266
+ # multiple pg_* extensions in the same call, the conversion procs are
267
+ # only reset once instead of once for every extension.
268
+ def extension(*exts)
269
+ super
270
+ unless (RESET_PROCS_EXTENSIONS & exts).empty?
271
+ reset_conversion_procs
272
+ end
273
+ end
274
+
275
+ # Return full foreign key information using the pg system tables, including
276
+ # :name, :on_delete, :on_update, and :deferrable entries in the hashes.
277
+ def foreign_key_list(table, opts={})
278
+ m = output_identifier_meth
279
+ im = input_identifier_meth
280
+ schema, table = schema_and_table(table)
281
+ range = 0...32
282
+
283
+ base_ds = metadata_dataset.
284
+ where(:cl__relkind=>'r', :co__contype=>'f', :cl__relname=>im.call(table)).
285
+ from(:pg_constraint___co).
286
+ join(:pg_class___cl, :oid=>:conrelid)
287
+
288
+ # We split the parsing into two separate queries, which are merged manually later.
289
+ # This is because PostgreSQL stores both the referencing and referenced columns in
290
+ # arrays, and I don't know a simple way to not create a cross product, as PostgreSQL
291
+ # doesn't appear to have a function that takes an array and element and gives you
292
+ # the index of that element in the array.
293
+
294
+ ds = base_ds.
295
+ join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
296
+ order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
297
+ select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
298
+ SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
299
+
300
+ ref_ds = base_ds.
301
+ join(:pg_class___cl2, :oid=>:co__confrelid).
302
+ join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
303
+ order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att2__attnum)).
304
+ select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
305
+
306
+ # If a schema is given, we only search in that schema, and the returned :table
307
+ # entry is schema qualified as well.
308
+ if schema
309
+ ds = ds.join(:pg_namespace___nsp, :oid=>:cl__relnamespace).
310
+ where(:nsp__nspname=>im.call(schema))
311
+ ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
312
+ select_more(:nsp2__nspname___schema)
313
+ end
314
+
315
+ h = {}
316
+ fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
317
+ ds.each do |row|
318
+ if r = h[row[:name]]
319
+ r[:columns] << m.call(row[:column])
320
+ else
321
+ h[row[:name]] = {:name=>m.call(row[:name]), :columns=>[m.call(row[:column])], :on_update=>fklod_map[row[:on_update]], :on_delete=>fklod_map[row[:on_delete]], :deferrable=>row[:deferrable]}
322
+ end
323
+ end
324
+ ref_ds.each do |row|
325
+ r = h[row[:name]]
326
+ r[:table] ||= schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
327
+ r[:key] ||= []
328
+ r[:key] << m.call(row[:refcolumn])
329
+ end
330
+ h.values
331
+ end
332
+
264
333
  # Use the pg_* system tables to determine indexes on a table
265
334
  def indexes(table, opts={})
266
335
  m = output_identifier_meth
@@ -273,14 +342,13 @@ module Sequel
273
342
  join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
274
343
  join(:pg_class___indc, :oid=>:indexrelid).
275
344
  join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
276
- filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
277
- order(:indc__relname, range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
345
+ filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true).
346
+ order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
278
347
  select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
279
-
348
+
280
349
  ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
281
- ds.filter!(:indisvalid=>true) if server_version >= 80200
282
350
  ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
283
-
351
+
284
352
  indexes = {}
285
353
  ds.each do |r|
286
354
  i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique]}
@@ -289,40 +357,66 @@ module Sequel
289
357
  indexes
290
358
  end
291
359
 
292
- # Dataset containing all current database locks
360
+ # Dataset containing all current database locks
293
361
  def locks
294
362
  dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
295
363
  end
296
-
364
+
365
+ # Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
366
+ #
367
+ # :payload :: The payload string to use for the NOTIFY statement. Only supported
368
+ # in PostgreSQL 9.0+.
369
+ # :server :: The server to which to send the NOTIFY statement, if the sharding support
370
+ # is being used.
371
+ def notify(channel, opts={})
372
+ execute_ddl("NOTIFY #{channel}#{", #{literal(opts[:payload].to_s)}" if opts[:payload]}", opts)
373
+ end
374
+
297
375
  # Return primary key for the given table.
298
376
  def primary_key(table, opts={})
299
377
  quoted_table = quote_schema_table(table)
300
- return @primary_keys[quoted_table] if @primary_keys.include?(quoted_table)
301
- @primary_keys[quoted_table] = if conn = opts[:conn]
302
- conn.primary_key(*schema_and_table(table))
303
- else
304
- synchronize(opts[:server]){|con| con.primary_key(*schema_and_table(table))}
305
- end
378
+ Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
379
+ schema, table = schema_and_table(table)
380
+ sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
381
+ sql << " AND pg_namespace.nspname = #{literal(schema)}" if schema
382
+ value = fetch(sql).single_value
383
+ Sequel.synchronize{@primary_keys[quoted_table] = value}
306
384
  end
307
-
385
+
308
386
  # Return the sequence providing the default for the primary key for the given table.
309
387
  def primary_key_sequence(table, opts={})
310
388
  quoted_table = quote_schema_table(table)
311
- return @primary_key_sequences[quoted_table] if @primary_key_sequences.include?(quoted_table)
312
- @primary_key_sequences[quoted_table] = if conn = opts[:conn]
313
- conn.sequence(*schema_and_table(table))
389
+ Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
390
+ schema, table = schema_and_table(table)
391
+ table = literal(table)
392
+ sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND t.relname = #{table}"
393
+ sql << " AND name.nspname = #{literal(schema)}" if schema
394
+ if pks = fetch(sql).single_record
395
+ value = literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
396
+ Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
314
397
  else
315
- synchronize(opts[:server]){|con| con.sequence(*schema_and_table(table))}
398
+ sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
399
+ sql << " AND name.nspname = #{literal(schema)}" if schema
400
+ if pks = fetch(sql).single_record
401
+ value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
402
+ Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
403
+ end
316
404
  end
317
405
  end
318
406
 
319
- # Reset the primary key sequence for the given table, baseing it on the
407
+ # Reset the database's conversion procs, requires a server query if there
408
+ # any named types.
409
+ def reset_conversion_procs
410
+ @conversion_procs = get_conversion_procs
411
+ end
412
+
413
+ # Reset the primary key sequence for the given table, basing it on the
320
414
  # maximum current value of the table's primary key.
321
415
  def reset_primary_key_sequence(table)
322
- pk = SQL::Identifier.new(primary_key(table))
323
416
  return unless seq = primary_key_sequence(table)
417
+ pk = SQL::Identifier.new(primary_key(table))
324
418
  db = self
325
- seq_ds = db.from(seq.lit)
419
+ seq_ds = db.from(LiteralString.new(seq))
326
420
  get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
327
421
  end
328
422
 
@@ -337,7 +431,7 @@ module Sequel
337
431
  def serial_primary_key_options
338
432
  {:primary_key => true, :serial => true, :type=>Integer}
339
433
  end
340
-
434
+
341
435
  # The version of the PostgreSQL server, used for determining capability.
342
436
  def server_version(server=nil)
343
437
  return @server_version if @server_version
@@ -345,12 +439,26 @@ module Sequel
345
439
  (conn.server_version rescue nil) if conn.respond_to?(:server_version)
346
440
  end
347
441
  unless @server_version
348
- m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
349
- @server_version = (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
442
+ @server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
443
+ (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
444
+ else
445
+ 0
446
+ end
350
447
  end
448
+ warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
351
449
  @server_version
352
450
  end
353
-
451
+
452
+ # PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
453
+ def supports_create_table_if_not_exists?
454
+ server_version >= 90100
455
+ end
456
+
457
+ # PostgreSQL supports DROP TABLE IF EXISTS
458
+ def supports_drop_table_if_exists?
459
+ true
460
+ end
461
+
354
462
  # PostgreSQL supports prepared transactions (two-phase commit) if
355
463
  # max_prepared_transactions is greater than 0.
356
464
  def supports_prepared_transactions?
@@ -368,39 +476,128 @@ module Sequel
368
476
  true
369
477
  end
370
478
 
371
- # Whether the given table exists in the database
479
+ # PostgreSQL supports transaction DDL statements.
480
+ def supports_transactional_ddl?
481
+ true
482
+ end
483
+
484
+ # Array of symbols specifying table names in the current database.
485
+ # The dataset used is yielded to the block if one is provided,
486
+ # otherwise, an array of symbols of table names is returned.
372
487
  #
373
488
  # Options:
374
489
  # * :schema - The schema to search (default_schema by default)
375
490
  # * :server - The server to use
376
- def table_exists?(table, opts={})
377
- im = input_identifier_meth
378
- schema, table = schema_and_table(table)
379
- opts[:schema] ||= schema
380
- tables(opts){|ds| !ds.first(:relname=>im.call(table)).nil?}
491
+ def tables(opts={}, &block)
492
+ pg_class_relname('r', opts, &block)
381
493
  end
382
-
383
- # Array of symbols specifying table names in the current database.
384
- # The dataset used is yielded to the block if one is provided,
385
- # otherwise, an array of symbols of table names is returned.
494
+
495
+ # Check whether the given type name string/symbol (e.g. :hstore) is supported by
496
+ # the database.
497
+ def type_supported?(type)
498
+ @supported_types ||= {}
499
+ @supported_types.fetch(type){@supported_types[type] = (from(:pg_type).filter(:typtype=>'b', :typname=>type.to_s).count > 0)}
500
+ end
501
+
502
+ # Array of symbols specifying view names in the current database.
386
503
  #
387
504
  # Options:
388
505
  # * :schema - The schema to search (default_schema by default)
389
506
  # * :server - The server to use
390
- def tables(opts={})
391
- ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
392
- ds = filter_schema(ds, opts)
393
- m = output_identifier_meth
394
- block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
507
+ def views(opts={})
508
+ pg_class_relname('v', opts)
395
509
  end
396
510
 
397
511
  private
398
512
 
513
+ # Use a PostgreSQL-specific alter table generator
514
+ def alter_table_generator_class
515
+ Postgres::AlterTableGenerator
516
+ end
517
+
518
+ # Handle :using option for set_column_type op, and the :validate_constraint op.
519
+ def alter_table_op_sql(table, op)
520
+ case op[:op]
521
+ when :set_column_type
522
+ s = super
523
+ if using = op[:using]
524
+ using = Sequel::LiteralString.new(using) if using.is_a?(String)
525
+ s << ' USING '
526
+ s << literal(using)
527
+ end
528
+ s
529
+ when :validate_constraint
530
+ "VALIDATE CONSTRAINT #{quote_identifier(op[:name])}"
531
+ else
532
+ super
533
+ end
534
+ end
535
+
536
+ # If the :synchronous option is given and non-nil, set synchronous_commit
537
+ # appropriately. Valid values for the :synchronous option are true,
538
+ # :on, false, :off, :local, and :remote_write.
539
+ def begin_new_transaction(conn, opts)
540
+ super
541
+ if opts.has_key?(:synchronous)
542
+ case sync = opts[:synchronous]
543
+ when true
544
+ sync = :on
545
+ when false
546
+ sync = :off
547
+ when nil
548
+ return
549
+ end
550
+
551
+ log_connection_execute(conn, "SET LOCAL synchronous_commit = #{sync}")
552
+ end
553
+ end
554
+
555
+ # Handle PostgreSQL specific default format.
556
+ def column_schema_normalize_default(default, type)
557
+ if m = POSTGRES_DEFAULT_RE.match(default)
558
+ default = m[1] || m[2]
559
+ end
560
+ super(default, type)
561
+ end
562
+
399
563
  # If the :prepare option is given and we aren't in a savepoint,
400
564
  # prepare the transaction for a two-phase commit.
401
565
  def commit_transaction(conn, opts={})
402
- if opts[:prepare] && Thread.current[:sequel_transaction_depth] <= 1
403
- log_connection_execute(conn, "PREPARE TRANSACTION #{literal(opts[:prepare])}")
566
+ if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
567
+ log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
568
+ else
569
+ super
570
+ end
571
+ end
572
+
573
+ # PostgreSQL can't combine rename_column operations, and it can combine
574
+ # the custom validate_constraint operation.
575
+ def combinable_alter_table_op?(op)
576
+ (super || op[:op] == :validate_constraint) && op[:op] != :rename_column
577
+ end
578
+
579
+ # The SQL queries to execute when starting a new connection.
580
+ def connection_configuration_sqls
581
+ sqls = []
582
+ sqls << "SET standard_conforming_strings = ON" if Postgres.force_standard_strings
583
+ if cmm = Postgres.client_min_messages
584
+ sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
585
+ end
586
+ sqls
587
+ end
588
+
589
+ # Handle exclusion constraints.
590
+ def constraint_definition_sql(constraint)
591
+ case constraint[:type]
592
+ when :exclude
593
+ elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
594
+ "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
595
+ when :foreign_key
596
+ sql = super
597
+ if constraint[:not_valid]
598
+ sql << " NOT VALID"
599
+ end
600
+ sql
404
601
  else
405
602
  super
406
603
  end
@@ -426,39 +623,54 @@ module Sequel
426
623
  AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
427
624
  END
428
625
  end
429
-
626
+
430
627
  # SQL for creating a procedural language.
431
628
  def create_language_sql(name, opts={})
432
629
  "CREATE#{' OR REPLACE' if opts[:replace] && server_version >= 90000}#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
433
630
  end
434
-
435
- # SQL for creating a database trigger.
631
+
632
+ # SQL for creating a schema.
633
+ def create_schema_sql(name)
634
+ "CREATE SCHEMA #{quote_identifier(name)}"
635
+ end
636
+
637
+ # Use a PostgreSQL-specific create table generator
638
+ def create_table_generator_class
639
+ Postgres::CreateTableGenerator
640
+ end
641
+
642
+ # SQL for creating a database trigger.
436
643
  def create_trigger_sql(table, name, function, opts={})
437
644
  events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
438
645
  whence = opts[:after] ? 'AFTER' : 'BEFORE'
439
646
  "CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
440
647
  end
441
-
648
+
442
649
  # The errors that the main adapters can raise, depends on the adapter being used
443
650
  def database_error_classes
444
651
  CONVERTED_EXCEPTIONS
445
652
  end
446
-
447
- # SQL for dropping a function from the database.
653
+
654
+ # SQL for dropping a function from the database.
448
655
  def drop_function_sql(name, opts={})
449
656
  "DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
450
657
  end
451
658
 
659
+ # Support :if_exists, :cascade, and :concurrently options.
660
+ def drop_index_sql(table, op)
661
+ "DROP INDEX#{' CONCURRENTLY' if op[:concurrently]}#{' IF EXISTS' if op[:if_exists]} #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))}#{' CASCADE' if op[:cascade]}"
662
+ end
663
+
452
664
  # SQL for dropping a procedural language from the database.
453
665
  def drop_language_sql(name, opts={})
454
666
  "DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
455
667
  end
456
668
 
457
- # Always CASCADE the table drop
458
- def drop_table_sql(name)
459
- "DROP TABLE #{quote_schema_table(name)} CASCADE"
669
+ # SQL for dropping a schema from the database.
670
+ def drop_schema_sql(name, opts={})
671
+ "DROP SCHEMA#{' IF EXISTS' if opts[:if_exists]} #{quote_identifier(name)}#{' CASCADE' if opts[:cascade]}"
460
672
  end
461
-
673
+
462
674
  # SQL for dropping a trigger from the database.
463
675
  def drop_trigger_sql(table, name, opts={})
464
676
  "DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
@@ -473,12 +685,24 @@ module Sequel
473
685
  ds.exclude(:pg_namespace__nspname=>EXCLUDE_SCHEMAS)
474
686
  end
475
687
  end
476
-
688
+
689
+ # Return a hash with oid keys and callable values, used for converting types.
690
+ def get_conversion_procs
691
+ procs = PG_TYPES.dup
692
+ procs[1184] = procs[1114] = method(:to_application_timestamp)
693
+ unless (pgnt = PG_NAMED_TYPES).empty?
694
+ from(:pg_type).where(:typtype=>'b', :typname=>pgnt.keys.map{|t| t.to_s}).select_map([:oid, :typname]).each do |oid, name|
695
+ procs[oid.to_i] ||= pgnt[name.untaint.to_sym]
696
+ end
697
+ end
698
+ procs
699
+ end
700
+
477
701
  # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
478
702
  def identifier_input_method_default
479
703
  nil
480
704
  end
481
-
705
+
482
706
  # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
483
707
  def identifier_output_method_default
484
708
  nil
@@ -488,7 +712,7 @@ module Sequel
488
712
  def index_definition_sql(table_name, index)
489
713
  cols = index[:columns]
490
714
  index_name = index[:name] || default_index_name(table_name, cols)
491
- expr = if o = index[:opclass]
715
+ expr = if o = index[:opclass]
492
716
  "(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
493
717
  else
494
718
  literal(Array(cols))
@@ -499,74 +723,79 @@ module Sequel
499
723
  filter = " WHERE #{filter_expr(filter)}" if filter
500
724
  case index_type
501
725
  when :full_text
502
- expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{dataset.send(:full_text_string_join, cols)}))"
726
+ expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
503
727
  index_type = :gin
504
728
  when :spatial
505
729
  index_type = :gist
506
730
  end
507
- "CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
731
+ "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
508
732
  end
509
-
510
- # The result of the insert for the given table and values. If values
511
- # is an array, assume the first column is the primary key and return
512
- # that. If values is a hash, lookup the primary key for the table. If
513
- # the primary key is present in the hash, return its value. Otherwise,
514
- # look up the sequence for the table's primary key. If one exists,
515
- # return the last value the of the sequence for the connection.
516
- def insert_result(conn, table, values)
517
- case values
518
- when Hash
519
- return nil unless pk = primary_key(table, :conn=>conn)
520
- if pk and pkv = values[pk.to_sym]
521
- pkv
522
- else
523
- begin
524
- if seq = primary_key_sequence(table, :conn=>conn)
525
- conn.last_insert_id(seq)
526
- end
527
- rescue Exception => e
528
- raise_error(e, :classes=>CONVERTED_EXCEPTIONS) unless RE_CURRVAL_ERROR.match(e.message)
529
- end
530
- end
531
- when Array
532
- values.first
533
- else
534
- nil
535
- end
733
+
734
+ # Setup datastructures shared by all postgres adapters.
735
+ def initialize_postgres_adapter
736
+ @primary_keys = {}
737
+ @primary_key_sequences = {}
738
+ reset_conversion_procs
536
739
  end
537
740
 
538
- # Don't log, since logging is done by the underlying connection.
539
- def log_connection_execute(conn, sql)
540
- conn.execute(sql)
741
+ # Backbone of the tables and views support.
742
+ def pg_class_relname(type, opts)
743
+ ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
744
+ ds = filter_schema(ds, opts)
745
+ m = output_identifier_meth
746
+ block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
541
747
  end
542
-
748
+
543
749
  # Use a dollar sign instead of question mark for the argument
544
750
  # placeholder.
545
751
  def prepared_arg_placeholder
546
752
  PREPARED_ARG_PLACEHOLDER
547
753
  end
548
-
754
+
755
+ # Remove the cached entries for primary keys and sequences when a table is
756
+ # changed.
757
+ def remove_cached_schema(table)
758
+ tab = quote_schema_table(table)
759
+ Sequel.synchronize do
760
+ @primary_keys.delete(tab)
761
+ @primary_key_sequences.delete(tab)
762
+ end
763
+ super
764
+ end
765
+
549
766
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
550
767
  # a rename table operation, so speciying a new schema in new_name will not have an effect.
551
768
  def rename_table_sql(name, new_name)
552
769
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
553
- end
770
+ end
554
771
 
555
772
  # PostgreSQL's autoincrementing primary keys are of type integer or bigint
556
773
  # using a nextval function call as a default.
557
774
  def schema_autoincrementing_primary_key?(schema)
558
- super and schema[:db_type] =~ /\A(?:integer|bigint)\z/io and schema[:default]=~/\Anextval/io
775
+ super && schema[:default] =~ /\Anextval/io
776
+ end
777
+
778
+ # Recognize PostgreSQL interval type.
779
+ def schema_column_type(db_type)
780
+ case db_type
781
+ when /\Ainterval\z/io
782
+ :interval
783
+ else
784
+ super
785
+ end
559
786
  end
560
787
 
561
788
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
562
789
  def schema_parse_table(table_name, opts)
563
- m = output_identifier_meth
564
- m2 = input_identifier_meth
790
+ m = output_identifier_meth(opts[:dataset])
791
+ m2 = input_identifier_meth(opts[:dataset])
565
792
  ds = metadata_dataset.select(:pg_attribute__attname___name,
793
+ SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
566
794
  SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
567
795
  SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
568
796
  SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
569
- SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)).
797
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key),
798
+ :pg_namespace__nspname).
570
799
  from(:pg_class).
571
800
  join(:pg_attribute, :attrelid=>:oid).
572
801
  join(:pg_type, :oid=>:atttypid).
@@ -578,18 +807,46 @@ module Sequel
578
807
  filter(:pg_class__relname=>m2.call(table_name)).
579
808
  order(:pg_attribute__attnum)
580
809
  ds = filter_schema(ds, opts)
810
+ current_schema = nil
581
811
  ds.map do |row|
812
+ sch = row.delete(:nspname)
813
+ if current_schema
814
+ if sch != current_schema
815
+ raise Error, "columns from tables in two separate schema were returned (please specify a schema): #{current_schema.inspect}, #{sch.inspect}"
816
+ end
817
+ else
818
+ current_schema = sch
819
+ end
582
820
  row[:default] = nil if blank_object?(row[:default])
583
821
  row[:type] = schema_column_type(row[:db_type])
584
822
  [m.call(row.delete(:name)), row]
585
823
  end
586
824
  end
587
825
 
826
+ # Set the transaction isolation level on the given connection
827
+ def set_transaction_isolation(conn, opts)
828
+ level = opts.fetch(:isolation, transaction_isolation_level)
829
+ read_only = opts[:read_only]
830
+ deferrable = opts[:deferrable]
831
+ if level || !read_only.nil? || !deferrable.nil?
832
+ sql = "SET TRANSACTION"
833
+ sql << " ISOLATION LEVEL #{Sequel::Database::TRANSACTION_ISOLATION_LEVELS[level]}" if level
834
+ sql << " READ #{read_only ? 'ONLY' : 'WRITE'}" unless read_only.nil?
835
+ sql << " #{'NOT ' unless deferrable}DEFERRABLE" unless deferrable.nil?
836
+ log_connection_execute(conn, sql)
837
+ end
838
+ end
839
+
588
840
  # Turns an array of argument specifiers into an SQL fragment used for function arguments. See create_function_sql.
589
841
  def sql_function_args(args)
590
842
  "(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
591
843
  end
592
-
844
+
845
+ # PostgreSQL can combine multiple alter table ops into a single query.
846
+ def supports_combining_alter_table_ops?
847
+ true
848
+ end
849
+
593
850
  # Handle bigserial type if :serial option is present
594
851
  def type_literal_generic_bignum(column)
595
852
  column[:serial] ? :bigserial : super
@@ -619,7 +876,7 @@ module Sequel
619
876
  end
620
877
  end
621
878
  end
622
-
879
+
623
880
  # Instance methods for datasets that connect to a PostgreSQL database.
624
881
  module DatasetMethods
625
882
  ACCESS_SHARE = 'ACCESS SHARE'.freeze
@@ -627,107 +884,123 @@ module Sequel
627
884
  BOOL_FALSE = 'false'.freeze
628
885
  BOOL_TRUE = 'true'.freeze
629
886
  COMMA_SEPARATOR = ', '.freeze
630
- DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
887
+ DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where returning')
888
+ DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
631
889
  EXCLUSIVE = 'EXCLUSIVE'.freeze
632
890
  EXPLAIN = 'EXPLAIN '.freeze
633
891
  EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
634
892
  FOR_SHARE = ' FOR SHARE'.freeze
893
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
894
+ INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
635
895
  LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
636
896
  NULL = LiteralString.new('NULL').freeze
637
897
  PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
638
898
  QUERY_PLAN = 'QUERY PLAN'.to_sym
639
899
  ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
640
900
  ROW_SHARE = 'ROW SHARE'.freeze
641
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
642
- SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with distinct columns from join where group having window compounds order limit lock')
901
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
902
+ SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having window compounds order limit lock')
643
903
  SHARE = 'SHARE'.freeze
644
904
  SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
645
905
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
646
906
  SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
647
- UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
648
-
907
+ UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where returning')
908
+ UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
909
+ SPACE = Dataset::SPACE
910
+ FROM = Dataset::FROM
911
+ APOS = Dataset::APOS
912
+ APOS_RE = Dataset::APOS_RE
913
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
914
+ PAREN_OPEN = Dataset::PAREN_OPEN
915
+ PAREN_CLOSE = Dataset::PAREN_CLOSE
916
+ COMMA = Dataset::COMMA
917
+ AS = Dataset::AS
918
+ XOR_OP = ' # '.freeze
919
+ CRLF = "\r\n".freeze
920
+ BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
921
+ WINDOW = " WINDOW ".freeze
922
+ EMPTY_STRING = ''.freeze
923
+
649
924
  # Shared methods for prepared statements when used with PostgreSQL databases.
650
925
  module PreparedStatementMethods
651
926
  # Override insert action to use RETURNING if the server supports it.
927
+ def run
928
+ if @prepared_type == :insert
929
+ fetch_rows(prepared_sql){|r| return r.values.first}
930
+ else
931
+ super
932
+ end
933
+ end
934
+
652
935
  def prepared_sql
653
936
  return @prepared_sql if @prepared_sql
937
+ @opts[:returning] = insert_pk if @prepared_type == :insert
654
938
  super
655
- if @prepared_type == :insert and !@opts[:disable_insert_returning] and server_version >= 80200
656
- @prepared_sql = insert_returning_pk_sql(*@prepared_modify_values)
657
- meta_def(:insert_returning_pk_sql){|*args| prepared_sql}
658
- end
659
939
  @prepared_sql
660
940
  end
661
941
  end
662
942
 
663
- # Add the disable_insert_returning! mutation method
664
- def self.extended(obj)
665
- obj.def_mutation_method(:disable_insert_returning)
666
- end
667
-
668
- # Add the disable_insert_returning! mutation method
669
- def self.included(mod)
670
- mod.def_mutation_method(:disable_insert_returning)
671
- end
672
-
673
- # Return the results of an ANALYZE query as a string
943
+ # Return the results of an EXPLAIN ANALYZE query as a string
674
944
  def analyze
675
945
  explain(:analyze=>true)
676
946
  end
677
-
947
+
678
948
  # Handle converting the ruby xor operator (^) into the
679
949
  # PostgreSQL xor operator (#).
680
- def complex_expression_sql(op, args)
950
+ def complex_expression_sql_append(sql, op, args)
681
951
  case op
682
952
  when :^
683
- "(#{literal(args.at(0))} # #{literal(args.at(1))})"
953
+ j = XOR_OP
954
+ c = false
955
+ args.each do |a|
956
+ sql << j if c
957
+ literal_append(sql, a)
958
+ c ||= true
959
+ end
684
960
  else
685
961
  super
686
962
  end
687
963
  end
688
964
 
689
- # Disable the use of INSERT RETURNING, even if the server supports it
690
- def disable_insert_returning
691
- clone(:disable_insert_returning=>true)
692
- end
693
-
694
965
  # Return the results of an EXPLAIN query as a string
695
966
  def explain(opts={})
696
- with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join("\r\n")
967
+ with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
697
968
  end
698
-
969
+
699
970
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
700
971
  def for_share
701
972
  lock_style(:share)
702
973
  end
703
-
974
+
704
975
  # PostgreSQL specific full text search syntax, using tsearch2 (included
705
976
  # in 8.3 by default, and available for earlier versions as an add-on).
706
977
  def full_text_search(cols, terms, opts = {})
707
978
  lang = opts[:language] || 'simple'
708
- filter("to_tsvector(#{literal(lang)}, #{full_text_string_join(cols)}) @@ to_tsquery(#{literal(lang)}, #{literal(Array(terms).join(' | '))})")
979
+ terms = terms.join(' | ') if terms.is_a?(Array)
980
+ filter("to_tsvector(?::regconfig, ?) @@ to_tsquery(?::regconfig, ?)", lang, full_text_string_join(cols), lang, terms)
709
981
  end
710
-
982
+
711
983
  # Insert given values into the database.
712
984
  def insert(*values)
713
- if @opts[:sql]
714
- execute_insert(insert_sql(*values))
715
- elsif @opts[:disable_insert_returning] || server_version < 80200
716
- execute_insert(insert_sql(*values), :table=>opts[:from].first, :values=>values.size == 1 ? values.first : values)
985
+ if @opts[:returning]
986
+ # already know which columns to return, let the standard code
987
+ # handle it
988
+ super
989
+ elsif @opts[:sql]
990
+ # raw SQL used, so don't know which table is being inserted
991
+ # into, and therefore can't determine primary key. Run the
992
+ # insert statement and return nil.
993
+ super
994
+ nil
717
995
  else
718
- clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
996
+ # Force the use of RETURNING with the primary key value.
997
+ returning(insert_pk).insert(*values){|r| return r.values.first}
719
998
  end
720
999
  end
721
1000
 
722
- # Use the RETURNING clause to return the columns listed in returning.
723
- def insert_returning_sql(returning, *values)
724
- "#{insert_sql(*values)} RETURNING #{column_list(Array(returning))}"
725
- end
726
-
727
1001
  # Insert a record returning the record inserted
728
1002
  def insert_select(*values)
729
- return if opts[:disable_insert_returning] || server_version < 80200
730
- naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
1003
+ returning.insert(*values){|r| return r}
731
1004
  end
732
1005
 
733
1006
  # Locks all tables in the dataset's FROM clause (but not in JOINs) with
@@ -743,50 +1016,124 @@ module Sequel
743
1016
  end
744
1017
  nil
745
1018
  end
746
-
747
- # For PostgreSQL version > 8.2, allow inserting multiple rows at once.
1019
+
1020
+ # PostgreSQL allows inserting multiple rows at once.
748
1021
  def multi_insert_sql(columns, values)
749
- return super if server_version < 80200
750
-
751
- # postgresql 8.2 introduces support for multi-row insert
752
- [insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
1022
+ sql = LiteralString.new('VALUES ')
1023
+ expression_list_append(sql, values.map{|r| Array(r)})
1024
+ [insert_sql(columns, sql)]
753
1025
  end
754
-
1026
+
1027
+ # PostgreSQL supports using the WITH clause in subqueries if it
1028
+ # supports using WITH at all (i.e. on PostgreSQL 8.4+).
1029
+ def supports_cte_in_subqueries?
1030
+ supports_cte?
1031
+ end
1032
+
755
1033
  # DISTINCT ON is a PostgreSQL extension
756
1034
  def supports_distinct_on?
757
1035
  true
758
1036
  end
759
-
1037
+
760
1038
  # PostgreSQL supports modifying joined datasets
761
1039
  def supports_modifying_joins?
762
1040
  true
763
1041
  end
764
1042
 
1043
+ # Returning is always supported.
1044
+ def supports_returning?(type)
1045
+ true
1046
+ end
1047
+
1048
+ # PostgreSQL supports pattern matching via regular expressions
1049
+ def supports_regexp?
1050
+ true
1051
+ end
1052
+
765
1053
  # PostgreSQL supports timezones in literal timestamps
766
1054
  def supports_timestamp_timezones?
767
1055
  true
768
1056
  end
769
-
1057
+
770
1058
  # PostgreSQL 8.4+ supports window functions
771
1059
  def supports_window_functions?
772
1060
  server_version >= 80400
773
1061
  end
774
1062
 
1063
+ # Truncates the dataset. Returns nil.
1064
+ #
1065
+ # Options:
1066
+ # :cascade :: whether to use the CASCADE option, useful when truncating
1067
+ # tables with Foreign Keys.
1068
+ # :only :: truncate using ONLY, so child tables are unaffected
1069
+ # :restart :: use RESTART IDENTITY to restart any related sequences
1070
+ #
1071
+ # :only and :restart only work correctly on PostgreSQL 8.4+.
1072
+ #
1073
+ # Usage:
1074
+ # DB[:table].truncate # TRUNCATE TABLE "table"
1075
+ # # => nil
1076
+ # DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
1077
+ # # => nil
1078
+ def truncate(opts = {})
1079
+ if opts.empty?
1080
+ super()
1081
+ else
1082
+ clone(:truncate_opts=>opts).truncate
1083
+ end
1084
+ end
1085
+
775
1086
  # Return a clone of the dataset with an addition named window that can be referenced in window functions.
776
1087
  def window(name, opts)
777
1088
  clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
778
1089
  end
779
-
1090
+
1091
+ protected
1092
+
1093
+ # If returned primary keys are requested, use RETURNING unless already set on the
1094
+ # dataset. If RETURNING is already set, use existing returning values. If RETURNING
1095
+ # is only set to return a single columns, return an array of just that column.
1096
+ # Otherwise, return an array of hashes.
1097
+ def _import(columns, values, opts={})
1098
+ if @opts[:returning]
1099
+ statements = multi_insert_sql(columns, values)
1100
+ @db.transaction(opts.merge(:server=>@opts[:server])) do
1101
+ statements.map{|st| returning_fetch_rows(st)}
1102
+ end.first.map{|v| v.length == 1 ? v.values.first : v}
1103
+ elsif opts[:return] == :primary_key
1104
+ returning(insert_pk)._import(columns, values, opts)
1105
+ else
1106
+ super
1107
+ end
1108
+ end
1109
+
780
1110
  private
781
-
1111
+
1112
+ # Format TRUNCATE statement with PostgreSQL specific options.
1113
+ def _truncate_sql(table)
1114
+ to = @opts[:truncate_opts] || {}
1115
+ "TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
1116
+ end
1117
+
1118
+ # Allow truncation of multiple source tables.
1119
+ def check_truncation_allowed!
1120
+ raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
1121
+ raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
1122
+ end
1123
+
782
1124
  # PostgreSQL allows deleting from joined datasets
783
1125
  def delete_clause_methods
784
- DELETE_CLAUSE_METHODS
785
- end
1126
+ if server_version >= 90100
1127
+ DELETE_CLAUSE_METHODS_91
1128
+ else
1129
+ DELETE_CLAUSE_METHODS
1130
+ end
1131
+ end
786
1132
 
787
1133
  # Only include the primary table in the main delete clause
788
1134
  def delete_from_sql(sql)
789
- sql << " FROM #{source_list(@opts[:from][0..0])}"
1135
+ sql << FROM
1136
+ source_list_append(sql, @opts[:from][0..0])
790
1137
  end
791
1138
 
792
1139
  # Use USING to specify additional tables in a delete query
@@ -794,36 +1141,60 @@ module Sequel
794
1141
  join_from_sql(:USING, sql)
795
1142
  end
796
1143
 
797
- # Use the RETURNING clause to return the primary key of the inserted record, if it exists
798
- def insert_returning_pk_sql(*values)
799
- pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
800
- insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
1144
+ # PostgreSQL allows a RETURNING clause.
1145
+ def insert_clause_methods
1146
+ if server_version >= 90100
1147
+ INSERT_CLAUSE_METHODS_91
1148
+ else
1149
+ INSERT_CLAUSE_METHODS
1150
+ end
801
1151
  end
802
-
1152
+
1153
+ # Return the primary key to use for RETURNING in an INSERT statement
1154
+ def insert_pk
1155
+ if (f = opts[:from]) && !f.empty? && (pk = db.primary_key(f.first))
1156
+ Sequel::SQL::Identifier.new(pk)
1157
+ end
1158
+ end
1159
+
803
1160
  # For multiple table support, PostgreSQL requires at least
804
1161
  # two from tables, with joins allowed.
805
1162
  def join_from_sql(type, sql)
806
1163
  if(from = @opts[:from][1..-1]).empty?
807
1164
  raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
808
1165
  else
809
- sql << " #{type} #{source_list(from)}"
1166
+ sql << SPACE << type.to_s << SPACE
1167
+ source_list_append(sql, from)
810
1168
  select_join_sql(sql)
811
1169
  end
812
1170
  end
813
1171
 
814
1172
  # Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
815
- def literal_blob(v)
816
- "'#{v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"}}'"
1173
+ def literal_blob_append(sql, v)
1174
+ sql << APOS << v.gsub(BLOB_RE){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << APOS
817
1175
  end
818
1176
 
819
1177
  # PostgreSQL uses FALSE for false values
820
1178
  def literal_false
821
1179
  BOOL_FALSE
822
1180
  end
1181
+
1182
+ # PostgreSQL quotes NaN and Infinity.
1183
+ def literal_float(value)
1184
+ if value.finite?
1185
+ super
1186
+ elsif value.nan?
1187
+ "'NaN'"
1188
+ elsif value.infinite? == 1
1189
+ "'Infinity'"
1190
+ else
1191
+ "'-Infinity'"
1192
+ end
1193
+ end
823
1194
 
824
1195
  # Assume that SQL standard quoting is on, per Sequel's defaults
825
- def literal_string(v)
826
- "'#{v.gsub("'", "''")}'"
1196
+ def literal_string_append(sql, v)
1197
+ sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
827
1198
  end
828
1199
 
829
1200
  # PostgreSQL uses FALSE for false values
@@ -835,7 +1206,15 @@ module Sequel
835
1206
  def select_clause_methods
836
1207
  server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
837
1208
  end
838
-
1209
+
1210
+ # PostgreSQL requires parentheses around compound datasets if they use
1211
+ # CTEs, and using them in other places doesn't hurt.
1212
+ def compound_dataset_sql_append(sql, ds)
1213
+ sql << PAREN_OPEN
1214
+ super
1215
+ sql << PAREN_CLOSE
1216
+ end
1217
+
839
1218
  # Support FOR SHARE locking when using the :share lock style.
840
1219
  def select_lock_sql(sql)
841
1220
  @opts[:lock] == :share ? (sql << FOR_SHARE) : super
@@ -843,14 +1222,26 @@ module Sequel
843
1222
 
844
1223
  # SQL fragment for named window specifications
845
1224
  def select_window_sql(sql)
846
- sql << " WINDOW #{@opts[:window].map{|name, window| "#{literal(name)} AS #{literal(window)}"}.join(', ')}" if @opts[:window]
1225
+ if ws = @opts[:window]
1226
+ sql << WINDOW
1227
+ c = false
1228
+ co = COMMA
1229
+ as = AS
1230
+ ws.map do |name, window|
1231
+ sql << co if c
1232
+ literal_append(sql, name)
1233
+ sql << as
1234
+ literal_append(sql, window)
1235
+ c ||= true
1236
+ end
1237
+ end
847
1238
  end
848
-
1239
+
849
1240
  # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
850
1241
  def select_with_sql_base
851
1242
  opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
852
1243
  end
853
-
1244
+
854
1245
  # The version of the database server
855
1246
  def server_version
856
1247
  db.server_version(@opts[:server])
@@ -858,15 +1249,19 @@ module Sequel
858
1249
 
859
1250
  # Concatenate the expressions with a space in between
860
1251
  def full_text_string_join(cols)
861
- cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
862
- cols = cols.zip([' '] * cols.length).flatten
1252
+ cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, EMPTY_STRING)}
1253
+ cols = cols.zip([SPACE] * cols.length).flatten
863
1254
  cols.pop
864
- literal(SQL::StringExpression.new(:'||', *cols))
1255
+ SQL::StringExpression.new(:'||', *cols)
865
1256
  end
866
1257
 
867
1258
  # PostgreSQL splits the main table from the joined tables
868
1259
  def update_clause_methods
869
- UPDATE_CLAUSE_METHODS
1260
+ if server_version >= 90100
1261
+ UPDATE_CLAUSE_METHODS_91
1262
+ else
1263
+ UPDATE_CLAUSE_METHODS
1264
+ end
870
1265
  end
871
1266
 
872
1267
  # Use FROM to specify additional tables in an update query
@@ -876,7 +1271,8 @@ module Sequel
876
1271
 
877
1272
  # Only include the primary table in the main update clause
878
1273
  def update_table_sql(sql)
879
- sql << " #{source_list(@opts[:from][0..0])}"
1274
+ sql << SPACE
1275
+ source_list_append(sql, @opts[:from][0..0])
880
1276
  end
881
1277
  end
882
1278
  end