viking-sequel 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,195 @@
1
+ module Sequel
2
+ module Oracle
3
+ module DatabaseMethods
4
+ TEMPORARY = 'GLOBAL TEMPORARY '.freeze
5
+ AUTOINCREMENT = ''.freeze
6
+
7
+ def create_sequence(name, opts={})
8
+ self << create_sequence_sql(name, opts)
9
+ end
10
+
11
+ def create_trigger(*args)
12
+ self << create_trigger_sql(*args)
13
+ end
14
+
15
+ def drop_sequence(name)
16
+ self << drop_sequence_sql(name)
17
+ end
18
+
19
+ # Oracle uses the :oracle database type
20
+ def database_type
21
+ :oracle
22
+ end
23
+
24
+ def tables(opts={})
25
+ ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE')
26
+ ds.map{|r| ds.send(:output_identifier, r[:tname])}
27
+ end
28
+
29
+ def table_exists?(name)
30
+ from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
31
+ end
32
+
33
+ private
34
+
35
+ def auto_increment_sql
36
+ AUTOINCREMENT
37
+ end
38
+
39
+ # SQL fragment for showing a table is temporary
40
+ def temporary_table_sql
41
+ TEMPORARY
42
+ end
43
+
44
+ def create_sequence_sql(name, opts={})
45
+ "CREATE SEQUENCE #{quote_identifier(name)} start with #{opts [:start_with]||1} increment by #{opts[:increment_by]||1} nomaxvalue"
46
+ end
47
+
48
+ def create_table_from_generator(name, generator, options)
49
+ drop_statement, create_statements = create_table_sql_list(name, generator, options)
50
+ (execute_ddl(drop_statement) rescue nil) if drop_statement
51
+ create_statements.each{|sql| execute_ddl(sql)}
52
+ end
53
+
54
+ def create_table_sql_list(name, generator, options={})
55
+ statements = [create_table_sql(name, generator, options)]
56
+ drop_seq_statement = nil
57
+ generator.columns.each do |c|
58
+ if c[:auto_increment]
59
+ c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
60
+ unless c[:create_sequence] == false
61
+ drop_seq_statement = drop_sequence_sql(c[:sequence_name])
62
+ statements << create_sequence_sql(c[:sequence_name], c)
63
+ end
64
+ unless c[:create_trigger] == false
65
+ c[:trigger_name] ||= "BI_#{name}_#{c[:name]}"
66
+ trigger_definition = <<-end_sql
67
+ BEGIN
68
+ IF :NEW.#{quote_identifier(c[:name])} IS NULL THEN
69
+ SELECT #{c[:sequence_name]}.nextval INTO :NEW.#{quote_identifier(c[:name])} FROM dual;
70
+ END IF;
71
+ END;
72
+ end_sql
73
+ statements << create_trigger_sql(name, c[:trigger_name], trigger_definition, {:events => [:insert]})
74
+ end
75
+ end
76
+ end
77
+ [drop_seq_statement, statements]
78
+ end
79
+
80
+ def create_trigger_sql(table, name, definition, opts={})
81
+ events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
82
+ sql = <<-end_sql
83
+ CREATE#{' OR REPLACE' if opts[:replace]} TRIGGER #{quote_identifier(name)}
84
+ #{opts[:after] ? 'AFTER' : 'BEFORE'} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}
85
+ REFERENCING NEW AS NEW FOR EACH ROW
86
+ #{definition}
87
+ end_sql
88
+ sql
89
+ end
90
+
91
+ def drop_sequence_sql(name)
92
+ "DROP SEQUENCE #{quote_identifier(name)}"
93
+ end
94
+ end
95
+
96
+ module DatasetMethods
97
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct columns from join where group having compounds order limit')
98
+
99
+ # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
100
+ def except(dataset, opts={})
101
+ opts = {:all=>opts} unless opts.is_a?(Hash)
102
+ raise(Sequel::Error, "EXCEPT ALL not supported") if opts[:all]
103
+ compound_clone(:minus, dataset, opts)
104
+ end
105
+
106
+ def empty?
107
+ db[:dual].where(exists).get(1) == nil
108
+ end
109
+
110
+ # If this dataset is associated with a sequence, return the most recently
111
+ # inserted sequence value.
112
+ def insert(*args)
113
+ r = super
114
+ if s = opts[:sequence]
115
+ with_sql("SELECT #{literal(s)}.currval FROM dual").single_value.to_i
116
+ else
117
+ r
118
+ end
119
+ end
120
+
121
+ # Oracle requires SQL standard datetimes
122
+ def requires_sql_standard_datetimes?
123
+ true
124
+ end
125
+
126
+ # Create a copy of this dataset associated to the given sequence name,
127
+ # which will be used when calling insert to find the most recently
128
+ # inserted value for the sequence.
129
+ def sequence(s)
130
+ clone(:sequence=>s)
131
+ end
132
+
133
+ # Oracle does not support INTERSECT ALL or EXCEPT ALL
134
+ def supports_intersect_except_all?
135
+ false
136
+ end
137
+
138
+ # Oracle supports timezones in literal timestamps.
139
+ def supports_timestamp_timezones?
140
+ true
141
+ end
142
+
143
+ # Oracle supports window functions
144
+ def supports_window_functions?
145
+ true
146
+ end
147
+
148
+ private
149
+
150
+ # Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
151
+ # the use of AS anywhere, so this disables it in all cases.
152
+ def as_sql(expression, aliaz)
153
+ "#{expression} #{quote_identifier(aliaz)}"
154
+ end
155
+
156
+ # The strftime format to use when literalizing the time.
157
+ def default_timestamp_format
158
+ "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
159
+ end
160
+
161
+ # Use a colon for the timestamp offset, since Oracle appears to require it.
162
+ def format_timestamp_offset(hour, minute)
163
+ sprintf("%+03i:%02i", hour, minute)
164
+ end
165
+
166
+ # Oracle uses the SQL standard of only doubling ' inside strings.
167
+ def literal_string(v)
168
+ "'#{v.gsub("'", "''")}'"
169
+ end
170
+
171
+ def select_clause_methods
172
+ SELECT_CLAUSE_METHODS
173
+ end
174
+
175
+ # Modify the SQL to add the list of tables to select FROM
176
+ # Oracle doesn't support select without FROM clause
177
+ # so add the dummy DUAL table if the dataset doesn't select
178
+ # from a table.
179
+ def select_from_sql(sql)
180
+ sql << " FROM #{source_list(@opts[:from] || ['DUAL'])}"
181
+ end
182
+
183
+ # Oracle requires a subselect to do limit and offset
184
+ def select_limit_sql(sql)
185
+ if limit = @opts[:limit]
186
+ if (offset = @opts[:offset]) && (offset > 0)
187
+ sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
188
+ else
189
+ sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,830 @@
1
+ module Sequel
2
+ Dataset::NON_SQL_OPTIONS << :disable_insert_returning
3
+
4
+ # Top level module for holding all PostgreSQL-related modules and classes
5
+ # for Sequel. There are a few module level accessors that are added via
6
+ # metaprogramming. These are:
7
+ # * client_min_messages (only available when using the native adapter) -
8
+ # Change the minimum level of messages that PostgreSQL will send to the
9
+ # the client. The PostgreSQL default is NOTICE, the Sequel default is
10
+ # WARNING. Set to nil to not change the server default.
11
+ # * force_standard_strings - Set to false to not force the use of
12
+ # standard strings
13
+ # * use_iso_date_format (only available when using the native adapter) -
14
+ # Set to false to not change the date format to
15
+ # ISO. This disables one of Sequel's optimizations.
16
+ #
17
+ # Changes in these settings only affect future connections. To make
18
+ # sure that they are applied, they should generally be called right
19
+ # after the Database object is instantiated and before a connection
20
+ # is actually made. For example, to use whatever the server defaults are:
21
+ #
22
+ # DB = Sequel.postgres(...)
23
+ # Sequel::Postgres.client_min_messages = nil
24
+ # Sequel::Postgres.force_standard_strings = false
25
+ # Sequel::Postgres.use_iso_date_format = false
26
+ # # A connection to the server is not made until here
27
+ # DB[:t].all
28
+ #
29
+ # The reason they can't be done earlier is that the Sequel::Postgres
30
+ # module is not loaded until a Database object which uses PostgreSQL
31
+ # is created.
32
+ module Postgres
33
+ # Array of exceptions that need to be converted. JDBC
34
+ # uses NativeExceptions, the native adapter uses PGError.
35
+ CONVERTED_EXCEPTIONS = []
36
+
37
+ @client_min_messages = :warning
38
+ @force_standard_strings = true
39
+
40
+ class << self
41
+ # By default, Sequel sets the minimum level of log messages sent to the client
42
+ # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
43
+ # of mostly useless messages when running migrations, such as a couple of lines
44
+ # for every serial primary key field.
45
+ attr_accessor :client_min_messages
46
+
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.
52
+ attr_accessor :force_standard_strings
53
+ end
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
67
+ FROM pg_class t
68
+ JOIN pg_namespace name ON (t.relnamespace = name.oid)
69
+ JOIN pg_attribute attr ON (t.oid = attrelid)
70
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
71
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
72
+ WHERE cons.contype = 'p'
73
+ AND def.adsrc ~* 'nextval'
74
+ #{"AND name.nspname = '#{schema}'" if schema}
75
+ AND t.relname = '#{table}'
76
+ end_sql
77
+ end
78
+ SELECT_PK = proc do |schema, table| <<-end_sql
79
+ SELECT pg_attribute.attname
80
+ FROM pg_class, pg_attribute, pg_index, pg_namespace
81
+ WHERE pg_class.oid = pg_attribute.attrelid
82
+ AND pg_class.relnamespace = pg_namespace.oid
83
+ AND pg_class.oid = pg_index.indrelid
84
+ AND pg_index.indkey[0] = pg_attribute.attnum
85
+ AND pg_index.indisprimary = 't'
86
+ #{"AND pg_namespace.nspname = '#{schema}'" if schema}
87
+ AND pg_class.relname = '#{table}'
88
+ end_sql
89
+ end
90
+ SELECT_SERIAL_SEQUENCE = proc do |schema, table| <<-end_sql
91
+ SELECT '"' || name.nspname || '".' || seq.relname || ''
92
+ FROM pg_class seq, pg_attribute attr, pg_depend dep,
93
+ pg_namespace name, pg_constraint cons
94
+ WHERE seq.oid = dep.objid
95
+ AND seq.relnamespace = name.oid
96
+ AND seq.relkind = 'S'
97
+ AND attr.attrelid = dep.refobjid
98
+ AND attr.attnum = dep.refobjsubid
99
+ AND attr.attrelid = cons.conrelid
100
+ AND attr.attnum = cons.conkey[1]
101
+ AND cons.contype = 'p'
102
+ #{"AND name.nspname = '#{schema}'" if schema}
103
+ AND seq.relname = '#{table}'
104
+ 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
+ PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
161
+ RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
162
+ SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
163
+
164
+ # Creates the function in the database. Arguments:
165
+ # * name : name of the function to create
166
+ # * definition : string definition of the function, or object file for a dynamically loaded C function.
167
+ # * opts : options hash:
168
+ # * :args : function arguments, can be either a symbol or string specifying a type or an array of 1-3 elements:
169
+ # * element 1 : argument data type
170
+ # * element 2 : argument name
171
+ # * element 3 : argument mode (e.g. in, out, inout)
172
+ # * :behavior : Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
173
+ # * :cost : The estimated cost of the function, used by the query planner.
174
+ # * :language : The language the function uses. SQL is the default.
175
+ # * :link_symbol : For a dynamically loaded see function, the function's link symbol if different from the definition argument.
176
+ # * :returns : The data type returned by the function. If you are using OUT or INOUT argument modes, this is ignored.
177
+ # Otherwise, if this is not specified, void is used by default to specify the function is not supposed to return a value.
178
+ # * :rows : The estimated number of rows the function will return. Only use if the function returns SETOF something.
179
+ # * :security_definer : Makes the privileges of the function the same as the privileges of the user who defined the function instead of
180
+ # the privileges of the user who runs the function. There are security implications when doing this, see the PostgreSQL documentation.
181
+ # * :set : Configuration variables to set while the function is being run, can be a hash or an array of two pairs. search_path is
182
+ # often used here if :security_definer is used.
183
+ # * :strict : Makes the function return NULL when any argument is NULL.
184
+ def create_function(name, definition, opts={})
185
+ self << create_function_sql(name, definition, opts)
186
+ end
187
+
188
+ # Create the procedural language in the database. Arguments:
189
+ # * name : Name of the procedural language (e.g. plpgsql)
190
+ # * opts : options hash:
191
+ # * :handler : The name of a previously registered function used as a call handler for this language.
192
+ # * :trusted : Marks the language being created as trusted, allowing unprivileged users to create functions using this language.
193
+ # * :validator : The name of previously registered function used as a validator of functions defined in this language.
194
+ def create_language(name, opts={})
195
+ self << create_language_sql(name, opts)
196
+ end
197
+
198
+ # Create a trigger in the database. Arguments:
199
+ # * table : the table on which this trigger operates
200
+ # * name : the name of this trigger
201
+ # * function : the function to call for this trigger, which should return type trigger.
202
+ # * opts : options hash:
203
+ # * :after : Calls the trigger after execution instead of before.
204
+ # * :args : An argument or array of arguments to pass to the function.
205
+ # * :each_row : Calls the trigger for each row instead of for each statement.
206
+ # * :events : Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
207
+ # the trigger is called for insert, update, or delete.
208
+ def create_trigger(table, name, function, opts={})
209
+ self << create_trigger_sql(table, name, function, opts)
210
+ end
211
+
212
+ # PostgreSQL uses the :postgres database type.
213
+ def database_type
214
+ :postgres
215
+ end
216
+
217
+ # Drops the function from the database. Arguments:
218
+ # * name : name of the function to drop
219
+ # * opts : options hash:
220
+ # * :args : The arguments for the function. See create_function_sql.
221
+ # * :cascade : Drop other objects depending on this function.
222
+ # * :if_exists : Don't raise an error if the function doesn't exist.
223
+ def drop_function(name, opts={})
224
+ self << drop_function_sql(name, opts)
225
+ end
226
+
227
+ # Drops a procedural language from the database. Arguments:
228
+ # * name : name of the procedural language to drop
229
+ # * opts : options hash:
230
+ # * :cascade : Drop other objects depending on this function.
231
+ # * :if_exists : Don't raise an error if the function doesn't exist.
232
+ def drop_language(name, opts={})
233
+ self << drop_language_sql(name, opts)
234
+ end
235
+
236
+ # Remove the cached entries for primary keys and sequences when dropping a table.
237
+ def drop_table(*names)
238
+ names.each do |name|
239
+ name = quote_schema_table(name)
240
+ @primary_keys.delete(name)
241
+ @primary_key_sequences.delete(name)
242
+ end
243
+ super
244
+ end
245
+
246
+ # Drops a trigger from the database. Arguments:
247
+ # * table : table from which to drop the trigger
248
+ # * name : name of the trigger to drop
249
+ # * opts : options hash:
250
+ # * :cascade : Drop other objects depending on this function.
251
+ # * :if_exists : Don't raise an error if the function doesn't exist.
252
+ def drop_trigger(table, name, opts={})
253
+ self << drop_trigger_sql(table, name, opts)
254
+ end
255
+
256
+ # Return a hash containing index information. Hash keys are index name symbols.
257
+ # Values are subhashes with two keys, :columns and :unique. The value of :columns
258
+ # is an array of symbols of column names. The value of :unique is true or false
259
+ # depending on if the index is unique.
260
+ #
261
+ # This will not output any partial indexes, indexes that aren't valid or ready,
262
+ # or indexes that contain anything other than simple column references.
263
+ def indexes(table)
264
+ m = output_identifier_meth
265
+ im = input_identifier_meth
266
+ schema, table = schema_and_table(table)
267
+ range = 0...32
268
+ attnums = server_version >= 80100 ? SQL::Function.new(:ANY, :ind__indkey) : range.map{|x| SQL::Subscript.new(:ind__indkey, [x])}
269
+ ds = metadata_dataset.
270
+ from(:pg_class___tab).
271
+ join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
272
+ join(:pg_class___indc, :oid=>:indexrelid).
273
+ join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
274
+ filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
275
+ order(:indc__relname, range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
276
+ select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
277
+
278
+ ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
279
+ ds.filter!(:indisvalid=>true) if server_version >= 80200
280
+ ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
281
+
282
+ indexes = {}
283
+ ds.each do |r|
284
+ i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique]}
285
+ i[:columns] << m.call(r[:column])
286
+ end
287
+ indexes
288
+ end
289
+
290
+ # Dataset containing all current database locks
291
+ def locks
292
+ dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
293
+ end
294
+
295
+ # Return primary key for the given table.
296
+ def primary_key(table, opts={})
297
+ quoted_table = quote_schema_table(table)
298
+ return @primary_keys[quoted_table] if @primary_keys.include?(quoted_table)
299
+ @primary_keys[quoted_table] = if conn = opts[:conn]
300
+ conn.primary_key(*schema_and_table(table))
301
+ else
302
+ synchronize(opts[:server]){|con| con.primary_key(*schema_and_table(table))}
303
+ end
304
+ end
305
+
306
+ # Return the sequence providing the default for the primary key for the given table.
307
+ def primary_key_sequence(table, opts={})
308
+ quoted_table = quote_schema_table(table)
309
+ return @primary_key_sequences[quoted_table] if @primary_key_sequences.include?(quoted_table)
310
+ @primary_key_sequences[quoted_table] = if conn = opts[:conn]
311
+ conn.sequence(*schema_and_table(table))
312
+ else
313
+ synchronize(opts[:server]){|con| con.sequence(*schema_and_table(table))}
314
+ end
315
+ end
316
+
317
+ # Reset the primary key sequence for the given table, baseing it on the
318
+ # maximum current value of the table's primary key.
319
+ def reset_primary_key_sequence(table)
320
+ pk = SQL::Identifier.new(primary_key(table))
321
+ return unless seq = primary_key_sequence(table)
322
+ db = self
323
+ seq_ds = db.from(seq.lit)
324
+ get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
325
+ end
326
+
327
+ # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
328
+ # managing incrementing primary keys.
329
+ def serial_primary_key_options
330
+ {:primary_key => true, :serial => true, :type=>Integer}
331
+ end
332
+
333
+ # The version of the PostgreSQL server, used for determining capability.
334
+ def server_version(server=nil)
335
+ return @server_version if @server_version
336
+ @server_version = synchronize(server) do |conn|
337
+ (conn.server_version rescue nil) if conn.respond_to?(:server_version)
338
+ end
339
+ unless @server_version
340
+ m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
341
+ @server_version = (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
342
+ end
343
+ @server_version
344
+ end
345
+
346
+ # PostgreSQL supports savepoints
347
+ def supports_savepoints?
348
+ true
349
+ end
350
+
351
+ # Whether the given table exists in the database
352
+ #
353
+ # Options:
354
+ # * :schema - The schema to search (default_schema by default)
355
+ # * :server - The server to use
356
+ def table_exists?(table, opts={})
357
+ im = input_identifier_meth
358
+ schema, table = schema_and_table(table)
359
+ opts[:schema] ||= schema
360
+ tables(opts){|ds| !ds.first(:relname=>im.call(table)).nil?}
361
+ end
362
+
363
+ # Array of symbols specifying table names in the current database.
364
+ # The dataset used is yielded to the block if one is provided,
365
+ # otherwise, an array of symbols of table names is returned.
366
+ #
367
+ # Options:
368
+ # * :schema - The schema to search (default_schema by default)
369
+ # * :server - The server to use
370
+ def tables(opts={})
371
+ 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, :nspname=>(opts[:schema]||default_schema||'public').to_s)
372
+ m = output_identifier_meth
373
+ block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
374
+ end
375
+
376
+ private
377
+
378
+ # SQL statement to create database function.
379
+ def create_function_sql(name, definition, opts={})
380
+ args = opts[:args]
381
+ if !opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 and %w'OUT INOUT'.include?(a[2].to_s)}
382
+ returns = opts[:returns] || 'void'
383
+ end
384
+ language = opts[:language] || 'SQL'
385
+ <<-END
386
+ CREATE#{' OR REPLACE' if opts[:replace]} FUNCTION #{name}#{sql_function_args(args)}
387
+ #{"RETURNS #{returns}" if returns}
388
+ LANGUAGE #{language}
389
+ #{opts[:behavior].to_s.upcase if opts[:behavior]}
390
+ #{'STRICT' if opts[:strict]}
391
+ #{'SECURITY DEFINER' if opts[:security_definer]}
392
+ #{"COST #{opts[:cost]}" if opts[:cost]}
393
+ #{"ROWS #{opts[:rows]}" if opts[:rows]}
394
+ #{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
395
+ AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
396
+ END
397
+ end
398
+
399
+ # SQL for creating a procedural language.
400
+ def create_language_sql(name, opts={})
401
+ "CREATE#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
402
+ end
403
+
404
+ # SQL for creating a database trigger.
405
+ def create_trigger_sql(table, name, function, opts={})
406
+ events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
407
+ whence = opts[:after] ? 'AFTER' : 'BEFORE'
408
+ "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(', ')})"
409
+ end
410
+
411
+ # The errors that the main adapters can raise, depends on the adapter being used
412
+ def database_error_classes
413
+ CONVERTED_EXCEPTIONS
414
+ end
415
+
416
+ # SQL for dropping a function from the database.
417
+ def drop_function_sql(name, opts={})
418
+ "DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
419
+ end
420
+
421
+ # SQL for dropping a procedural language from the database.
422
+ def drop_language_sql(name, opts={})
423
+ "DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
424
+ end
425
+
426
+ # Always CASCADE the table drop
427
+ def drop_table_sql(name)
428
+ "DROP TABLE #{quote_schema_table(name)} CASCADE"
429
+ end
430
+
431
+ # SQL for dropping a trigger from the database.
432
+ def drop_trigger_sql(table, name, opts={})
433
+ "DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
434
+ end
435
+
436
+ # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
437
+ def identifier_input_method_default
438
+ nil
439
+ end
440
+
441
+ # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
442
+ def identifier_output_method_default
443
+ nil
444
+ end
445
+
446
+ # PostgreSQL specific index SQL.
447
+ def index_definition_sql(table_name, index)
448
+ cols = index[:columns]
449
+ index_name = index[:name] || default_index_name(table_name, cols)
450
+ expr = if o = index[:opclass]
451
+ "(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
452
+ else
453
+ literal(Array(cols))
454
+ end
455
+ unique = "UNIQUE " if index[:unique]
456
+ index_type = index[:type]
457
+ filter = index[:where] || index[:filter]
458
+ filter = " WHERE #{filter_expr(filter)}" if filter
459
+ case index_type
460
+ when :full_text
461
+ expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{dataset.send(:full_text_string_join, cols)}))"
462
+ index_type = :gin
463
+ when :spatial
464
+ index_type = :gist
465
+ end
466
+ "CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
467
+ end
468
+
469
+ # The result of the insert for the given table and values. If values
470
+ # is an array, assume the first column is the primary key and return
471
+ # that. If values is a hash, lookup the primary key for the table. If
472
+ # the primary key is present in the hash, return its value. Otherwise,
473
+ # look up the sequence for the table's primary key. If one exists,
474
+ # return the last value the of the sequence for the connection.
475
+ def insert_result(conn, table, values)
476
+ case values
477
+ when Hash
478
+ return nil unless pk = primary_key(table, :conn=>conn)
479
+ if pk and pkv = values[pk.to_sym]
480
+ pkv
481
+ else
482
+ begin
483
+ if seq = primary_key_sequence(table, :conn=>conn)
484
+ conn.last_insert_id(seq)
485
+ end
486
+ rescue Exception => e
487
+ raise_error(e, :classes=>CONVERTED_EXCEPTIONS) unless RE_CURRVAL_ERROR.match(e.message)
488
+ end
489
+ end
490
+ when Array
491
+ values.first
492
+ else
493
+ nil
494
+ end
495
+ end
496
+
497
+ # Don't log, since logging is done by the underlying connection.
498
+ def log_connection_execute(conn, sql)
499
+ conn.execute(sql)
500
+ end
501
+
502
+ # Use a dollar sign instead of question mark for the argument
503
+ # placeholder.
504
+ def prepared_arg_placeholder
505
+ PREPARED_ARG_PLACEHOLDER
506
+ end
507
+
508
+ # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
509
+ # a rename table operation, so speciying a new schema in new_name will not have an effect.
510
+ def rename_table_sql(name, new_name)
511
+ "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
512
+ end
513
+
514
+ # PostgreSQL's autoincrementing primary keys are of type integer or bigint
515
+ # using a nextval function call as a default.
516
+ def schema_autoincrementing_primary_key?(schema)
517
+ super and schema[:db_type] =~ /\A(?:integer|bigint)\z/io and schema[:default]=~/\Anextval/io
518
+ end
519
+
520
+ # The dataset used for parsing table schemas, using the pg_* system catalogs.
521
+ def schema_parse_table(table_name, opts)
522
+ m = output_identifier_meth
523
+ m2 = input_identifier_meth
524
+ ds = metadata_dataset.select(:pg_attribute__attname___name,
525
+ SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
526
+ SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
527
+ SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
528
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)).
529
+ from(:pg_class).
530
+ join(:pg_attribute, :attrelid=>:oid).
531
+ join(:pg_type, :oid=>:atttypid).
532
+ left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
533
+ left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
534
+ filter(:pg_attribute__attisdropped=>false).
535
+ filter{|o| o.pg_attribute__attnum > 0}.
536
+ filter(:pg_class__relname=>m2.call(table_name)).
537
+ order(:pg_attribute__attnum)
538
+ ds.join!(:pg_namespace, :oid=>:pg_class__relnamespace, :nspname=>(opts[:schema] || default_schema).to_s) if opts[:schema] || default_schema
539
+ ds.map do |row|
540
+ row[:default] = nil if blank_object?(row[:default])
541
+ row[:type] = schema_column_type(row[:db_type])
542
+ [m.call(row.delete(:name)), row]
543
+ end
544
+ end
545
+
546
+ # Turns an array of argument specifiers into an SQL fragment used for function arguments. See create_function_sql.
547
+ def sql_function_args(args)
548
+ "(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
549
+ end
550
+
551
+ # Handle bigserial type if :serial option is present
552
+ def type_literal_generic_bignum(column)
553
+ column[:serial] ? :bigserial : super
554
+ end
555
+
556
+ # PostgreSQL uses the bytea data type for blobs
557
+ def type_literal_generic_file(column)
558
+ :bytea
559
+ end
560
+
561
+ # Handle serial type if :serial option is present
562
+ def type_literal_generic_integer(column)
563
+ column[:serial] ? :serial : super
564
+ end
565
+
566
+ # PostgreSQL prefers the text datatype. If a fixed size is requested,
567
+ # the char type is used. If the text type is specifically
568
+ # disallowed or there is a size specified, use the varchar type.
569
+ # Otherwise use the type type.
570
+ def type_literal_generic_string(column)
571
+ if column[:fixed]
572
+ "char(#{column[:size]||255})"
573
+ elsif column[:text] == false or column[:size]
574
+ "varchar(#{column[:size]||255})"
575
+ else
576
+ :text
577
+ end
578
+ end
579
+ end
580
+
581
+ # Instance methods for datasets that connect to a PostgreSQL database.
582
+ module DatasetMethods
583
+ ACCESS_SHARE = 'ACCESS SHARE'.freeze
584
+ ACCESS_EXCLUSIVE = 'ACCESS EXCLUSIVE'.freeze
585
+ BOOL_FALSE = 'false'.freeze
586
+ BOOL_TRUE = 'true'.freeze
587
+ COMMA_SEPARATOR = ', '.freeze
588
+ DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
589
+ EXCLUSIVE = 'EXCLUSIVE'.freeze
590
+ EXPLAIN = 'EXPLAIN '.freeze
591
+ EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
592
+ FOR_SHARE = ' FOR SHARE'.freeze
593
+ LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
594
+ NULL = LiteralString.new('NULL').freeze
595
+ PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
596
+ QUERY_PLAN = 'QUERY PLAN'.to_sym
597
+ ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
598
+ ROW_SHARE = 'ROW SHARE'.freeze
599
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
600
+ SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with distinct columns from join where group having window compounds order limit lock')
601
+ SHARE = 'SHARE'.freeze
602
+ SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
603
+ SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
604
+ SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
605
+ UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
606
+
607
+ # Shared methods for prepared statements when used with PostgreSQL databases.
608
+ module PreparedStatementMethods
609
+ # Override insert action to use RETURNING if the server supports it.
610
+ def prepared_sql
611
+ return @prepared_sql if @prepared_sql
612
+ super
613
+ if @prepared_type == :insert and !@opts[:disable_insert_returning] and server_version >= 80200
614
+ @prepared_sql = insert_returning_pk_sql(*@prepared_modify_values)
615
+ meta_def(:insert_returning_pk_sql){|*args| prepared_sql}
616
+ end
617
+ @prepared_sql
618
+ end
619
+ end
620
+
621
+ # Add the disable_insert_returning! mutation method
622
+ def self.extended(obj)
623
+ obj.def_mutation_method(:disable_insert_returning)
624
+ end
625
+
626
+ # Add the disable_insert_returning! mutation method
627
+ def self.included(mod)
628
+ mod.def_mutation_method(:disable_insert_returning)
629
+ end
630
+
631
+ # Return the results of an ANALYZE query as a string
632
+ def analyze
633
+ explain(:analyze=>true)
634
+ end
635
+
636
+ # Disable the use of INSERT RETURNING, even if the server supports it
637
+ def disable_insert_returning
638
+ clone(:disable_insert_returning=>true)
639
+ end
640
+
641
+ # Return the results of an EXPLAIN query as a string
642
+ def explain(opts={})
643
+ with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join("\r\n")
644
+ end
645
+
646
+ # Return a cloned dataset which will use FOR SHARE to lock returned rows.
647
+ def for_share
648
+ lock_style(:share)
649
+ end
650
+
651
+ # PostgreSQL specific full text search syntax, using tsearch2 (included
652
+ # in 8.3 by default, and available for earlier versions as an add-on).
653
+ def full_text_search(cols, terms, opts = {})
654
+ lang = opts[:language] || 'simple'
655
+ filter("to_tsvector(#{literal(lang)}, #{full_text_string_join(cols)}) @@ to_tsquery(#{literal(lang)}, #{literal(Array(terms).join(' | '))})")
656
+ end
657
+
658
+ # Insert given values into the database.
659
+ def insert(*values)
660
+ if @opts[:sql]
661
+ execute_insert(insert_sql(*values))
662
+ elsif @opts[:disable_insert_returning] || server_version < 80200
663
+ execute_insert(insert_sql(*values), :table=>opts[:from].first, :values=>values.size == 1 ? values.first : values)
664
+ else
665
+ clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
666
+ end
667
+ end
668
+
669
+ # Use the RETURNING clause to return the columns listed in returning.
670
+ def insert_returning_sql(returning, *values)
671
+ "#{insert_sql(*values)} RETURNING #{column_list(Array(returning))}"
672
+ end
673
+
674
+ # Insert a record returning the record inserted
675
+ def insert_select(*values)
676
+ return if opts[:disable_insert_returning] || server_version < 80200
677
+ naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
678
+ end
679
+
680
+ # Locks all tables in the dataset's FROM clause (but not in JOINs) with
681
+ # the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
682
+ # a new transaction, locks the table, and yields. If a block is not given
683
+ # just locks the tables. Note that PostgreSQL will probably raise an error
684
+ # if you lock the table outside of an existing transaction. Returns nil.
685
+ def lock(mode, opts={})
686
+ if block_given? # perform locking inside a transaction and yield to block
687
+ @db.transaction(opts){lock(mode, opts); yield}
688
+ else
689
+ @db.execute(LOCK % [source_list(@opts[:from]), mode], opts) # lock without a transaction
690
+ end
691
+ nil
692
+ end
693
+
694
+ # For PostgreSQL version > 8.2, allow inserting multiple rows at once.
695
+ def multi_insert_sql(columns, values)
696
+ return super if server_version < 80200
697
+
698
+ # postgresql 8.2 introduces support for multi-row insert
699
+ [insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
700
+ end
701
+
702
+ # DISTINCT ON is a PostgreSQL extension
703
+ def supports_distinct_on?
704
+ true
705
+ end
706
+
707
+ # PostgreSQL supports modifying joined datasets
708
+ def supports_modifying_joins?
709
+ true
710
+ end
711
+
712
+ # PostgreSQL supports timezones in literal timestamps
713
+ def supports_timestamp_timezones?
714
+ true
715
+ end
716
+
717
+ # PostgreSQL 8.4+ supports window functions
718
+ def supports_window_functions?
719
+ server_version >= 80400
720
+ end
721
+
722
+ # Return a clone of the dataset with an addition named window that can be referenced in window functions.
723
+ def window(name, opts)
724
+ clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
725
+ end
726
+
727
+ private
728
+
729
+ # PostgreSQL allows deleting from joined datasets
730
+ def delete_clause_methods
731
+ DELETE_CLAUSE_METHODS
732
+ end
733
+
734
+ # Only include the primary table in the main delete clause
735
+ def delete_from_sql(sql)
736
+ sql << " FROM #{source_list(@opts[:from][0..0])}"
737
+ end
738
+
739
+ # Use USING to specify additional tables in a delete query
740
+ def delete_using_sql(sql)
741
+ join_from_sql(:USING, sql)
742
+ end
743
+
744
+ # Use the RETURNING clause to return the primary key of the inserted record, if it exists
745
+ def insert_returning_pk_sql(*values)
746
+ pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
747
+ insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
748
+ end
749
+
750
+ # For multiple table support, PostgreSQL requires at least
751
+ # two from tables, with joins allowed.
752
+ def join_from_sql(type, sql)
753
+ if(from = @opts[:from][1..-1]).empty?
754
+ raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
755
+ else
756
+ sql << " #{type} #{source_list(from)}"
757
+ select_join_sql(sql)
758
+ end
759
+ end
760
+
761
+ # Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
762
+ def literal_blob(v)
763
+ "'#{v.gsub(/[\000-\037\047\134\177-\377]/){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"}}'"
764
+ end
765
+
766
+ # PostgreSQL uses FALSE for false values
767
+ def literal_false
768
+ BOOL_FALSE
769
+ end
770
+
771
+ # Assume that SQL standard quoting is on, per Sequel's defaults
772
+ def literal_string(v)
773
+ "'#{v.gsub("'", "''")}'"
774
+ end
775
+
776
+ # PostgreSQL uses FALSE for false values
777
+ def literal_true
778
+ BOOL_TRUE
779
+ end
780
+
781
+ # The order of clauses in the SELECT SQL statement
782
+ def select_clause_methods
783
+ server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
784
+ end
785
+
786
+ # Support FOR SHARE locking when using the :share lock style.
787
+ def select_lock_sql(sql)
788
+ @opts[:lock] == :share ? (sql << FOR_SHARE) : super
789
+ end
790
+
791
+ # SQL fragment for named window specifications
792
+ def select_window_sql(sql)
793
+ sql << " WINDOW #{@opts[:window].map{|name, window| "#{literal(name)} AS #{literal(window)}"}.join(', ')}" if @opts[:window]
794
+ end
795
+
796
+ # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
797
+ def select_with_sql_base
798
+ opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
799
+ end
800
+
801
+ # The version of the database server
802
+ def server_version
803
+ db.server_version(@opts[:server])
804
+ end
805
+
806
+ # Concatenate the expressions with a space in between
807
+ def full_text_string_join(cols)
808
+ cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
809
+ cols = cols.zip([' '] * cols.length).flatten
810
+ cols.pop
811
+ literal(SQL::StringExpression.new(:'||', *cols))
812
+ end
813
+
814
+ # PostgreSQL splits the main table from the joined tables
815
+ def update_clause_methods
816
+ UPDATE_CLAUSE_METHODS
817
+ end
818
+
819
+ # Use FROM to specify additional tables in an update query
820
+ def update_from_sql(sql)
821
+ join_from_sql(:FROM, sql)
822
+ end
823
+
824
+ # Only include the primary table in the main update clause
825
+ def update_table_sql(sql)
826
+ sql << " #{source_list(@opts[:from][0..0])}"
827
+ end
828
+ end
829
+ end
830
+ end