sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -1,6 +1,4 @@
1
1
  module Sequel
2
- Dataset::NON_SQL_OPTIONS << :disable_insert_returning
3
-
4
2
  # Top level module for holding all PostgreSQL-related modules and classes
5
3
  # for Sequel. There are a few module level accessors that are added via
6
4
  # metaprogramming. These are:
@@ -33,10 +31,10 @@ module Sequel
33
31
  # Array of exceptions that need to be converted. JDBC
34
32
  # uses NativeExceptions, the native adapter uses PGError.
35
33
  CONVERTED_EXCEPTIONS = []
36
-
34
+
37
35
  @client_min_messages = :warning
38
36
  @force_standard_strings = true
39
-
37
+
40
38
  class << self
41
39
  # By default, Sequel sets the minimum level of log messages sent to the client
42
40
  # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
@@ -45,25 +43,32 @@ module Sequel
45
43
  attr_accessor :client_min_messages
46
44
 
47
45
  # 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.
46
+ # '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
47
+ # to interpreting plain strings, newer versions use standard strings by
48
+ # default. Sequel assumes that SQL standard strings will be used. Setting
49
+ # this to false means Sequel will use the database's default.
52
50
  attr_accessor :force_standard_strings
53
51
  end
54
52
 
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
53
+ # Methods shared by Database instances that connect to PostgreSQL.
54
+ module DatabaseMethods
55
+ EXCLUDE_SCHEMAS = /pg_*|information_schema/i
56
+ PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
57
+ RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
58
+ SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
59
+ 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
60
+
61
+ # SQL fragment for custom sequences (ones not created by serial primary key),
62
+ # Returning the schema and literal form of the sequence name, by parsing
63
+ # the column defaults table.
64
+ SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
65
+ SELECT name.nspname AS "schema",
66
+ CASE
67
+ WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
68
+ substr(split_part(def.adsrc, '''', 2),
69
+ strpos(split_part(def.adsrc, '''', 2), '.')+1)
70
+ ELSE split_part(def.adsrc, '''', 2)
71
+ END AS "sequence"
67
72
  FROM pg_class t
68
73
  JOIN pg_namespace name ON (t.relnamespace = name.oid)
69
74
  JOIN pg_attribute attr ON (t.oid = attrelid)
@@ -71,24 +76,26 @@ module Sequel
71
76
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
72
77
  WHERE cons.contype = 'p'
73
78
  AND def.adsrc ~* 'nextval'
74
- #{"AND name.nspname = '#{schema}'" if schema}
75
- AND t.relname = '#{table}'
76
79
  end_sql
77
- end
78
- SELECT_PK = proc do |schema, table| <<-end_sql
79
- SELECT pg_attribute.attname
80
+ ).strip.gsub(/\s+/, ' ').freeze
81
+
82
+ # SQL fragment for determining primary key column for the given table. Only
83
+ # returns the first primary key if the table has a composite primary key.
84
+ SELECT_PK_SQL = (<<-end_sql
85
+ SELECT pg_attribute.attname AS pk
80
86
  FROM pg_class, pg_attribute, pg_index, pg_namespace
81
87
  WHERE pg_class.oid = pg_attribute.attrelid
82
88
  AND pg_class.relnamespace = pg_namespace.oid
83
89
  AND pg_class.oid = pg_index.indrelid
84
90
  AND pg_index.indkey[0] = pg_attribute.attnum
85
91
  AND pg_index.indisprimary = 't'
86
- #{"AND pg_namespace.nspname = '#{schema}'" if schema}
87
- AND pg_class.relname = '#{table}'
88
92
  end_sql
89
- end
90
- SELECT_SERIAL_SEQUENCE = proc do |schema, table| <<-end_sql
91
- SELECT '"' || name.nspname || '".' || seq.relname || ''
93
+ ).strip.gsub(/\s+/, ' ').freeze
94
+
95
+ # SQL fragment for getting sequence associated with table's
96
+ # primary key, assuming it was a serial primary key column.
97
+ SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
98
+ SELECT name.nspname AS "schema", seq.relname AS "sequence"
92
99
  FROM pg_class seq, pg_attribute attr, pg_depend dep,
93
100
  pg_namespace name, pg_constraint cons
94
101
  WHERE seq.oid = dep.objid
@@ -99,68 +106,8 @@ module Sequel
99
106
  AND attr.attrelid = cons.conrelid
100
107
  AND attr.attnum = cons.conkey[1]
101
108
  AND cons.contype = 'p'
102
- #{"AND name.nspname = '#{schema}'" if schema}
103
- AND seq.relname = '#{table}'
104
109
  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
125
-
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
110
+ ).strip.gsub(/\s+/, ' ').freeze
164
111
 
165
112
  # Commit an existing prepared transaction with the given transaction
166
113
  # identifier string.
@@ -191,7 +138,7 @@ module Sequel
191
138
  def create_function(name, definition, opts={})
192
139
  self << create_function_sql(name, definition, opts)
193
140
  end
194
-
141
+
195
142
  # Create the procedural language in the database. Arguments:
196
143
  # * name : Name of the procedural language (e.g. plpgsql)
197
144
  # * opts : options hash:
@@ -202,7 +149,13 @@ module Sequel
202
149
  def create_language(name, opts={})
203
150
  self << create_language_sql(name, opts)
204
151
  end
205
-
152
+
153
+ # Create a schema in the database. Arguments:
154
+ # * name : Name of the schema (e.g. admin)
155
+ def create_schema(name)
156
+ self << create_schema_sql(name)
157
+ end
158
+
206
159
  # Create a trigger in the database. Arguments:
207
160
  # * table : the table on which this trigger operates
208
161
  # * name : the name of this trigger
@@ -216,7 +169,7 @@ module Sequel
216
169
  def create_trigger(table, name, function, opts={})
217
170
  self << create_trigger_sql(table, name, function, opts)
218
171
  end
219
-
172
+
220
173
  # PostgreSQL uses the :postgres database type.
221
174
  def database_type
222
175
  :postgres
@@ -231,7 +184,7 @@ module Sequel
231
184
  def drop_function(name, opts={})
232
185
  self << drop_function_sql(name, opts)
233
186
  end
234
-
187
+
235
188
  # Drops a procedural language from the database. Arguments:
236
189
  # * name : name of the procedural language to drop
237
190
  # * opts : options hash:
@@ -240,15 +193,14 @@ module Sequel
240
193
  def drop_language(name, opts={})
241
194
  self << drop_language_sql(name, opts)
242
195
  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
196
+
197
+ # Drops a schema from the database. Arguments:
198
+ # * name : name of the schema to drop
199
+ # * opts : options hash:
200
+ # * :cascade : Drop all objects in this schema.
201
+ # * :if_exists : Don't raise an error if the schema doesn't exist.
202
+ def drop_schema(name, opts={})
203
+ self << drop_schema_sql(name, opts)
252
204
  end
253
205
 
254
206
  # Drops a trigger from the database. Arguments:
@@ -260,7 +212,65 @@ module Sequel
260
212
  def drop_trigger(table, name, opts={})
261
213
  self << drop_trigger_sql(table, name, opts)
262
214
  end
263
-
215
+
216
+ # Return full foreign key information using the pg system tables, including
217
+ # :name, :on_delete, :on_update, and :deferrable entries in the hashes.
218
+ def foreign_key_list(table, opts={})
219
+ m = output_identifier_meth
220
+ im = input_identifier_meth
221
+ schema, table = schema_and_table(table)
222
+ range = 0...32
223
+
224
+ base_ds = metadata_dataset.
225
+ where(:cl__relkind=>'r', :co__contype=>'f', :cl__relname=>im.call(table)).
226
+ from(:pg_constraint___co).
227
+ join(:pg_class___cl, :oid=>:conrelid)
228
+
229
+ # We split the parsing into two separate queries, which are merged manually later.
230
+ # This is because PostgreSQL stores both the referencing and referenced columns in
231
+ # arrays, and I don't know a simple way to not create a cross product, as PostgreSQL
232
+ # doesn't appear to have a function that takes an array and element and gives you
233
+ # the index of that element in the array.
234
+
235
+ ds = base_ds.
236
+ join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
237
+ order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
238
+ select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
239
+ SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
240
+
241
+ ref_ds = base_ds.
242
+ join(:pg_class___cl2, :oid=>:co__confrelid).
243
+ join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
244
+ order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att2__attnum)).
245
+ select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
246
+
247
+ # If a schema is given, we only search in that schema, and the returned :table
248
+ # entry is schema qualified as well.
249
+ if schema
250
+ ds = ds.join(:pg_namespace___nsp, :oid=>:cl__relnamespace).
251
+ where(:nsp__nspname=>im.call(schema))
252
+ ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
253
+ select_more(:nsp2__nspname___schema)
254
+ end
255
+
256
+ h = {}
257
+ fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
258
+ ds.each do |row|
259
+ if r = h[row[:name]]
260
+ r[:columns] << m.call(row[:column])
261
+ else
262
+ 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]}
263
+ end
264
+ end
265
+ ref_ds.each do |row|
266
+ r = h[row[:name]]
267
+ r[:table] ||= schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
268
+ r[:key] ||= []
269
+ r[:key] << m.call(row[:refcolumn])
270
+ end
271
+ h.values
272
+ end
273
+
264
274
  # Use the pg_* system tables to determine indexes on a table
265
275
  def indexes(table, opts={})
266
276
  m = output_identifier_meth
@@ -273,14 +283,13 @@ module Sequel
273
283
  join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
274
284
  join(:pg_class___indc, :oid=>:indexrelid).
275
285
  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)).
286
+ filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true).
287
+ order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
278
288
  select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
279
-
289
+
280
290
  ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
281
- ds.filter!(:indisvalid=>true) if server_version >= 80200
282
291
  ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
283
-
292
+
284
293
  indexes = {}
285
294
  ds.each do |r|
286
295
  i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique]}
@@ -289,40 +298,59 @@ module Sequel
289
298
  indexes
290
299
  end
291
300
 
292
- # Dataset containing all current database locks
301
+ # Dataset containing all current database locks
293
302
  def locks
294
303
  dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
295
304
  end
296
-
305
+
306
+ # Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
307
+ #
308
+ # :payload :: The payload string to use for the NOTIFY statement. Only supported
309
+ # in PostgreSQL 9.0+.
310
+ # :server :: The server to which to send the NOTIFY statement, if the sharding support
311
+ # is being used.
312
+ def notify(channel, opts={})
313
+ execute_ddl("NOTIFY #{channel}#{", #{literal(opts[:payload].to_s)}" if opts[:payload]}", opts)
314
+ end
315
+
297
316
  # Return primary key for the given table.
298
317
  def primary_key(table, opts={})
299
318
  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))}
319
+ @primary_keys.fetch(quoted_table) do
320
+ schema, table = schema_and_table(table)
321
+ sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
322
+ sql << "AND pg_namespace.nspname = #{literal(schema)}" if schema
323
+ @primary_keys[quoted_table] = fetch(sql).single_value
305
324
  end
306
325
  end
307
-
326
+
308
327
  # Return the sequence providing the default for the primary key for the given table.
309
328
  def primary_key_sequence(table, opts={})
310
329
  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))
314
- else
315
- synchronize(opts[:server]){|con| con.sequence(*schema_and_table(table))}
330
+ @primary_key_sequences.fetch(quoted_table) do
331
+ schema, table = schema_and_table(table)
332
+ table = literal(table)
333
+ sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
334
+ sql << " AND name.nspname = #{literal(schema)}" if schema
335
+ unless pks = fetch(sql).single_record
336
+ sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
337
+ sql << " AND name.nspname = #{literal(schema)}" if schema
338
+ pks = fetch(sql).single_record
339
+ end
340
+
341
+ @primary_key_sequences[quoted_table] = if pks
342
+ literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
343
+ end
316
344
  end
317
345
  end
318
-
346
+
319
347
  # Reset the primary key sequence for the given table, baseing it on the
320
348
  # maximum current value of the table's primary key.
321
349
  def reset_primary_key_sequence(table)
322
- pk = SQL::Identifier.new(primary_key(table))
323
350
  return unless seq = primary_key_sequence(table)
351
+ pk = SQL::Identifier.new(primary_key(table))
324
352
  db = self
325
- seq_ds = db.from(seq.lit)
353
+ seq_ds = db.from(LiteralString.new(seq))
326
354
  get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
327
355
  end
328
356
 
@@ -337,7 +365,7 @@ module Sequel
337
365
  def serial_primary_key_options
338
366
  {:primary_key => true, :serial => true, :type=>Integer}
339
367
  end
340
-
368
+
341
369
  # The version of the PostgreSQL server, used for determining capability.
342
370
  def server_version(server=nil)
343
371
  return @server_version if @server_version
@@ -345,12 +373,26 @@ module Sequel
345
373
  (conn.server_version rescue nil) if conn.respond_to?(:server_version)
346
374
  end
347
375
  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
376
+ @server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
377
+ (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
378
+ else
379
+ 0
380
+ end
350
381
  end
382
+ warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
351
383
  @server_version
352
384
  end
353
-
385
+
386
+ # PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
387
+ def supports_create_table_if_not_exists?
388
+ server_version >= 90100
389
+ end
390
+
391
+ # PostgreSQL supports DROP TABLE IF EXISTS
392
+ def supports_drop_table_if_exists?
393
+ true
394
+ end
395
+
354
396
  # PostgreSQL supports prepared transactions (two-phase commit) if
355
397
  # max_prepared_transactions is greater than 0.
356
398
  def supports_prepared_transactions?
@@ -368,44 +410,95 @@ module Sequel
368
410
  true
369
411
  end
370
412
 
371
- # Whether the given table exists in the database
413
+ # PostgreSQL supports transaction DDL statements.
414
+ def supports_transactional_ddl?
415
+ true
416
+ end
417
+
418
+ # Array of symbols specifying table names in the current database.
419
+ # The dataset used is yielded to the block if one is provided,
420
+ # otherwise, an array of symbols of table names is returned.
372
421
  #
373
422
  # Options:
374
423
  # * :schema - The schema to search (default_schema by default)
375
424
  # * :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?}
425
+ def tables(opts={}, &block)
426
+ pg_class_relname('r', opts, &block)
381
427
  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.
428
+
429
+ # Check whether the given type name string/symbol (e.g. :hstore) is supported by
430
+ # the database.
431
+ def type_supported?(type)
432
+ @supported_types ||= {}
433
+ @supported_types.fetch(type){@supported_types[type] = (from(:pg_type).filter(:typtype=>'b', :typname=>type.to_s).count > 0)}
434
+ end
435
+
436
+ # Array of symbols specifying view names in the current database.
386
437
  #
387
438
  # Options:
388
439
  # * :schema - The schema to search (default_schema by default)
389
440
  # * :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])}
441
+ def views(opts={})
442
+ pg_class_relname('v', opts)
395
443
  end
396
444
 
397
445
  private
398
446
 
447
+ # Handle :using option for set_column_type op.
448
+ def alter_table_sql(table, op)
449
+ case op[:op]
450
+ when :set_column_type
451
+ s = super
452
+ if using = op[:using]
453
+ using = Sequel::LiteralString.new(using) if using.is_a?(String)
454
+ s << ' USING '
455
+ s << literal(using)
456
+ end
457
+ s
458
+ else
459
+ super
460
+ end
461
+ end
462
+
463
+ # If the :synchronous option is given and non-nil, set synchronous_commit
464
+ # appropriately. Valid values for the :synchronous option are true,
465
+ # :on, false, :off, :local, and :remote_write.
466
+ def begin_new_transaction(conn, opts)
467
+ super
468
+ if opts.has_key?(:synchronous)
469
+ case sync = opts[:synchronous]
470
+ when true
471
+ sync = :on
472
+ when false
473
+ sync = :off
474
+ when nil
475
+ return
476
+ end
477
+
478
+ log_connection_execute(conn, "SET LOCAL synchronous_commit = #{sync}")
479
+ end
480
+ end
481
+
399
482
  # If the :prepare option is given and we aren't in a savepoint,
400
483
  # prepare the transaction for a two-phase commit.
401
484
  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])}")
485
+ if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
486
+ log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
404
487
  else
405
488
  super
406
489
  end
407
490
  end
408
491
 
492
+ # The SQL queries to execute when starting a new connection.
493
+ def connection_configuration_sqls
494
+ sqls = []
495
+ sqls << "SET standard_conforming_strings = ON" if Postgres.force_standard_strings
496
+ if cmm = Postgres.client_min_messages
497
+ sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
498
+ end
499
+ sqls
500
+ end
501
+
409
502
  # SQL statement to create database function.
410
503
  def create_function_sql(name, definition, opts={})
411
504
  args = opts[:args]
@@ -426,39 +519,49 @@ module Sequel
426
519
  AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
427
520
  END
428
521
  end
429
-
522
+
430
523
  # SQL for creating a procedural language.
431
524
  def create_language_sql(name, opts={})
432
525
  "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
526
  end
434
-
435
- # SQL for creating a database trigger.
527
+
528
+ # SQL for creating a schema.
529
+ def create_schema_sql(name)
530
+ "CREATE SCHEMA #{quote_identifier(name)}"
531
+ end
532
+
533
+ # SQL for creating a database trigger.
436
534
  def create_trigger_sql(table, name, function, opts={})
437
535
  events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
438
536
  whence = opts[:after] ? 'AFTER' : 'BEFORE'
439
537
  "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
538
  end
441
-
539
+
442
540
  # The errors that the main adapters can raise, depends on the adapter being used
443
541
  def database_error_classes
444
542
  CONVERTED_EXCEPTIONS
445
543
  end
446
-
447
- # SQL for dropping a function from the database.
544
+
545
+ # SQL for dropping a function from the database.
448
546
  def drop_function_sql(name, opts={})
449
547
  "DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
450
548
  end
451
549
 
550
+ # Support :if_exists, :cascade, and :concurrently options.
551
+ def drop_index_sql(table, op)
552
+ "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]}"
553
+ end
554
+
452
555
  # SQL for dropping a procedural language from the database.
453
556
  def drop_language_sql(name, opts={})
454
557
  "DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
455
558
  end
456
559
 
457
- # Always CASCADE the table drop
458
- def drop_table_sql(name)
459
- "DROP TABLE #{quote_schema_table(name)} CASCADE"
560
+ # SQL for dropping a schema from the database.
561
+ def drop_schema_sql(name, opts={})
562
+ "DROP SCHEMA#{' IF EXISTS' if opts[:if_exists]} #{quote_identifier(name)}#{' CASCADE' if opts[:cascade]}"
460
563
  end
461
-
564
+
462
565
  # SQL for dropping a trigger from the database.
463
566
  def drop_trigger_sql(table, name, opts={})
464
567
  "DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
@@ -473,12 +576,12 @@ module Sequel
473
576
  ds.exclude(:pg_namespace__nspname=>EXCLUDE_SCHEMAS)
474
577
  end
475
578
  end
476
-
579
+
477
580
  # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
478
581
  def identifier_input_method_default
479
582
  nil
480
583
  end
481
-
584
+
482
585
  # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
483
586
  def identifier_output_method_default
484
587
  nil
@@ -488,7 +591,7 @@ module Sequel
488
591
  def index_definition_sql(table_name, index)
489
592
  cols = index[:columns]
490
593
  index_name = index[:name] || default_index_name(table_name, cols)
491
- expr = if o = index[:opclass]
594
+ expr = if o = index[:opclass]
492
595
  "(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
493
596
  else
494
597
  literal(Array(cols))
@@ -499,58 +602,42 @@ module Sequel
499
602
  filter = " WHERE #{filter_expr(filter)}" if filter
500
603
  case index_type
501
604
  when :full_text
502
- expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{dataset.send(:full_text_string_join, cols)}))"
605
+ expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
503
606
  index_type = :gin
504
607
  when :spatial
505
608
  index_type = :gist
506
609
  end
507
- "CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
508
- 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
610
+ "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}"
536
611
  end
537
612
 
538
- # Don't log, since logging is done by the underlying connection.
539
- def log_connection_execute(conn, sql)
540
- conn.execute(sql)
613
+ # Backbone of the tables and views support.
614
+ def pg_class_relname(type, opts)
615
+ 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)
616
+ ds = filter_schema(ds, opts)
617
+ m = output_identifier_meth
618
+ block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
541
619
  end
542
-
620
+
543
621
  # Use a dollar sign instead of question mark for the argument
544
622
  # placeholder.
545
623
  def prepared_arg_placeholder
546
624
  PREPARED_ARG_PLACEHOLDER
547
625
  end
548
-
626
+
627
+ # Remove the cached entries for primary keys and sequences when a table is
628
+ # changed.
629
+ def remove_cached_schema(table)
630
+ tab = quote_schema_table(table)
631
+ @primary_keys.delete(tab)
632
+ @primary_key_sequences.delete(tab)
633
+ super
634
+ end
635
+
549
636
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
550
637
  # a rename table operation, so speciying a new schema in new_name will not have an effect.
551
638
  def rename_table_sql(name, new_name)
552
639
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
553
- end
640
+ end
554
641
 
555
642
  # PostgreSQL's autoincrementing primary keys are of type integer or bigint
556
643
  # using a nextval function call as a default.
@@ -560,13 +647,14 @@ module Sequel
560
647
 
561
648
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
562
649
  def schema_parse_table(table_name, opts)
563
- m = output_identifier_meth
564
- m2 = input_identifier_meth
650
+ m = output_identifier_meth(opts[:dataset])
651
+ m2 = input_identifier_meth(opts[:dataset])
565
652
  ds = metadata_dataset.select(:pg_attribute__attname___name,
566
653
  SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
567
654
  SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
568
655
  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)).
656
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key),
657
+ :pg_namespace__nspname).
570
658
  from(:pg_class).
571
659
  join(:pg_attribute, :attrelid=>:oid).
572
660
  join(:pg_type, :oid=>:atttypid).
@@ -578,18 +666,41 @@ module Sequel
578
666
  filter(:pg_class__relname=>m2.call(table_name)).
579
667
  order(:pg_attribute__attnum)
580
668
  ds = filter_schema(ds, opts)
669
+ current_schema = nil
581
670
  ds.map do |row|
671
+ sch = row.delete(:nspname)
672
+ if current_schema
673
+ if sch != current_schema
674
+ raise Error, "columns from tables in two separate schema were returned (please specify a schema): #{current_schema.inspect}, #{sch.inspect}"
675
+ end
676
+ else
677
+ current_schema = sch
678
+ end
582
679
  row[:default] = nil if blank_object?(row[:default])
583
680
  row[:type] = schema_column_type(row[:db_type])
584
681
  [m.call(row.delete(:name)), row]
585
682
  end
586
683
  end
587
684
 
685
+ # Set the transaction isolation level on the given connection
686
+ def set_transaction_isolation(conn, opts)
687
+ level = opts.fetch(:isolation, transaction_isolation_level)
688
+ read_only = opts[:read_only]
689
+ deferrable = opts[:deferrable]
690
+ if level || !read_only.nil? || !deferrable.nil?
691
+ sql = "SET TRANSACTION"
692
+ sql << " ISOLATION LEVEL #{Sequel::Database::TRANSACTION_ISOLATION_LEVELS[level]}" if level
693
+ sql << " READ #{read_only ? 'ONLY' : 'WRITE'}" unless read_only.nil?
694
+ sql << " #{'NOT ' unless deferrable}DEFERRABLE" unless deferrable.nil?
695
+ log_connection_execute(conn, sql)
696
+ end
697
+ end
698
+
588
699
  # Turns an array of argument specifiers into an SQL fragment used for function arguments. See create_function_sql.
589
700
  def sql_function_args(args)
590
701
  "(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
591
702
  end
592
-
703
+
593
704
  # Handle bigserial type if :serial option is present
594
705
  def type_literal_generic_bignum(column)
595
706
  column[:serial] ? :bigserial : super
@@ -619,7 +730,7 @@ module Sequel
619
730
  end
620
731
  end
621
732
  end
622
-
733
+
623
734
  # Instance methods for datasets that connect to a PostgreSQL database.
624
735
  module DatasetMethods
625
736
  ACCESS_SHARE = 'ACCESS SHARE'.freeze
@@ -627,107 +738,123 @@ module Sequel
627
738
  BOOL_FALSE = 'false'.freeze
628
739
  BOOL_TRUE = 'true'.freeze
629
740
  COMMA_SEPARATOR = ', '.freeze
630
- DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
741
+ DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where returning')
742
+ DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
631
743
  EXCLUSIVE = 'EXCLUSIVE'.freeze
632
744
  EXPLAIN = 'EXPLAIN '.freeze
633
745
  EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
634
746
  FOR_SHARE = ' FOR SHARE'.freeze
747
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
748
+ INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
635
749
  LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
636
750
  NULL = LiteralString.new('NULL').freeze
637
751
  PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
638
752
  QUERY_PLAN = 'QUERY PLAN'.to_sym
639
753
  ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
640
754
  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')
755
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
756
+ SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having window compounds order limit lock')
643
757
  SHARE = 'SHARE'.freeze
644
758
  SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
645
759
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
646
760
  SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
647
- UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
648
-
761
+ UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where returning')
762
+ UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
763
+ SPACE = Dataset::SPACE
764
+ FROM = Dataset::FROM
765
+ APOS = Dataset::APOS
766
+ APOS_RE = Dataset::APOS_RE
767
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
768
+ PAREN_OPEN = Dataset::PAREN_OPEN
769
+ PAREN_CLOSE = Dataset::PAREN_CLOSE
770
+ COMMA = Dataset::COMMA
771
+ AS = Dataset::AS
772
+ XOR_OP = ' # '.freeze
773
+ CRLF = "\r\n".freeze
774
+ BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
775
+ WINDOW = " WINDOW ".freeze
776
+ EMPTY_STRING = ''.freeze
777
+
649
778
  # Shared methods for prepared statements when used with PostgreSQL databases.
650
779
  module PreparedStatementMethods
651
780
  # Override insert action to use RETURNING if the server supports it.
781
+ def run
782
+ if @prepared_type == :insert
783
+ fetch_rows(prepared_sql){|r| return r.values.first}
784
+ else
785
+ super
786
+ end
787
+ end
788
+
652
789
  def prepared_sql
653
790
  return @prepared_sql if @prepared_sql
791
+ @opts[:returning] = insert_pk if @prepared_type == :insert
654
792
  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
793
  @prepared_sql
660
794
  end
661
795
  end
662
796
 
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
797
+ # Return the results of an EXPLAIN ANALYZE query as a string
674
798
  def analyze
675
799
  explain(:analyze=>true)
676
800
  end
677
-
801
+
678
802
  # Handle converting the ruby xor operator (^) into the
679
803
  # PostgreSQL xor operator (#).
680
- def complex_expression_sql(op, args)
804
+ def complex_expression_sql_append(sql, op, args)
681
805
  case op
682
806
  when :^
683
- "(#{literal(args.at(0))} # #{literal(args.at(1))})"
807
+ j = XOR_OP
808
+ c = false
809
+ args.each do |a|
810
+ sql << j if c
811
+ literal_append(sql, a)
812
+ c ||= true
813
+ end
684
814
  else
685
815
  super
686
816
  end
687
817
  end
688
818
 
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
819
  # Return the results of an EXPLAIN query as a string
695
820
  def explain(opts={})
696
- with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join("\r\n")
821
+ with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
697
822
  end
698
-
823
+
699
824
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
700
825
  def for_share
701
826
  lock_style(:share)
702
827
  end
703
-
828
+
704
829
  # PostgreSQL specific full text search syntax, using tsearch2 (included
705
830
  # in 8.3 by default, and available for earlier versions as an add-on).
706
831
  def full_text_search(cols, terms, opts = {})
707
832
  lang = opts[:language] || 'simple'
708
- filter("to_tsvector(#{literal(lang)}, #{full_text_string_join(cols)}) @@ to_tsquery(#{literal(lang)}, #{literal(Array(terms).join(' | '))})")
833
+ terms = terms.join(' | ') if terms.is_a?(Array)
834
+ filter("to_tsvector(?::regconfig, ?) @@ to_tsquery(?::regconfig, ?)", lang, full_text_string_join(cols), lang, terms)
709
835
  end
710
-
836
+
711
837
  # Insert given values into the database.
712
838
  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)
839
+ if @opts[:returning]
840
+ # already know which columns to return, let the standard code
841
+ # handle it
842
+ super
843
+ elsif @opts[:sql]
844
+ # raw SQL used, so don't know which table is being inserted
845
+ # into, and therefore can't determine primary key. Run the
846
+ # insert statement and return nil.
847
+ super
848
+ nil
717
849
  else
718
- clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
850
+ # Force the use of RETURNING with the primary key value.
851
+ returning(insert_pk).insert(*values){|r| return r.values.first}
719
852
  end
720
853
  end
721
854
 
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
855
  # Insert a record returning the record inserted
728
856
  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
857
+ returning.insert(*values){|r| return r}
731
858
  end
732
859
 
733
860
  # Locks all tables in the dataset's FROM clause (but not in JOINs) with
@@ -743,50 +870,119 @@ module Sequel
743
870
  end
744
871
  nil
745
872
  end
746
-
747
- # For PostgreSQL version > 8.2, allow inserting multiple rows at once.
873
+
874
+ # PostgreSQL allows inserting multiple rows at once.
748
875
  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)))]
876
+ sql = LiteralString.new('VALUES ')
877
+ expression_list_append(sql, values.map{|r| Array(r)})
878
+ [insert_sql(columns, sql)]
753
879
  end
754
-
880
+
881
+ # PostgreSQL supports using the WITH clause in subqueries if it
882
+ # supports using WITH at all (i.e. on PostgreSQL 8.4+).
883
+ def supports_cte_in_subqueries?
884
+ supports_cte?
885
+ end
886
+
755
887
  # DISTINCT ON is a PostgreSQL extension
756
888
  def supports_distinct_on?
757
889
  true
758
890
  end
759
-
891
+
760
892
  # PostgreSQL supports modifying joined datasets
761
893
  def supports_modifying_joins?
762
894
  true
763
895
  end
764
896
 
897
+ # Returning is always supported.
898
+ def supports_returning?(type)
899
+ true
900
+ end
901
+
765
902
  # PostgreSQL supports timezones in literal timestamps
766
903
  def supports_timestamp_timezones?
767
904
  true
768
905
  end
769
-
906
+
770
907
  # PostgreSQL 8.4+ supports window functions
771
908
  def supports_window_functions?
772
909
  server_version >= 80400
773
910
  end
774
911
 
912
+ # Truncates the dataset. Returns nil.
913
+ #
914
+ # Options:
915
+ # :cascade :: whether to use the CASCADE option, useful when truncating
916
+ # tables with Foreign Keys.
917
+ # :only :: truncate using ONLY, so child tables are unaffected
918
+ # :restart :: use RESTART IDENTITY to restart any related sequences
919
+ #
920
+ # :only and :restart only work correctly on PostgreSQL 8.4+.
921
+ #
922
+ # Usage:
923
+ # DB[:table].truncate # TRUNCATE TABLE "table"
924
+ # # => nil
925
+ # DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
926
+ # # => nil
927
+ def truncate(opts = {})
928
+ if opts.empty?
929
+ super()
930
+ else
931
+ clone(:truncate_opts=>opts).truncate
932
+ end
933
+ end
934
+
775
935
  # Return a clone of the dataset with an addition named window that can be referenced in window functions.
776
936
  def window(name, opts)
777
937
  clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
778
938
  end
779
-
939
+
940
+ protected
941
+
942
+ # If returned primary keys are requested, use RETURNING unless already set on the
943
+ # dataset. If RETURNING is already set, use existing returning values. If RETURNING
944
+ # is only set to return a single columns, return an array of just that column.
945
+ # Otherwise, return an array of hashes.
946
+ def _import(columns, values, opts={})
947
+ if @opts[:returning]
948
+ statements = multi_insert_sql(columns, values)
949
+ @db.transaction(opts.merge(:server=>@opts[:server])) do
950
+ statements.map{|st| returning_fetch_rows(st)}
951
+ end.first.map{|v| v.length == 1 ? v.values.first : v}
952
+ elsif opts[:return] == :primary_key
953
+ returning(insert_pk)._import(columns, values, opts)
954
+ else
955
+ super
956
+ end
957
+ end
958
+
780
959
  private
781
-
960
+
961
+ # Format TRUNCATE statement with PostgreSQL specific options.
962
+ def _truncate_sql(table)
963
+ to = @opts[:truncate_opts] || {}
964
+ "TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
965
+ end
966
+
967
+ # Allow truncation of multiple source tables.
968
+ def check_truncation_allowed!
969
+ raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
970
+ raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
971
+ end
972
+
782
973
  # PostgreSQL allows deleting from joined datasets
783
974
  def delete_clause_methods
784
- DELETE_CLAUSE_METHODS
785
- end
975
+ if server_version >= 90100
976
+ DELETE_CLAUSE_METHODS_91
977
+ else
978
+ DELETE_CLAUSE_METHODS
979
+ end
980
+ end
786
981
 
787
982
  # Only include the primary table in the main delete clause
788
983
  def delete_from_sql(sql)
789
- sql << " FROM #{source_list(@opts[:from][0..0])}"
984
+ sql << FROM
985
+ source_list_append(sql, @opts[:from][0..0])
790
986
  end
791
987
 
792
988
  # Use USING to specify additional tables in a delete query
@@ -794,36 +990,60 @@ module Sequel
794
990
  join_from_sql(:USING, sql)
795
991
  end
796
992
 
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)
993
+ # PostgreSQL allows a RETURNING clause.
994
+ def insert_clause_methods
995
+ if server_version >= 90100
996
+ INSERT_CLAUSE_METHODS_91
997
+ else
998
+ INSERT_CLAUSE_METHODS
999
+ end
801
1000
  end
802
-
1001
+
1002
+ # Return the primary key to use for RETURNING in an INSERT statement
1003
+ def insert_pk
1004
+ if (f = opts[:from]) && !f.empty? && (pk = db.primary_key(f.first))
1005
+ Sequel::SQL::Identifier.new(pk)
1006
+ end
1007
+ end
1008
+
803
1009
  # For multiple table support, PostgreSQL requires at least
804
1010
  # two from tables, with joins allowed.
805
1011
  def join_from_sql(type, sql)
806
1012
  if(from = @opts[:from][1..-1]).empty?
807
1013
  raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
808
1014
  else
809
- sql << " #{type} #{source_list(from)}"
1015
+ sql << SPACE << type.to_s << SPACE
1016
+ source_list_append(sql, from)
810
1017
  select_join_sql(sql)
811
1018
  end
812
1019
  end
813
1020
 
814
1021
  # 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')}"}}'"
1022
+ def literal_blob_append(sql, v)
1023
+ sql << APOS << v.gsub(BLOB_RE){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << APOS
817
1024
  end
818
1025
 
819
1026
  # PostgreSQL uses FALSE for false values
820
1027
  def literal_false
821
1028
  BOOL_FALSE
822
1029
  end
1030
+
1031
+ # PostgreSQL quotes NaN and Infinity.
1032
+ def literal_float(value)
1033
+ if value.finite?
1034
+ super
1035
+ elsif value.nan?
1036
+ "'NaN'"
1037
+ elsif value.infinite? == 1
1038
+ "'Infinity'"
1039
+ else
1040
+ "'-Infinity'"
1041
+ end
1042
+ end
823
1043
 
824
1044
  # Assume that SQL standard quoting is on, per Sequel's defaults
825
- def literal_string(v)
826
- "'#{v.gsub("'", "''")}'"
1045
+ def literal_string_append(sql, v)
1046
+ sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
827
1047
  end
828
1048
 
829
1049
  # PostgreSQL uses FALSE for false values
@@ -835,7 +1055,15 @@ module Sequel
835
1055
  def select_clause_methods
836
1056
  server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
837
1057
  end
838
-
1058
+
1059
+ # PostgreSQL requires parentheses around compound datasets if they use
1060
+ # CTEs, and using them in other places doesn't hurt.
1061
+ def compound_dataset_sql_append(sql, ds)
1062
+ sql << PAREN_OPEN
1063
+ super
1064
+ sql << PAREN_CLOSE
1065
+ end
1066
+
839
1067
  # Support FOR SHARE locking when using the :share lock style.
840
1068
  def select_lock_sql(sql)
841
1069
  @opts[:lock] == :share ? (sql << FOR_SHARE) : super
@@ -843,14 +1071,26 @@ module Sequel
843
1071
 
844
1072
  # SQL fragment for named window specifications
845
1073
  def select_window_sql(sql)
846
- sql << " WINDOW #{@opts[:window].map{|name, window| "#{literal(name)} AS #{literal(window)}"}.join(', ')}" if @opts[:window]
1074
+ if ws = @opts[:window]
1075
+ sql << WINDOW
1076
+ c = false
1077
+ co = COMMA
1078
+ as = AS
1079
+ ws.map do |name, window|
1080
+ sql << co if c
1081
+ literal_append(sql, name)
1082
+ sql << as
1083
+ literal_append(sql, window)
1084
+ c ||= true
1085
+ end
1086
+ end
847
1087
  end
848
-
1088
+
849
1089
  # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
850
1090
  def select_with_sql_base
851
1091
  opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
852
1092
  end
853
-
1093
+
854
1094
  # The version of the database server
855
1095
  def server_version
856
1096
  db.server_version(@opts[:server])
@@ -858,15 +1098,19 @@ module Sequel
858
1098
 
859
1099
  # Concatenate the expressions with a space in between
860
1100
  def full_text_string_join(cols)
861
- cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
862
- cols = cols.zip([' '] * cols.length).flatten
1101
+ cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, EMPTY_STRING)}
1102
+ cols = cols.zip([SPACE] * cols.length).flatten
863
1103
  cols.pop
864
- literal(SQL::StringExpression.new(:'||', *cols))
1104
+ SQL::StringExpression.new(:'||', *cols)
865
1105
  end
866
1106
 
867
1107
  # PostgreSQL splits the main table from the joined tables
868
1108
  def update_clause_methods
869
- UPDATE_CLAUSE_METHODS
1109
+ if server_version >= 90100
1110
+ UPDATE_CLAUSE_METHODS_91
1111
+ else
1112
+ UPDATE_CLAUSE_METHODS
1113
+ end
870
1114
  end
871
1115
 
872
1116
  # Use FROM to specify additional tables in an update query
@@ -876,7 +1120,8 @@ module Sequel
876
1120
 
877
1121
  # Only include the primary table in the main update clause
878
1122
  def update_table_sql(sql)
879
- sql << " #{source_list(@opts[:from][0..0])}"
1123
+ sql << SPACE
1124
+ source_list_append(sql, @opts[:from][0..0])
880
1125
  end
881
1126
  end
882
1127
  end