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
@@ -0,0 +1,155 @@
1
+ Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
2
+
3
+ module Sequel
4
+ module MySQL
5
+ # This module is used by the mysql and mysql2 adapters to support
6
+ # prepared statements and stored procedures.
7
+ module PreparedStatements
8
+ module DatabaseMethods
9
+ # Support stored procedures on MySQL
10
+ def call_sproc(name, opts={}, &block)
11
+ args = opts[:args] || []
12
+ execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
13
+ end
14
+
15
+ # Executes the given SQL using an available connection, yielding the
16
+ # connection if the block is given.
17
+ def execute(sql, opts={}, &block)
18
+ if opts[:sproc]
19
+ call_sproc(sql, opts, &block)
20
+ elsif sql.is_a?(Symbol)
21
+ execute_prepared_statement(sql, opts, &block)
22
+ else
23
+ synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def add_prepared_statements_cache(conn)
30
+ class << conn
31
+ attr_accessor :prepared_statements
32
+ end
33
+ conn.prepared_statements = {}
34
+ end
35
+
36
+ # Executes a prepared statement on an available connection. If the
37
+ # prepared statement already exists for the connection and has the same
38
+ # SQL, reuse it, otherwise, prepare the new statement. Because of the
39
+ # usual MySQL stupidity, we are forced to name arguments via separate
40
+ # SET queries. Use @sequel_arg_N (for N starting at 1) for these
41
+ # arguments.
42
+ def execute_prepared_statement(ps_name, opts, &block)
43
+ args = opts[:arguments]
44
+ ps = prepared_statement(ps_name)
45
+ sql = ps.prepared_sql
46
+ synchronize(opts[:server]) do |conn|
47
+ unless conn.prepared_statements[ps_name] == sql
48
+ conn.prepared_statements[ps_name] = sql
49
+ _execute(conn, "PREPARE #{ps_name} FROM #{literal(sql)}", opts)
50
+ end
51
+ i = 0
52
+ _execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
53
+ opts = opts.merge(:log_sql=>" (#{sql})") if ps.log_sql
54
+ _execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
55
+ end
56
+ end
57
+
58
+ end
59
+ module DatasetMethods
60
+ include Sequel::Dataset::StoredProcedures
61
+
62
+ # Methods to add to MySQL prepared statement calls without using a
63
+ # real database prepared statement and bound variables.
64
+ module CallableStatementMethods
65
+ # Extend given dataset with this module so subselects inside subselects in
66
+ # prepared statements work.
67
+ def subselect_sql_append(sql, ds)
68
+ ps = ds.to_prepared_statement(:select).clone(:append_sql => sql)
69
+ ps.extend(CallableStatementMethods)
70
+ ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
71
+ ps.prepared_args = prepared_args
72
+ ps.prepared_sql
73
+ end
74
+ end
75
+
76
+ # Methods for MySQL prepared statements using the native driver.
77
+ module PreparedStatementMethods
78
+ include Sequel::Dataset::UnnumberedArgumentMapper
79
+
80
+ # Raise a more obvious error if you attempt to call a unnamed prepared statement.
81
+ def call(*)
82
+ raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
83
+ super
84
+ end
85
+
86
+ private
87
+
88
+ # Execute the prepared statement with the bind arguments instead of
89
+ # the given SQL.
90
+ def execute(sql, opts={}, &block)
91
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
92
+ end
93
+
94
+ # Same as execute, explicit due to intricacies of alias and super.
95
+ def execute_dui(sql, opts={}, &block)
96
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
97
+ end
98
+
99
+ # Same as execute, explicit due to intricacies of alias and super.
100
+ def execute_insert(sql, opts={}, &block)
101
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
102
+ end
103
+ end
104
+
105
+ # Methods for MySQL stored procedures using the native driver.
106
+ module StoredProcedureMethods
107
+ include Sequel::Dataset::StoredProcedureMethods
108
+
109
+ private
110
+
111
+ # Execute the database stored procedure with the stored arguments.
112
+ def execute(sql, opts={}, &block)
113
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
114
+ end
115
+
116
+ # Same as execute, explicit due to intricacies of alias and super.
117
+ def execute_dui(sql, opts={}, &block)
118
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
119
+ end
120
+ end
121
+
122
+ # MySQL is different in that it supports prepared statements but not bound
123
+ # variables outside of prepared statements. The default implementation
124
+ # breaks the use of subselects in prepared statements, so extend the
125
+ # temporary prepared statement that this creates with a module that
126
+ # fixes it.
127
+ def call(type, bind_arguments={}, *values, &block)
128
+ ps = to_prepared_statement(type, values)
129
+ ps.extend(CallableStatementMethods)
130
+ ps.call(bind_arguments, &block)
131
+ end
132
+
133
+ # Store the given type of prepared statement in the associated database
134
+ # with the given name.
135
+ def prepare(type, name=nil, *values)
136
+ ps = to_prepared_statement(type, values)
137
+ ps.extend(PreparedStatementMethods)
138
+ if name
139
+ ps.prepared_statement_name = name
140
+ db.set_prepared_statement(name, ps)
141
+ end
142
+ ps
143
+ end
144
+
145
+ private
146
+
147
+ # Extend the dataset with the MySQL stored procedure methods.
148
+ def prepare_extend_sproc(ds)
149
+ ds.extend(StoredProcedureMethods)
150
+ end
151
+
152
+ end
153
+ end
154
+ end
155
+ end
@@ -1,9 +1,13 @@
1
+ Sequel.require 'adapters/utils/emulate_offset_with_row_number'
2
+
1
3
  module Sequel
2
4
  module Oracle
3
5
  module DatabaseMethods
4
6
  TEMPORARY = 'GLOBAL TEMPORARY '.freeze
5
7
  AUTOINCREMENT = ''.freeze
6
8
 
9
+ attr_accessor :autosequence
10
+
7
11
  def create_sequence(name, opts={})
8
12
  self << create_sequence_sql(name, opts)
9
13
  end
@@ -12,6 +16,10 @@ module Sequel
12
16
  self << create_trigger_sql(*args)
13
17
  end
14
18
 
19
+ def current_user
20
+ @current_user ||= metadata_dataset.get{sys_context('USERENV', 'CURRENT_USER')}
21
+ end
22
+
15
23
  def drop_sequence(name)
16
24
  self << drop_sequence_sql(name)
17
25
  end
@@ -21,33 +29,59 @@ module Sequel
21
29
  :oracle
22
30
  end
23
31
 
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])}
32
+ # Oracle namespaces indexes per table.
33
+ def global_index_namespace?
34
+ false
27
35
  end
28
36
 
29
- def table_exists?(name)
30
- from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
37
+ def tables(opts={})
38
+ m = output_identifier_meth
39
+ metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE').map{|r| m.call(r[:tname])}
31
40
  end
32
41
 
33
42
  def views(opts={})
34
- ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW')
35
- ds.map{|r| ds.send(:output_identifier, r[:tname])}
43
+ m = output_identifier_meth
44
+ metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW').map{|r| m.call(r[:tname])}
36
45
  end
37
46
 
38
47
  def view_exists?(name)
39
- from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'VIEW').count > 0
48
+ m = input_identifier_meth
49
+ metadata_dataset.from(:tab).filter(:tname =>m.call(name), :tabtype => 'VIEW').count > 0
40
50
  end
41
51
 
42
52
  private
43
53
 
44
- def auto_increment_sql
45
- AUTOINCREMENT
54
+ # Handle Oracle specific ALTER TABLE SQL
55
+ def alter_table_sql(table, op)
56
+ case op[:op]
57
+ when :add_column
58
+ if op[:primary_key]
59
+ sqls = []
60
+ sqls << alter_table_sql(table, op.merge(:primary_key=>nil))
61
+ if op[:auto_increment]
62
+ seq_name = default_sequence_name(table, op[:name])
63
+ sqls << drop_sequence_sql(seq_name)
64
+ sqls << create_sequence_sql(seq_name, op)
65
+ sqls << "UPDATE #{quote_schema_table(table)} SET #{quote_identifier(op[:name])} = #{seq_name}.nextval"
66
+ end
67
+ sqls << "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})"
68
+ sqls
69
+ else
70
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
71
+ end
72
+ when :set_column_null
73
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{op[:null] ? 'NULL' : 'NOT NULL'}"
74
+ when :set_column_type
75
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{type_literal(op)}"
76
+ when :set_column_default
77
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} DEFAULT #{literal(op[:default])}"
78
+ else
79
+ super(table, op)
80
+ end
46
81
  end
47
82
 
48
- # SQL fragment for showing a table is temporary
49
- def temporary_table_sql
50
- TEMPORARY
83
+ def auto_increment_sql
84
+ AUTOINCREMENT
51
85
  end
52
86
 
53
87
  def create_sequence_sql(name, opts={})
@@ -65,7 +99,7 @@ module Sequel
65
99
  drop_seq_statement = nil
66
100
  generator.columns.each do |c|
67
101
  if c[:auto_increment]
68
- c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
102
+ c[:sequence_name] ||= default_sequence_name(name, c[:name])
69
103
  unless c[:create_sequence] == false
70
104
  drop_seq_statement = drop_sequence_sql(c[:sequence_name])
71
105
  statements << create_sequence_sql(c[:sequence_name], c)
@@ -97,13 +131,122 @@ module Sequel
97
131
  sql
98
132
  end
99
133
 
134
+ def default_sequence_name(table, column)
135
+ "seq_#{table}_#{column}"
136
+ end
137
+
100
138
  def drop_sequence_sql(name)
101
139
  "DROP SEQUENCE #{quote_identifier(name)}"
102
140
  end
141
+
142
+ def remove_cached_schema(table)
143
+ @primary_key_sequences.delete(table)
144
+ super
145
+ end
146
+
147
+ def sequence_for_table(table)
148
+ return nil unless autosequence
149
+ @primary_key_sequences.fetch(table) do |key|
150
+ pk = schema(table).select{|k, v| v[:primary_key]}
151
+ @primary_key_sequences[table] = if pk.length == 1
152
+ seq = "seq_#{table}_#{pk.first.first}"
153
+ seq.to_sym unless from(:user_sequences).filter(:sequence_name=>input_identifier_meth.call(seq)).empty?
154
+ end
155
+ end
156
+ end
157
+
158
+ # Oracle's integer/:number type handles larger values than
159
+ # most other databases's bigint types, so it should be
160
+ # safe to use for Bignum.
161
+ def type_literal_generic_bignum(column)
162
+ :integer
163
+ end
164
+
165
+ # Oracle doesn't have a time type, so use timestamp for all
166
+ # time columns.
167
+ def type_literal_generic_time(column)
168
+ :timestamp
169
+ end
170
+
171
+ # Oracle doesn't have a boolean type or even a reasonable
172
+ # facsimile. Using a char(1) seems to be the recommended way.
173
+ def type_literal_generic_trueclass(column)
174
+ :'char(1)'
175
+ end
176
+
177
+ # SQL fragment for showing a table is temporary
178
+ def temporary_table_sql
179
+ TEMPORARY
180
+ end
103
181
  end
104
182
 
105
183
  module DatasetMethods
106
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct columns from join where group having compounds order limit lock')
184
+ include EmulateOffsetWithRowNumber
185
+
186
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having compounds order lock')
187
+ ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
188
+ SPACE = Dataset::SPACE
189
+ APOS = Dataset::APOS
190
+ APOS_RE = Dataset::APOS_RE
191
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
192
+ FROM = Dataset::FROM
193
+ BITCOMP_OPEN = "((0 - ".freeze
194
+ BITCOMP_CLOSE = ") - 1)".freeze
195
+ ILIKE_0 = "(UPPER(".freeze
196
+ ILIKE_1 = ") ".freeze
197
+ ILIKE_2 = ' UPPER('.freeze
198
+ ILIKE_3 = "))".freeze
199
+ LIKE = 'LIKE'.freeze
200
+ NOT_LIKE = 'NOT LIKE'.freeze
201
+ TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
202
+ TIMESTAMP_OFFSET_FORMAT = "%+03i:%02i".freeze
203
+ BOOL_FALSE = "'N'".freeze
204
+ BOOL_TRUE = "'Y'".freeze
205
+ HSTAR = "H*".freeze
206
+ DUAL = ['DUAL'.freeze].freeze
207
+
208
+ # Oracle needs to emulate bitwise operators and ILIKE/NOT ILIKE operators.
209
+ def complex_expression_sql_append(sql, op, args)
210
+ case op
211
+ when :&
212
+ sql << complex_expression_arg_pairs(args){|a, b| "CAST(BITAND(#{literal(a)}, #{literal(b)}) AS INTEGER)"}
213
+ when :|
214
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} - #{complex_expression_sql(:&, [a, b])} + #{literal(b)})"}
215
+ when :^
216
+ sql << complex_expression_arg_pairs(args){|*x| "(#{complex_expression_sql(:|, x)} - #{complex_expression_sql(:&, x)})"}
217
+ when :'B~'
218
+ sql << BITCOMP_OPEN
219
+ literal_append(sql, args.at(0))
220
+ sql << BITCOMP_CLOSE
221
+ when :<<
222
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * power(2, #{literal b}))"}
223
+ when :>>
224
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
225
+ when :%
226
+ sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
227
+ when :ILIKE, :'NOT ILIKE'
228
+ sql << ILIKE_0
229
+ literal_append(sql, args.at(0))
230
+ sql << ILIKE_1
231
+ sql << (op == :ILIKE ? LIKE : NOT_LIKE)
232
+ sql<< ILIKE_2
233
+ literal_append(sql, args.at(1))
234
+ sql << ILIKE_3
235
+ else
236
+ super
237
+ end
238
+ end
239
+
240
+ # Oracle doesn't support CURRENT_TIME, as it doesn't have
241
+ # a type for storing just time values without a date, so
242
+ # use CURRENT_TIMESTAMP in its place.
243
+ def constant_sql_append(sql, c)
244
+ if c == :CURRENT_TIME
245
+ super(sql, :CURRENT_TIMESTAMP)
246
+ else
247
+ super
248
+ end
249
+ end
107
250
 
108
251
  # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
109
252
  def except(dataset, opts={})
@@ -112,19 +255,10 @@ module Sequel
112
255
  compound_clone(:minus, dataset, opts)
113
256
  end
114
257
 
258
+ # Use a custom expression with EXISTS to determine whether a dataset
259
+ # is empty.
115
260
  def empty?
116
- db[:dual].where(exists).get(1) == nil
117
- end
118
-
119
- # If this dataset is associated with a sequence, return the most recently
120
- # inserted sequence value.
121
- def insert(*args)
122
- r = super
123
- if s = opts[:sequence]
124
- with_sql("SELECT #{literal(s)}.currval FROM dual").single_value.to_i
125
- else
126
- r
127
- end
261
+ db[:dual].where(@opts[:offset] ? exists : unordered.exists).get(1) == nil
128
262
  end
129
263
 
130
264
  # Oracle requires SQL standard datetimes
@@ -139,16 +273,61 @@ module Sequel
139
273
  clone(:sequence=>s)
140
274
  end
141
275
 
276
+ # Handle LIMIT by using a unlimited subselect filtered with ROWNUM.
277
+ def select_sql
278
+ if (limit = @opts[:limit]) && !@opts[:sql]
279
+ ds = clone(:limit=>nil)
280
+ # Lock doesn't work in subselects, so don't use a subselect when locking.
281
+ # Don't use a subselect if custom SQL is used, as it breaks somethings.
282
+ ds = ds.from_self unless @opts[:lock]
283
+ sql = @opts[:append_sql] || ''
284
+ subselect_sql_append(sql, ds.where(SQL::ComplexExpression.new(:<=, ROW_NUMBER_EXPRESSION, limit)))
285
+ sql
286
+ else
287
+ super
288
+ end
289
+ end
290
+
291
+ # Oracle requires recursive CTEs to have column aliases.
292
+ def recursive_cte_requires_column_aliases?
293
+ true
294
+ end
295
+
296
+ # Oracle supports GROUP BY CUBE
297
+ def supports_group_cube?
298
+ true
299
+ end
300
+
301
+ # Oracle supports GROUP BY ROLLUP
302
+ def supports_group_rollup?
303
+ true
304
+ end
305
+
142
306
  # Oracle does not support INTERSECT ALL or EXCEPT ALL
143
307
  def supports_intersect_except_all?
144
308
  false
145
309
  end
310
+
311
+ # Oracle does not support IS TRUE.
312
+ def supports_is_true?
313
+ false
314
+ end
315
+
316
+ # Oracle does not support SELECT *, column
317
+ def supports_select_all_and_column?
318
+ false
319
+ end
146
320
 
147
321
  # Oracle supports timezones in literal timestamps.
148
322
  def supports_timestamp_timezones?
149
323
  true
150
324
  end
151
325
 
326
+ # Oracle does not support WHERE 'Y' for WHERE TRUE.
327
+ def supports_where_true?
328
+ false
329
+ end
330
+
152
331
  # Oracle supports window functions
153
332
  def supports_window_functions?
154
333
  true
@@ -158,25 +337,54 @@ module Sequel
158
337
 
159
338
  # Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
160
339
  # the use of AS anywhere, so this disables it in all cases.
161
- def as_sql(expression, aliaz)
162
- "#{expression} #{quote_identifier(aliaz)}"
340
+ def as_sql_append(sql, aliaz)
341
+ sql << SPACE
342
+ quote_identifier_append(sql, aliaz)
163
343
  end
164
344
 
165
345
  # The strftime format to use when literalizing the time.
166
346
  def default_timestamp_format
167
- "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
347
+ TIMESTAMP_FORMAT
348
+ end
349
+
350
+ # If this dataset is associated with a sequence, return the most recently
351
+ # inserted sequence value.
352
+ def execute_insert(sql, opts={})
353
+ f = @opts[:from]
354
+ super(sql, {:table=>(f.first if f), :sequence=>@opts[:sequence]}.merge(opts))
168
355
  end
169
356
 
170
357
  # Use a colon for the timestamp offset, since Oracle appears to require it.
171
358
  def format_timestamp_offset(hour, minute)
172
- sprintf("%+03i:%02i", hour, minute)
359
+ sprintf(TIMESTAMP_OFFSET_FORMAT, hour, minute)
360
+ end
361
+
362
+ # Oracle doesn't support empty values when inserting.
363
+ def insert_supports_empty_values?
364
+ false
365
+ end
366
+
367
+ # Use string in hex format for blob data.
368
+ def literal_blob_append(sql, v)
369
+ sql << APOS << v.unpack(HSTAR).first << APOS
370
+ end
371
+
372
+ # Oracle uses 'N' for false values.
373
+ def literal_false
374
+ BOOL_FALSE
173
375
  end
174
376
 
175
377
  # Oracle uses the SQL standard of only doubling ' inside strings.
176
- def literal_string(v)
177
- "'#{v.gsub("'", "''")}'"
378
+ def literal_string_append(sql, v)
379
+ sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
380
+ end
381
+
382
+ # Oracle uses 'Y' for true values.
383
+ def literal_true
384
+ BOOL_TRUE
178
385
  end
179
386
 
387
+ # Use the Oracle-specific SQL clauses (no limit, since it is emulated).
180
388
  def select_clause_methods
181
389
  SELECT_CLAUSE_METHODS
182
390
  end
@@ -186,18 +394,8 @@ module Sequel
186
394
  # so add the dummy DUAL table if the dataset doesn't select
187
395
  # from a table.
188
396
  def select_from_sql(sql)
189
- sql << " FROM #{source_list(@opts[:from] || ['DUAL'])}"
190
- end
191
-
192
- # Oracle requires a subselect to do limit and offset
193
- def select_limit_sql(sql)
194
- if limit = @opts[:limit]
195
- if (offset = @opts[:offset]) && (offset > 0)
196
- sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
197
- else
198
- sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
199
- end
200
- end
397
+ sql << FROM
398
+ source_list_append(sql, @opts[:from] || DUAL)
201
399
  end
202
400
  end
203
401
  end