sequel 3.21.0 → 3.32.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 (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -13,6 +13,18 @@ module Sequel
13
13
  # ORA-03114: not connected to ORACLE
14
14
  CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
15
15
 
16
+ ORACLE_TYPES = {:blob=>lambda{|b| Sequel::SQL::Blob.new(b.read)}}
17
+
18
+ # Hash of conversion procs for this database.
19
+ attr_reader :conversion_procs
20
+
21
+ def initialize(opts={})
22
+ super
23
+ @autosequence = opts[:autosequence]
24
+ @primary_key_sequences = {}
25
+ @conversion_procs = ORACLE_TYPES.dup
26
+ end
27
+
16
28
  def connect(server)
17
29
  opts = server_opts(server)
18
30
  if opts[:database]
@@ -22,61 +34,144 @@ module Sequel
22
34
  dbname = opts[:host]
23
35
  end
24
36
  conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
37
+ conn.prefetch_rows = typecast_value_integer(opts[:prefetch_rows]) if opts[:prefetch_rows]
25
38
  conn.autocommit = true
26
39
  conn.non_blocking = true
40
+
41
+ # The ruby-oci8 gem which retrieves oracle columns with a type of
42
+ # DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE is complex based on the
43
+ # ruby version (1.9.2 or later) and Oracle version (9 or later)
44
+ # In the now standard case of 1.9.2 and Oracle 9 or later, the timezone
45
+ # is determined by the Oracle session timezone. Thus if the user
46
+ # requests Sequel provide UTC timezone to the application,
47
+ # we need to alter the session timezone to be UTC
48
+ if Sequel.application_timezone == :utc
49
+ conn.exec("ALTER SESSION SET TIME_ZONE='-00:00'")
50
+ end
51
+
52
+ class << conn
53
+ attr_reader :prepared_statements
54
+ end
55
+ conn.instance_variable_set(:@prepared_statements, {})
56
+
27
57
  conn
28
58
  end
29
-
30
- def dataset(opts = nil)
31
- Oracle::Dataset.new(self, opts)
59
+
60
+ def execute(sql, opts={}, &block)
61
+ _execute(nil, sql, opts, &block)
32
62
  end
63
+ alias do execute
33
64
 
34
- def schema_parse_table(table, opts={})
35
- ds = dataset
36
- ds.identifier_output_method = :downcase
37
- schema_and_table = "#{"#{quote_identifier(opts[:schema])}." if opts[:schema]}#{quote_identifier(table)}"
38
- table_schema = []
39
- metadata = transaction(opts){|conn| conn.describe_table(schema_and_table)}
40
- metadata.columns.each do |column|
41
- table_schema << [
42
- column.name.downcase.to_sym,
43
- {
44
- :type => column.data_type,
45
- :db_type => column.type_string.split(' ')[0],
46
- :type_string => column.type_string,
47
- :charset_form => column.charset_form,
48
- :char_used => column.char_used?,
49
- :char_size => column.char_size,
50
- :data_size => column.data_size,
51
- :precision => column.precision,
52
- :scale => column.scale,
53
- :fsprecision => column.fsprecision,
54
- :lfprecision => column.lfprecision,
55
- :allow_null => column.nullable?
56
- }
57
- ]
58
- end
59
- table_schema
65
+ def execute_insert(sql, opts={})
66
+ _execute(:insert, sql, opts)
60
67
  end
61
68
 
62
- def execute(sql, opts={})
69
+ private
70
+
71
+ def _execute(type, sql, opts={}, &block)
63
72
  synchronize(opts[:server]) do |conn|
64
73
  begin
65
- r = log_yield(sql){conn.exec(sql)}
66
- yield(r) if block_given?
67
- r
68
- rescue OCIException => e
69
- raise_error(e, :disconnect=>CONNECTION_ERROR_CODES.include?(e.code))
74
+ return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
75
+ if args = opts[:arguments]
76
+ r = conn.parse(sql)
77
+ args = cursor_bind_params(conn, r, args)
78
+ nr = log_yield(sql, args){r.exec}
79
+ r = nr unless block_given?
80
+ else
81
+ r = log_yield(sql){conn.exec(sql)}
82
+ end
83
+ if block_given?
84
+ begin
85
+ yield(r)
86
+ ensure
87
+ r.close
88
+ end
89
+ elsif type == :insert
90
+ last_insert_id(conn, opts)
91
+ else
92
+ r
93
+ end
94
+ rescue OCIException, RuntimeError => e
95
+ # ruby-oci8 is naughty and raises strings in some places
96
+ raise_error(e)
97
+ end
98
+ end
99
+ end
100
+
101
+ PS_TYPES = {'string'.freeze=>String, 'integer'.freeze=>Integer, 'float'.freeze=>Float,
102
+ 'decimal'.freeze=>Float, 'date'.freeze=>Time, 'datetime'.freeze=>Time,
103
+ 'time'.freeze=>Time, 'boolean'.freeze=>String, 'blob'.freeze=>OCI8::BLOB}
104
+ def cursor_bind_params(conn, cursor, args)
105
+ cursor
106
+ i = 0
107
+ args.map do |arg, type|
108
+ i += 1
109
+ case arg
110
+ when true
111
+ arg = 'Y'
112
+ when false
113
+ arg = 'N'
114
+ when BigDecimal
115
+ arg = arg.to_f
116
+ when ::Sequel::SQL::Blob
117
+ raise Error, "Sequel's oracle adapter does not currently support using a blob in a bound variable"
118
+ end
119
+ if t = PS_TYPES[type]
120
+ cursor.bind_param(i, arg, t)
121
+ else
122
+ cursor.bind_param(i, arg, arg.class)
123
+ end
124
+ arg
125
+ end
126
+ end
127
+
128
+ def execute_prepared_statement(conn, type, name, opts)
129
+ ps = prepared_statements[name]
130
+ sql = ps.prepared_sql
131
+ if cursora = conn.prepared_statements[name]
132
+ cursor, cursor_sql = cursora
133
+ if cursor_sql != sql
134
+ cursor.close
135
+ cursor = nil
136
+ end
137
+ end
138
+ unless cursor
139
+ cursor = log_yield("Preparing #{name}: #{sql}"){conn.parse(sql)}
140
+ conn.prepared_statements[name] = [cursor, sql]
141
+ end
142
+ args = cursor_bind_params(conn, cursor, opts[:arguments])
143
+ r = log_yield("Executing #{name}", args){cursor.exec}
144
+ if block_given?
145
+ yield(cursor)
146
+ elsif type == :insert
147
+ last_insert_id(conn, opts)
148
+ else
149
+ r
150
+ end
151
+ end
152
+
153
+ def last_insert_id(conn, opts)
154
+ unless sequence = opts[:sequence]
155
+ if t = opts[:table]
156
+ sequence = sequence_for_table(t)
157
+ end
158
+ end
159
+ if sequence
160
+ sql = "SELECT #{literal(sequence)}.currval FROM dual"
161
+ begin
162
+ cursor = log_yield(sql){conn.exec(sql)}
163
+ row = cursor.fetch
164
+ row.each{|v| return (v.to_i if v)}
165
+ rescue OCIError
166
+ nil
167
+ ensure
168
+ cursor.close if cursor
70
169
  end
71
170
  end
72
171
  end
73
- alias_method :do, :execute
74
172
 
75
- private
76
-
77
173
  def begin_transaction(conn, opts={})
78
174
  log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
79
- conn
80
175
  end
81
176
 
82
177
  def commit_transaction(conn, opts={})
@@ -88,49 +183,256 @@ module Sequel
88
183
  rescue OCIInvalidHandle
89
184
  nil
90
185
  end
186
+
187
+ def disconnect_error?(e, opts)
188
+ super || (e.is_a?(::OCIError) && CONNECTION_ERROR_CODES.include?(e.code))
189
+ end
91
190
 
92
- def remove_transaction(conn)
93
- conn.autocommit = true if conn
191
+ def oracle_column_type(h)
192
+ case h[:oci8_type]
193
+ when :number
194
+ case h[:scale]
195
+ when 0
196
+ :integer
197
+ when -127
198
+ :float
199
+ else
200
+ :decimal
201
+ end
202
+ when :date
203
+ :datetime
204
+ else
205
+ schema_column_type(h[:db_type])
206
+ end
207
+ end
208
+
209
+ def remove_transaction(conn, committed)
210
+ conn.autocommit = true
211
+ ensure
94
212
  super
95
213
  end
96
214
 
97
215
  def rollback_transaction(conn, opts={})
98
216
  log_yield(TRANSACTION_ROLLBACK){conn.rollback}
99
217
  end
218
+
219
+ def schema_parse_table(table, opts={})
220
+ schema, table = schema_and_table(table)
221
+ schema ||= opts[:schema]
222
+ schema_and_table = if ds = opts[:dataset]
223
+ ds.literal(schema ? SQL::QualifiedIdentifier.new(schema, table) : SQL::Identifier.new(table))
224
+ else
225
+ "#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
226
+ end
227
+ table_schema = []
228
+ m = output_identifier_meth(ds)
229
+ im = input_identifier_meth(ds)
230
+
231
+ # Primary Keys
232
+ ds = metadata_dataset.from(:all_constraints___cons, :all_cons_columns___cols).
233
+ where(:cols__table_name=>im.call(table), :cons__constraint_type=>'P',
234
+ :cons__constraint_name=>:cols__constraint_name, :cons__owner=>:cols__owner)
235
+ ds = ds.where(:cons__owner=>im.call(schema)) if schema
236
+ pks = ds.select_map(:cols__column_name)
237
+
238
+ # Default values
239
+ defaults = begin
240
+ metadata_dataset.from(:dba_tab_cols).
241
+ where(:table_name=>im.call(table)).
242
+ to_hash(:column_name, :data_default)
243
+ rescue DatabaseError
244
+ {}
245
+ end
246
+
247
+ metadata = synchronize(opts[:server]) do |conn|
248
+ begin
249
+ log_yield("Connection.describe_table"){conn.describe_table(schema_and_table)}
250
+ rescue OCIError => e
251
+ raise_error(e)
252
+ end
253
+ end
254
+ metadata.columns.each do |column|
255
+ h = {
256
+ :primary_key => pks.include?(column.name),
257
+ :default => defaults[column.name],
258
+ :oci8_type => column.data_type,
259
+ :db_type => column.type_string.split(' ')[0],
260
+ :type_string => column.type_string,
261
+ :charset_form => column.charset_form,
262
+ :char_used => column.char_used?,
263
+ :char_size => column.char_size,
264
+ :data_size => column.data_size,
265
+ :precision => column.precision,
266
+ :scale => column.scale,
267
+ :fsprecision => column.fsprecision,
268
+ :lfprecision => column.lfprecision,
269
+ :allow_null => column.nullable?
270
+ }
271
+ h[:type] = oracle_column_type(h)
272
+ table_schema << [m.call(column.name), h]
273
+ end
274
+ table_schema
275
+ end
100
276
  end
101
277
 
102
278
  class Dataset < Sequel::Dataset
103
279
  include DatasetMethods
104
280
 
281
+ Database::DatasetClass = self
282
+
283
+ PREPARED_ARG_PLACEHOLDER = ':'.freeze
284
+
285
+ # Oracle already supports named bind arguments, so use directly.
286
+ module ArgumentMapper
287
+ include Sequel::Dataset::ArgumentMapper
288
+
289
+ protected
290
+
291
+ # Return a hash with the same values as the given hash,
292
+ # but with the keys converted to strings.
293
+ def map_to_prepared_args(bind_vars)
294
+ prepared_args.map{|v, t| [bind_vars[v], t]}
295
+ end
296
+
297
+ private
298
+
299
+ # Oracle uses a : before the name of the argument for named
300
+ # arguments.
301
+ def prepared_arg(k)
302
+ y, type = k.to_s.split("__", 2)
303
+ prepared_args << [y.to_sym, type]
304
+ i = prepared_args.length
305
+ LiteralString.new(":#{i}")
306
+ end
307
+
308
+ # Always assume a prepared argument.
309
+ def prepared_arg?(k)
310
+ true
311
+ end
312
+ end
313
+
314
+ # Oracle prepared statement uses a new prepared statement each time
315
+ # it is called, but it does use the bind arguments.
316
+ module BindArgumentMethods
317
+ include ArgumentMapper
318
+
319
+ private
320
+
321
+ # Run execute_select on the database with the given SQL and the stored
322
+ # bind arguments.
323
+ def execute(sql, opts={}, &block)
324
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
325
+ end
326
+
327
+ # Same as execute, explicit due to intricacies of alias and super.
328
+ def execute_dui(sql, opts={}, &block)
329
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
330
+ end
331
+
332
+ # Same as execute, explicit due to intricacies of alias and super.
333
+ def execute_insert(sql, opts={}, &block)
334
+ super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
335
+ end
336
+ end
337
+
338
+ module PreparedStatementMethods
339
+ include BindArgumentMethods
340
+
341
+ private
342
+
343
+ # Execute the stored prepared statement name and the stored bind
344
+ # arguments instead of the SQL given.
345
+ def execute(sql, opts={}, &block)
346
+ super(prepared_statement_name, opts, &block)
347
+ end
348
+
349
+ # Same as execute, explicit due to intricacies of alias and super.
350
+ def execute_dui(sql, opts={}, &block)
351
+ super(prepared_statement_name, opts, &block)
352
+ end
353
+
354
+ # Same as execute, explicit due to intricacies of alias and super.
355
+ def execute_insert(sql, opts={}, &block)
356
+ super(prepared_statement_name, opts, &block)
357
+ end
358
+ end
359
+
360
+ # Execute the given type of statement with the hash of values.
361
+ def call(type, bind_vars={}, *values, &block)
362
+ ps = to_prepared_statement(type, values)
363
+ ps.extend(BindArgumentMethods)
364
+ ps.call(bind_vars, &block)
365
+ end
366
+
367
+ # Prepare the given type of query with the given name and store
368
+ # it in the database. Note that a new native prepared statement is
369
+ # created on each call to this prepared statement.
370
+ def prepare(type, name=nil, *values)
371
+ ps = to_prepared_statement(type, values)
372
+ ps.extend(PreparedStatementMethods)
373
+ if name
374
+ ps.prepared_statement_name = name
375
+ db.prepared_statements[name] = ps
376
+ end
377
+ ps
378
+ end
379
+
105
380
  def fetch_rows(sql)
106
381
  execute(sql) do |cursor|
107
- begin
108
- @columns = cursor.get_col_names.map{|c| output_identifier(c)}
109
- while r = cursor.fetch
110
- row = {}
111
- r.each_with_index {|v, i| row[@columns[i]] = v unless @columns[i] == :raw_rnum_}
112
- yield row
113
- end
114
- ensure
115
- cursor.close
382
+ offset = @opts[:offset]
383
+ rn = row_number_column
384
+ cps = db.conversion_procs
385
+ cols = columns = cursor.get_col_names.map{|c| output_identifier(c)}
386
+ metadata = cursor.column_metadata
387
+ cm = cols.zip(metadata).map{|c, m| [c, cps[m.data_type]]}
388
+ columns = cols.reject{|x| x == rn} if offset
389
+ @columns = columns
390
+ while r = cursor.fetch
391
+ row = {}
392
+ r.zip(cm).each{|v, (c, cp)| row[c] = ((v && cp) ? cp.call(v) : v)}
393
+ row.delete(rn) if offset
394
+ yield row
116
395
  end
117
396
  end
118
397
  self
119
398
  end
120
399
 
400
+ # Create a named prepared statement that is stored in the
401
+ # database (and connection) for reuse.
402
+ def prepare(type, name=nil, *values)
403
+ ps = to_prepared_statement(type, values)
404
+ ps.extend(PreparedStatementMethods)
405
+ if name
406
+ ps.prepared_statement_name = name
407
+ db.prepared_statements[name] = ps
408
+ end
409
+ ps
410
+ end
411
+
412
+ # Oracle requires type specifiers for placeholders, at least
413
+ # if you ever want to use a nil/NULL value as the value for
414
+ # the placeholder.
415
+ def requires_placeholder_type_specifiers?
416
+ true
417
+ end
418
+
121
419
  private
122
420
 
123
- def literal_other(v)
421
+ def literal_other_append(sql, v)
124
422
  case v
125
423
  when OraDate
126
- literal(Sequel.database_to_application_timestamp(v))
424
+ literal_append(sql, db.to_application_timestamp(v))
127
425
  when OCI8::CLOB
128
426
  v.rewind
129
- literal(v.read)
427
+ literal_append(sql, v.read)
130
428
  else
131
429
  super
132
430
  end
133
431
  end
432
+
433
+ def prepared_arg_placeholder
434
+ PREPARED_ARG_PLACEHOLDER
435
+ end
134
436
  end
135
437
  end
136
438
  end