activerecord 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (311) hide show
  1. data/CHANGELOG +4928 -3
  2. data/README +45 -46
  3. data/RUNNING_UNIT_TESTS +8 -11
  4. data/Rakefile +247 -0
  5. data/install.rb +8 -38
  6. data/lib/active_record/aggregations.rb +64 -49
  7. data/lib/active_record/associations/association_collection.rb +217 -47
  8. data/lib/active_record/associations/association_proxy.rb +159 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
  12. data/lib/active_record/associations/has_many_association.rb +145 -75
  13. data/lib/active_record/associations/has_many_through_association.rb +283 -0
  14. data/lib/active_record/associations/has_one_association.rb +96 -0
  15. data/lib/active_record/associations.rb +1537 -304
  16. data/lib/active_record/attribute_methods.rb +328 -0
  17. data/lib/active_record/base.rb +2001 -588
  18. data/lib/active_record/calculations.rb +269 -0
  19. data/lib/active_record/callbacks.rb +169 -165
  20. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
  22. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  23. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
  31. data/lib/active_record/fixtures.rb +946 -100
  32. data/lib/active_record/locking/optimistic.rb +144 -0
  33. data/lib/active_record/locking/pessimistic.rb +77 -0
  34. data/lib/active_record/migration.rb +417 -0
  35. data/lib/active_record/observer.rb +142 -32
  36. data/lib/active_record/query_cache.rb +23 -0
  37. data/lib/active_record/reflection.rb +163 -70
  38. data/lib/active_record/schema.rb +58 -0
  39. data/lib/active_record/schema_dumper.rb +171 -0
  40. data/lib/active_record/serialization.rb +98 -0
  41. data/lib/active_record/serializers/json_serializer.rb +71 -0
  42. data/lib/active_record/serializers/xml_serializer.rb +315 -0
  43. data/lib/active_record/timestamp.rb +41 -0
  44. data/lib/active_record/transactions.rb +87 -57
  45. data/lib/active_record/validations.rb +909 -122
  46. data/lib/active_record/vendor/db2.rb +362 -0
  47. data/lib/active_record/vendor/mysql.rb +126 -29
  48. data/lib/active_record/version.rb +9 -0
  49. data/lib/active_record.rb +35 -7
  50. data/lib/activerecord.rb +1 -0
  51. data/test/aaa_create_tables_test.rb +72 -0
  52. data/test/abstract_unit.rb +73 -5
  53. data/test/active_schema_test_mysql.rb +43 -0
  54. data/test/adapter_test.rb +105 -0
  55. data/test/adapter_test_sqlserver.rb +95 -0
  56. data/test/aggregations_test.rb +110 -16
  57. data/test/all.sh +2 -2
  58. data/test/ar_schema_test.rb +33 -0
  59. data/test/association_inheritance_reload.rb +14 -0
  60. data/test/associations/ar_joins_test.rb +0 -0
  61. data/test/associations/callbacks_test.rb +147 -0
  62. data/test/associations/cascaded_eager_loading_test.rb +110 -0
  63. data/test/associations/eager_singularization_test.rb +145 -0
  64. data/test/associations/eager_test.rb +442 -0
  65. data/test/associations/extension_test.rb +47 -0
  66. data/test/associations/inner_join_association_test.rb +88 -0
  67. data/test/associations/join_model_test.rb +553 -0
  68. data/test/associations_test.rb +1930 -267
  69. data/test/attribute_methods_test.rb +146 -0
  70. data/test/base_test.rb +1316 -84
  71. data/test/binary_test.rb +32 -0
  72. data/test/calculations_test.rb +251 -0
  73. data/test/callbacks_test.rb +400 -0
  74. data/test/class_inheritable_attributes_test.rb +3 -4
  75. data/test/column_alias_test.rb +17 -0
  76. data/test/connection_test_firebird.rb +8 -0
  77. data/test/connection_test_mysql.rb +30 -0
  78. data/test/connections/native_db2/connection.rb +25 -0
  79. data/test/connections/native_firebird/connection.rb +26 -0
  80. data/test/connections/native_frontbase/connection.rb +27 -0
  81. data/test/connections/native_mysql/connection.rb +21 -18
  82. data/test/connections/native_openbase/connection.rb +21 -0
  83. data/test/connections/native_oracle/connection.rb +27 -0
  84. data/test/connections/native_postgresql/connection.rb +17 -18
  85. data/test/connections/native_sqlite/connection.rb +17 -16
  86. data/test/connections/native_sqlite3/connection.rb +25 -0
  87. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  88. data/test/connections/native_sybase/connection.rb +23 -0
  89. data/test/copy_table_test_sqlite.rb +69 -0
  90. data/test/datatype_test_postgresql.rb +203 -0
  91. data/test/date_time_test.rb +37 -0
  92. data/test/default_test_firebird.rb +16 -0
  93. data/test/defaults_test.rb +67 -0
  94. data/test/deprecated_finder_test.rb +30 -0
  95. data/test/finder_test.rb +607 -32
  96. data/test/fixtures/accounts.yml +28 -0
  97. data/test/fixtures/all/developers.yml +0 -0
  98. data/test/fixtures/all/people.csv +0 -0
  99. data/test/fixtures/all/tasks.yml +0 -0
  100. data/test/fixtures/author.rb +107 -0
  101. data/test/fixtures/author_favorites.yml +4 -0
  102. data/test/fixtures/authors.yml +7 -0
  103. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  104. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  105. data/test/fixtures/bad_fixtures/blank_line +3 -0
  106. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  107. data/test/fixtures/bad_fixtures/missing_value +1 -0
  108. data/test/fixtures/binaries.yml +132 -0
  109. data/test/fixtures/binary.rb +2 -0
  110. data/test/fixtures/book.rb +4 -0
  111. data/test/fixtures/books.yml +7 -0
  112. data/test/fixtures/categories/special_categories.yml +9 -0
  113. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  114. data/test/fixtures/categories.yml +14 -0
  115. data/test/fixtures/categories_ordered.yml +7 -0
  116. data/test/fixtures/categories_posts.yml +23 -0
  117. data/test/fixtures/categorization.rb +5 -0
  118. data/test/fixtures/categorizations.yml +17 -0
  119. data/test/fixtures/category.rb +26 -0
  120. data/test/fixtures/citation.rb +6 -0
  121. data/test/fixtures/comment.rb +23 -0
  122. data/test/fixtures/comments.yml +59 -0
  123. data/test/fixtures/companies.yml +55 -0
  124. data/test/fixtures/company.rb +81 -4
  125. data/test/fixtures/company_in_module.rb +32 -6
  126. data/test/fixtures/computer.rb +4 -0
  127. data/test/fixtures/computers.yml +4 -0
  128. data/test/fixtures/contact.rb +16 -0
  129. data/test/fixtures/courses.yml +7 -0
  130. data/test/fixtures/customer.rb +28 -3
  131. data/test/fixtures/customers.yml +17 -0
  132. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  133. data/test/fixtures/db_definitions/db2.sql +235 -0
  134. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  135. data/test/fixtures/db_definitions/db22.sql +5 -0
  136. data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
  137. data/test/fixtures/db_definitions/firebird.sql +310 -0
  138. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  139. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  140. data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
  141. data/test/fixtures/db_definitions/frontbase.sql +273 -0
  142. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  143. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  144. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  145. data/test/fixtures/db_definitions/openbase.sql +318 -0
  146. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  148. data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
  149. data/test/fixtures/db_definitions/oracle.sql +330 -0
  150. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  151. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  152. data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
  153. data/test/fixtures/db_definitions/postgresql.sql +217 -38
  154. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/postgresql2.sql +2 -2
  156. data/test/fixtures/db_definitions/schema.rb +354 -0
  157. data/test/fixtures/db_definitions/schema2.rb +11 -0
  158. data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
  159. data/test/fixtures/db_definitions/sqlite.sql +139 -5
  160. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  161. data/test/fixtures/db_definitions/sqlite2.sql +1 -0
  162. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  163. data/test/fixtures/db_definitions/sybase.sql +222 -0
  164. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  165. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  166. data/test/fixtures/developer.rb +70 -6
  167. data/test/fixtures/developers.yml +21 -0
  168. data/test/fixtures/developers_projects/david_action_controller +2 -1
  169. data/test/fixtures/developers_projects/david_active_record +2 -1
  170. data/test/fixtures/developers_projects.yml +17 -0
  171. data/test/fixtures/edge.rb +5 -0
  172. data/test/fixtures/edges.yml +6 -0
  173. data/test/fixtures/entrants.yml +14 -0
  174. data/test/fixtures/example.log +1 -0
  175. data/test/fixtures/fk_test_has_fk.yml +3 -0
  176. data/test/fixtures/fk_test_has_pk.yml +2 -0
  177. data/test/fixtures/flowers.jpg +0 -0
  178. data/test/fixtures/funny_jokes.yml +10 -0
  179. data/test/fixtures/item.rb +7 -0
  180. data/test/fixtures/items.yml +4 -0
  181. data/test/fixtures/joke.rb +3 -0
  182. data/test/fixtures/keyboard.rb +3 -0
  183. data/test/fixtures/legacy_thing.rb +3 -0
  184. data/test/fixtures/legacy_things.yml +3 -0
  185. data/test/fixtures/matey.rb +4 -0
  186. data/test/fixtures/mateys.yml +4 -0
  187. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  188. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  189. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  190. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  191. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  192. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  193. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  194. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  195. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  196. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  197. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  198. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  199. data/test/fixtures/minimalistic.rb +2 -0
  200. data/test/fixtures/minimalistics.yml +2 -0
  201. data/test/fixtures/mixed_case_monkey.rb +3 -0
  202. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  203. data/test/fixtures/mixins.yml +29 -0
  204. data/test/fixtures/movies.yml +7 -0
  205. data/test/fixtures/naked/csv/accounts.csv +1 -0
  206. data/test/fixtures/naked/yml/accounts.yml +1 -0
  207. data/test/fixtures/naked/yml/companies.yml +1 -0
  208. data/test/fixtures/naked/yml/courses.yml +1 -0
  209. data/test/fixtures/order.rb +4 -0
  210. data/test/fixtures/parrot.rb +13 -0
  211. data/test/fixtures/parrots.yml +27 -0
  212. data/test/fixtures/parrots_pirates.yml +7 -0
  213. data/test/fixtures/people.yml +3 -0
  214. data/test/fixtures/person.rb +4 -0
  215. data/test/fixtures/pirate.rb +5 -0
  216. data/test/fixtures/pirates.yml +9 -0
  217. data/test/fixtures/post.rb +59 -0
  218. data/test/fixtures/posts.yml +48 -0
  219. data/test/fixtures/project.rb +27 -2
  220. data/test/fixtures/projects.yml +7 -0
  221. data/test/fixtures/reader.rb +4 -0
  222. data/test/fixtures/readers.yml +4 -0
  223. data/test/fixtures/reply.rb +18 -2
  224. data/test/fixtures/reserved_words/distinct.yml +5 -0
  225. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  226. data/test/fixtures/reserved_words/group.yml +14 -0
  227. data/test/fixtures/reserved_words/select.yml +8 -0
  228. data/test/fixtures/reserved_words/values.yml +7 -0
  229. data/test/fixtures/ship.rb +3 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/subject.rb +4 -0
  232. data/test/fixtures/subscriber.rb +4 -3
  233. data/test/fixtures/tag.rb +7 -0
  234. data/test/fixtures/tagging.rb +10 -0
  235. data/test/fixtures/taggings.yml +25 -0
  236. data/test/fixtures/tags.yml +7 -0
  237. data/test/fixtures/task.rb +3 -0
  238. data/test/fixtures/tasks.yml +7 -0
  239. data/test/fixtures/topic.rb +20 -3
  240. data/test/fixtures/topics.yml +22 -0
  241. data/test/fixtures/treasure.rb +4 -0
  242. data/test/fixtures/treasures.yml +10 -0
  243. data/test/fixtures/vertex.rb +9 -0
  244. data/test/fixtures/vertices.yml +4 -0
  245. data/test/fixtures_test.rb +574 -8
  246. data/test/inheritance_test.rb +113 -27
  247. data/test/json_serialization_test.rb +180 -0
  248. data/test/lifecycle_test.rb +56 -29
  249. data/test/locking_test.rb +273 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +933 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_test.rb +95 -0
  254. data/test/modules_test.rb +23 -10
  255. data/test/multiple_db_test.rb +17 -3
  256. data/test/pk_test.rb +59 -15
  257. data/test/query_cache_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +124 -27
  260. data/test/reserved_word_test_mysql.rb +177 -0
  261. data/test/schema_authorization_test_postgresql.rb +75 -0
  262. data/test/schema_dumper_test.rb +131 -0
  263. data/test/schema_test_postgresql.rb +64 -0
  264. data/test/serialization_test.rb +47 -0
  265. data/test/synonym_test_oracle.rb +17 -0
  266. data/test/table_name_test_sqlserver.rb +23 -0
  267. data/test/threaded_connections_test.rb +48 -0
  268. data/test/transactions_test.rb +227 -29
  269. data/test/unconnected_test.rb +14 -6
  270. data/test/validations_test.rb +1293 -32
  271. data/test/xml_serialization_test.rb +202 -0
  272. metadata +347 -143
  273. data/dev-utils/eval_debugger.rb +0 -9
  274. data/examples/associations.rb +0 -87
  275. data/examples/shared_setup.rb +0 -15
  276. data/examples/validation.rb +0 -88
  277. data/lib/active_record/deprecated_associations.rb +0 -70
  278. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  279. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  280. data/lib/active_record/support/clean_logger.rb +0 -10
  281. data/lib/active_record/support/inflector.rb +0 -70
  282. data/lib/active_record/vendor/simple.rb +0 -702
  283. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  284. data/lib/active_record/wrappings.rb +0 -59
  285. data/rakefile +0 -122
  286. data/test/deprecated_associations_test.rb +0 -336
  287. data/test/fixtures/accounts/signals37 +0 -3
  288. data/test/fixtures/accounts/unknown +0 -2
  289. data/test/fixtures/companies/first_client +0 -6
  290. data/test/fixtures/companies/first_firm +0 -4
  291. data/test/fixtures/companies/second_client +0 -6
  292. data/test/fixtures/courses/java +0 -2
  293. data/test/fixtures/courses/ruby +0 -2
  294. data/test/fixtures/customers/david +0 -6
  295. data/test/fixtures/db_definitions/mysql.sql +0 -96
  296. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  297. data/test/fixtures/developers/david +0 -2
  298. data/test/fixtures/developers/jamis +0 -2
  299. data/test/fixtures/entrants/first +0 -3
  300. data/test/fixtures/entrants/second +0 -3
  301. data/test/fixtures/entrants/third +0 -3
  302. data/test/fixtures/fixture_database.sqlite +0 -0
  303. data/test/fixtures/fixture_database_2.sqlite +0 -0
  304. data/test/fixtures/movies/first +0 -2
  305. data/test/fixtures/movies/second +0 -2
  306. data/test/fixtures/projects/action_controller +0 -2
  307. data/test/fixtures/projects/active_record +0 -2
  308. data/test/fixtures/topics/first +0 -9
  309. data/test/fixtures/topics/second +0 -8
  310. data/test/inflector_test.rb +0 -104
  311. data/test/thread_safety_test.rb +0 -33
@@ -0,0 +1,362 @@
1
+ require 'db2/db2cli.rb'
2
+
3
+ module DB2
4
+ module DB2Util
5
+ include DB2CLI
6
+
7
+ def free() SQLFreeHandle(@handle_type, @handle); end
8
+ def handle() @handle; end
9
+
10
+ def check_rc(rc)
11
+ if ![SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA_FOUND].include?(rc)
12
+ rec = 1
13
+ msg = ''
14
+ loop do
15
+ a = SQLGetDiagRec(@handle_type, @handle, rec, 500)
16
+ break if a[0] != SQL_SUCCESS
17
+ msg << a[3] if !a[3].nil? and a[3] != '' # Create message.
18
+ rec += 1
19
+ end
20
+ raise "DB2 error: #{msg}"
21
+ end
22
+ end
23
+ end
24
+
25
+ class Environment
26
+ include DB2Util
27
+
28
+ def initialize
29
+ @handle_type = SQL_HANDLE_ENV
30
+ rc, @handle = SQLAllocHandle(@handle_type, SQL_NULL_HANDLE)
31
+ check_rc(rc)
32
+ end
33
+
34
+ def data_sources(buffer_length = 1024)
35
+ retval = []
36
+ max_buffer_length = buffer_length
37
+
38
+ a = SQLDataSources(@handle, SQL_FETCH_FIRST, SQL_MAX_DSN_LENGTH + 1, buffer_length)
39
+ retval << [a[1], a[3]]
40
+ max_buffer_length = [max_buffer_length, a[4]].max
41
+
42
+ loop do
43
+ a = SQLDataSources(@handle, SQL_FETCH_NEXT, SQL_MAX_DSN_LENGTH + 1, buffer_length)
44
+ break if a[0] == SQL_NO_DATA_FOUND
45
+
46
+ retval << [a[1], a[3]]
47
+ max_buffer_length = [max_buffer_length, a[4]].max
48
+ end
49
+
50
+ if max_buffer_length > buffer_length
51
+ get_data_sources(max_buffer_length)
52
+ else
53
+ retval
54
+ end
55
+ end
56
+ end
57
+
58
+ class Connection
59
+ include DB2Util
60
+
61
+ def initialize(environment)
62
+ @env = environment
63
+ @handle_type = SQL_HANDLE_DBC
64
+ rc, @handle = SQLAllocHandle(@handle_type, @env.handle)
65
+ check_rc(rc)
66
+ end
67
+
68
+ def connect(server_name, user_name = '', auth = '')
69
+ check_rc(SQLConnect(@handle, server_name, user_name.to_s, auth.to_s))
70
+ end
71
+
72
+ def set_connect_attr(attr, value)
73
+ value += "\0" if value.class == String
74
+ check_rc(SQLSetConnectAttr(@handle, attr, value))
75
+ end
76
+
77
+ def set_auto_commit_on
78
+ set_connect_attr(SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON)
79
+ end
80
+
81
+ def set_auto_commit_off
82
+ set_connect_attr(SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF)
83
+ end
84
+
85
+ def disconnect
86
+ check_rc(SQLDisconnect(@handle))
87
+ end
88
+
89
+ def rollback
90
+ check_rc(SQLEndTran(@handle_type, @handle, SQL_ROLLBACK))
91
+ end
92
+
93
+ def commit
94
+ check_rc(SQLEndTran(@handle_type, @handle, SQL_COMMIT))
95
+ end
96
+ end
97
+
98
+ class Statement
99
+ include DB2Util
100
+
101
+ def initialize(connection)
102
+ @conn = connection
103
+ @handle_type = SQL_HANDLE_STMT
104
+ @parms = [] #yun
105
+ @sql = '' #yun
106
+ @numParms = 0 #yun
107
+ @prepared = false #yun
108
+ @parmArray = [] #yun. attributes of the parameter markers
109
+ rc, @handle = SQLAllocHandle(@handle_type, @conn.handle)
110
+ check_rc(rc)
111
+ end
112
+
113
+ def columns(table_name, schema_name = '%')
114
+ check_rc(SQLColumns(@handle, '', schema_name.upcase, table_name.upcase, '%'))
115
+ fetch_all
116
+ end
117
+
118
+ def tables(schema_name = '%')
119
+ check_rc(SQLTables(@handle, '', schema_name.upcase, '%', 'TABLE'))
120
+ fetch_all
121
+ end
122
+
123
+ def indexes(table_name, schema_name = '')
124
+ check_rc(SQLStatistics(@handle, '', schema_name.upcase, table_name.upcase, SQL_INDEX_ALL, SQL_ENSURE))
125
+ fetch_all
126
+ end
127
+
128
+ def prepare(sql)
129
+ @sql = sql
130
+ check_rc(SQLPrepare(@handle, sql))
131
+ rc, @numParms = SQLNumParams(@handle) #number of question marks
132
+ check_rc(rc)
133
+ #--------------------------------------------------------------------------
134
+ # parameter attributes are stored in instance variable @parmArray so that
135
+ # they are available when execute method is called.
136
+ #--------------------------------------------------------------------------
137
+ if @numParms > 0 # get parameter marker attributes
138
+ 1.upto(@numParms) do |i| # parameter number starts from 1
139
+ rc, type, size, decimalDigits = SQLDescribeParam(@handle, i)
140
+ check_rc(rc)
141
+ @parmArray << Parameter.new(type, size, decimalDigits)
142
+ end
143
+ end
144
+ @prepared = true
145
+ self
146
+ end
147
+
148
+ def execute(*parms)
149
+ raise "The statement was not prepared" if @prepared == false
150
+
151
+ if parms.size == 1 and parms[0].class == Array
152
+ parms = parms[0]
153
+ end
154
+
155
+ if @numParms != parms.size
156
+ raise "Number of parameters supplied does not match with the SQL statement"
157
+ end
158
+
159
+ if @numParms > 0 #need to bind parameters
160
+ #--------------------------------------------------------------------
161
+ #calling bindParms may not be safe. Look comment below.
162
+ #--------------------------------------------------------------------
163
+ #bindParms(parms)
164
+
165
+ valueArray = []
166
+ 1.upto(@numParms) do |i| # parameter number starts from 1
167
+ type = @parmArray[i - 1].class
168
+ size = @parmArray[i - 1].size
169
+ decimalDigits = @parmArray[i - 1].decimalDigits
170
+
171
+ if parms[i - 1].class == String
172
+ valueArray << parms[i - 1]
173
+ else
174
+ valueArray << parms[i - 1].to_s
175
+ end
176
+
177
+ rc = SQLBindParameter(@handle, i, type, size, decimalDigits, valueArray[i - 1])
178
+ check_rc(rc)
179
+ end
180
+ end
181
+
182
+ check_rc(SQLExecute(@handle))
183
+
184
+ if @numParms != 0
185
+ check_rc(SQLFreeStmt(@handle, SQL_RESET_PARAMS)) # Reset parameters
186
+ end
187
+
188
+ self
189
+ end
190
+
191
+ #-------------------------------------------------------------------------------
192
+ # The last argument(value) to SQLBindParameter is a deferred argument, that is,
193
+ # it should be available when SQLExecute is called. Even though "value" is
194
+ # local to bindParms method, it seems that it is available when SQLExecute
195
+ # is called. I am not sure whether it would still work if garbage collection
196
+ # is done between bindParms call and SQLExecute call inside the execute method
197
+ # above.
198
+ #-------------------------------------------------------------------------------
199
+ def bindParms(parms) # This is the real thing. It uses SQLBindParms
200
+ 1.upto(@numParms) do |i| # parameter number starts from 1
201
+ rc, dataType, parmSize, decimalDigits = SQLDescribeParam(@handle, i)
202
+ check_rc(rc)
203
+ if parms[i - 1].class == String
204
+ value = parms[i - 1]
205
+ else
206
+ value = parms[i - 1].to_s
207
+ end
208
+ rc = SQLBindParameter(@handle, i, dataType, parmSize, decimalDigits, value)
209
+ check_rc(rc)
210
+ end
211
+ end
212
+
213
+ #------------------------------------------------------------------------------
214
+ # bind method does not use DB2's SQLBindParams, but replaces "?" in the
215
+ # SQL statement with the value before passing the SQL statement to DB2.
216
+ # It is not efficient and can handle only strings since it puts everything in
217
+ # quotes.
218
+ #------------------------------------------------------------------------------
219
+ def bind(sql, args) #does not use SQLBindParams
220
+ arg_index = 0
221
+ result = ""
222
+ tokens(sql).each do |part|
223
+ case part
224
+ when '?'
225
+ result << "'" + (args[arg_index]) + "'" #put it into quotes
226
+ arg_index += 1
227
+ when '??'
228
+ result << "?"
229
+ else
230
+ result << part
231
+ end
232
+ end
233
+ if arg_index < args.size
234
+ raise "Too many SQL parameters"
235
+ elsif arg_index > args.size
236
+ raise "Not enough SQL parameters"
237
+ end
238
+ result
239
+ end
240
+
241
+ ## Break the sql string into parts.
242
+ #
243
+ # This is NOT a full lexer for SQL. It just breaks up the SQL
244
+ # string enough so that question marks, double question marks and
245
+ # quoted strings are separated. This is used when binding
246
+ # arguments to "?" in the SQL string. Note: comments are not
247
+ # handled.
248
+ #
249
+ def tokens(sql)
250
+ toks = sql.scan(/('([^'\\]|''|\\.)*'|"([^"\\]|""|\\.)*"|\?\??|[^'"?]+)/)
251
+ toks.collect { |t| t[0] }
252
+ end
253
+
254
+ def exec_direct(sql)
255
+ check_rc(SQLExecDirect(@handle, sql))
256
+ self
257
+ end
258
+
259
+ def set_cursor_name(name)
260
+ check_rc(SQLSetCursorName(@handle, name))
261
+ self
262
+ end
263
+
264
+ def get_cursor_name
265
+ rc, name = SQLGetCursorName(@handle)
266
+ check_rc(rc)
267
+ name
268
+ end
269
+
270
+ def row_count
271
+ rc, rowcount = SQLRowCount(@handle)
272
+ check_rc(rc)
273
+ rowcount
274
+ end
275
+
276
+ def num_result_cols
277
+ rc, cols = SQLNumResultCols(@handle)
278
+ check_rc(rc)
279
+ cols
280
+ end
281
+
282
+ def fetch_all
283
+ if block_given?
284
+ while row = fetch do
285
+ yield row
286
+ end
287
+ else
288
+ res = []
289
+ while row = fetch do
290
+ res << row
291
+ end
292
+ res
293
+ end
294
+ end
295
+
296
+ def fetch
297
+ cols = get_col_desc
298
+ rc = SQLFetch(@handle)
299
+ if rc == SQL_NO_DATA_FOUND
300
+ SQLFreeStmt(@handle, SQL_CLOSE) # Close cursor
301
+ SQLFreeStmt(@handle, SQL_RESET_PARAMS) # Reset parameters
302
+ return nil
303
+ end
304
+ raise "ERROR" unless rc == SQL_SUCCESS
305
+
306
+ retval = []
307
+ cols.each_with_index do |c, i|
308
+ rc, content = SQLGetData(@handle, i + 1, c[1], c[2] + 1) #yun added 1 to c[2]
309
+ retval << adjust_content(content)
310
+ end
311
+ retval
312
+ end
313
+
314
+ def fetch_as_hash
315
+ cols = get_col_desc
316
+ rc = SQLFetch(@handle)
317
+ if rc == SQL_NO_DATA_FOUND
318
+ SQLFreeStmt(@handle, SQL_CLOSE) # Close cursor
319
+ SQLFreeStmt(@handle, SQL_RESET_PARAMS) # Reset parameters
320
+ return nil
321
+ end
322
+ raise "ERROR" unless rc == SQL_SUCCESS
323
+
324
+ retval = {}
325
+ cols.each_with_index do |c, i|
326
+ rc, content = SQLGetData(@handle, i + 1, c[1], c[2] + 1) #yun added 1 to c[2]
327
+ retval[c[0]] = adjust_content(content)
328
+ end
329
+ retval
330
+ end
331
+
332
+ def get_col_desc
333
+ rc, nr_cols = SQLNumResultCols(@handle)
334
+ cols = (1..nr_cols).collect do |c|
335
+ rc, name, bl, type, col_sz = SQLDescribeCol(@handle, c, 1024)
336
+ [name.downcase, type, col_sz]
337
+ end
338
+ end
339
+
340
+ def adjust_content(c)
341
+ case c.class.to_s
342
+ when 'DB2CLI::NullClass'
343
+ return nil
344
+ when 'DB2CLI::Time'
345
+ "%02d:%02d:%02d" % [c.hour, c.minute, c.second]
346
+ when 'DB2CLI::Date'
347
+ "%04d-%02d-%02d" % [c.year, c.month, c.day]
348
+ when 'DB2CLI::Timestamp'
349
+ "%04d-%02d-%02d %02d:%02d:%02d" % [c.year, c.month, c.day, c.hour, c.minute, c.second]
350
+ else
351
+ return c
352
+ end
353
+ end
354
+ end
355
+
356
+ class Parameter
357
+ attr_reader :type, :size, :decimalDigits
358
+ def initialize(type, size, decimalDigits)
359
+ @type, @size, @decimalDigits = type, size, decimalDigits
360
+ end
361
+ end
362
+ end
@@ -1,14 +1,15 @@
1
- # $Id: mysql.rb,v 1.1 2004/02/24 15:42:29 webster132 Exp $
1
+ # $Id: mysql.rb,v 1.24 2005/02/12 11:37:15 tommy Exp $
2
2
  #
3
- # Copyright (C) 2003 TOMITA Masahiro
3
+ # Copyright (C) 2003-2005 TOMITA Masahiro
4
4
  # tommy@tmtm.org
5
5
  #
6
6
 
7
7
  class Mysql
8
8
 
9
- VERSION = "4.0-ruby-0.2.4"
9
+ VERSION = "4.0-ruby-0.2.6-plus-changes"
10
10
 
11
11
  require "socket"
12
+ require "digest/sha1"
12
13
 
13
14
  MAX_PACKET_LENGTH = 256*256*256-1
14
15
  MAX_ALLOWED_PACKET = 1024*1024*1024
@@ -17,6 +18,9 @@ class Mysql
17
18
  MYSQL_PORT = 3306
18
19
  PROTOCOL_VERSION = 10
19
20
 
21
+ SCRAMBLE_LENGTH = 20
22
+ SCRAMBLE_LENGTH_323 = 8
23
+
20
24
  # Command
21
25
  COM_SLEEP = 0
22
26
  COM_QUIT = 1
@@ -51,11 +55,15 @@ class Mysql
51
55
  CLIENT_ODBC = 1 << 6
52
56
  CLIENT_LOCAL_FILES = 1 << 7
53
57
  CLIENT_IGNORE_SPACE = 1 << 8
58
+ CLIENT_PROTOCOL_41 = 1 << 9
54
59
  CLIENT_INTERACTIVE = 1 << 10
55
60
  CLIENT_SSL = 1 << 11
56
61
  CLIENT_IGNORE_SIGPIPE = 1 << 12
57
62
  CLIENT_TRANSACTIONS = 1 << 13
63
+ CLIENT_RESERVED = 1 << 14
64
+ CLIENT_SECURE_CONNECTION = 1 << 15
58
65
  CLIENT_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
66
+ PROTO_AUTH41 = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION
59
67
 
60
68
  # Connection Option
61
69
  OPT_CONNECT_TIMEOUT = 0
@@ -115,23 +123,50 @@ class Mysql
115
123
  @server_capabilities, = a.slice!(0,2).unpack("v")
116
124
  end
117
125
  if a.size >= 16 then
118
- @server_language, @server_status = a.unpack("cv")
126
+ @server_language, @server_status = a.slice!(0,3).unpack("cv")
119
127
  end
120
128
 
121
129
  flag = 0 if flag == nil
122
130
  flag |= @client_flag | CLIENT_CAPABILITIES
123
131
  flag |= CLIENT_CONNECT_WITH_DB if db
124
- data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+(user||"")+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)
125
- if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0 then
126
- data << "\0"+db
132
+
133
+ @pre_411 = (0 == @server_capabilities & PROTO_AUTH41)
134
+ if @pre_411
135
+ data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+
136
+ (user||"")+"\0"+
137
+ scramble(passwd, @scramble_buff, @protocol_version==9)
138
+ else
139
+ dummy, @salt2 = a.unpack("a13a12")
140
+ @scramble_buff += @salt2
141
+ flag |= PROTO_AUTH41
142
+ data = Net::int4str(flag) + Net::int4str(@max_allowed_packet) +
143
+ ([8] + Array.new(23, 0)).pack("c24") + (user||"")+"\0"+
144
+ scramble41(passwd, @scramble_buff)
145
+ end
146
+
147
+ if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0
148
+ data << "\0" if @pre_411
149
+ data << db
127
150
  @db = db.dup
128
151
  end
129
152
  write data
130
- read
153
+ pkt = read
154
+ handle_auth_fallback(pkt, passwd)
155
+ ObjectSpace.define_finalizer(self, Mysql.finalizer(@net))
131
156
  self
132
157
  end
133
158
  alias :connect :real_connect
134
159
 
160
+ def handle_auth_fallback(pkt, passwd)
161
+ # A packet like this means that we need to send an old-format password
162
+ if pkt.size == 1 and pkt[0] == 254 and
163
+ @server_capabilities & CLIENT_SECURE_CONNECTION != 0 then
164
+ data = scramble(passwd, @scramble_buff, @protocol_version == 9)
165
+ write data + "\0"
166
+ read
167
+ end
168
+ end
169
+
135
170
  def escape_string(str)
136
171
  Mysql::escape_string str
137
172
  end
@@ -182,8 +217,13 @@ class Mysql
182
217
  end
183
218
 
184
219
  def change_user(user="", passwd="", db="")
185
- data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
186
- command COM_CHANGE_USER, data
220
+ if @pre_411
221
+ data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
222
+ else
223
+ data = user+"\0"+scramble41(passwd, @scramble_buff)+db
224
+ end
225
+ pkt = command COM_CHANGE_USER, data
226
+ handle_auth_fallback(pkt, passwd)
187
227
  @user = user
188
228
  @passwd = passwd
189
229
  @db = db
@@ -243,7 +283,11 @@ class Mysql
243
283
 
244
284
  def list_fields(table, field=nil)
245
285
  command COM_FIELD_LIST, "#{table}\0#{field}", true
246
- f = read_rows 6
286
+ if @pre_411
287
+ f = read_rows 6
288
+ else
289
+ f = read_rows 7
290
+ end
247
291
  fields = unpack_fields(f, @server_capabilities & CLIENT_LONG_FLAG != 0)
248
292
  res = Result::new self, fields, f.length
249
293
  res.eof = true
@@ -253,7 +297,11 @@ class Mysql
253
297
  def list_processes()
254
298
  data = command COM_PROCESS_INFO
255
299
  @field_count = get_length data
256
- fields = read_rows 5
300
+ if @pre_411
301
+ fields = read_rows 5
302
+ else
303
+ fields = read_rows 7
304
+ end
257
305
  @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
258
306
  @status = :STATUS_GET_RESULT
259
307
  store_result
@@ -311,7 +359,11 @@ class Mysql
311
359
 
312
360
  def read_one_row(field_count)
313
361
  data = read
314
- return if data[0] == 254 and data.length == 1
362
+ if data[0] == 254 and data.length == 1 ## EOF
363
+ return
364
+ elsif data[0] == 254 and data.length == 5
365
+ return
366
+ end
315
367
  rec = []
316
368
  field_count.times do
317
369
  len = get_length data
@@ -363,7 +415,11 @@ class Mysql
363
415
  end
364
416
  else
365
417
  @extra_info = get_length(data, true)
366
- fields = read_rows 5
418
+ if @pre_411
419
+ fields = read_rows(5)
420
+ else
421
+ fields = read_rows(7)
422
+ end
367
423
  @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
368
424
  @status = :STATUS_GET_RESULT
369
425
  end
@@ -373,19 +429,34 @@ class Mysql
373
429
  def unpack_fields(data, long_flag_protocol)
374
430
  ret = []
375
431
  data.each do |f|
376
- table = org_table = f[0]
377
- name = f[1]
378
- length = f[2][0]+f[2][1]*256+f[2][2]*256*256
379
- type = f[3][0]
380
- if long_flag_protocol then
381
- flags = f[4][0]+f[4][1]*256
382
- decimals = f[4][2]
432
+ if @pre_411
433
+ table = org_table = f[0]
434
+ name = f[1]
435
+ length = f[2][0]+f[2][1]*256+f[2][2]*256*256
436
+ type = f[3][0]
437
+ if long_flag_protocol then
438
+ flags = f[4][0]+f[4][1]*256
439
+ decimals = f[4][2]
440
+ else
441
+ flags = f[4][0]
442
+ decimals = f[4][1]
443
+ end
444
+ def_value = f[5]
445
+ max_length = 0
383
446
  else
384
- flags = f[4][0]
385
- decimals = f[4][1]
447
+ catalog = f[0]
448
+ db = f[1]
449
+ table = f[2]
450
+ org_table = f[3]
451
+ name = f[4]
452
+ org_name = f[5]
453
+ length = f[6][2]+f[6][3]*256+f[6][4]*256*256
454
+ type = f[6][6]
455
+ flags = f[6][7]+f[6][8]*256
456
+ decimals = f[6][9]
457
+ def_value = ""
458
+ max_length = 0
386
459
  end
387
- def_value = f[5]
388
- max_length = 0
389
460
  ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
390
461
  end
391
462
  ret
@@ -478,10 +549,10 @@ class Mysql
478
549
  return "" if password == nil or password == ""
479
550
  raise "old version password is not implemented" if old_ver
480
551
  hash_pass = hash_password password
481
- hash_message = hash_password message
552
+ hash_message = hash_password message.slice(0,SCRAMBLE_LENGTH_323)
482
553
  rnd = Random::new hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]
483
554
  to = []
484
- 1.upto(message.length) do
555
+ 1.upto(SCRAMBLE_LENGTH_323) do
485
556
  to << ((rnd.rnd*31)+64).floor
486
557
  end
487
558
  extra = (rnd.rnd*31).floor
@@ -489,6 +560,16 @@ class Mysql
489
560
  to.join
490
561
  end
491
562
 
563
+ def scramble41(password, message)
564
+ return 0x00.chr if password.nil? or password.empty?
565
+ buf = [0x14]
566
+ s1 = Digest::SHA1.digest(password)
567
+ s2 = Digest::SHA1.digest(s1)
568
+ x = Digest::SHA1.digest(message + s2)
569
+ (0..s1.length - 1).each {|i| buf.push(s1[i] ^ x[i])}
570
+ buf.pack("C*")
571
+ end
572
+
492
573
  def error(errno)
493
574
  @errno = errno
494
575
  @error = Error::err errno
@@ -574,7 +655,6 @@ class Mysql
574
655
  def free()
575
656
  @handle.skip_result
576
657
  @handle = @fields = @data = nil
577
- GC::start
578
658
  end
579
659
 
580
660
  def num_fields()
@@ -1022,6 +1102,9 @@ class Mysql
1022
1102
  end
1023
1103
  @sock.sync = true
1024
1104
  buf.join
1105
+ rescue
1106
+ errno = Error::CR_SERVER_LOST
1107
+ raise Error::new(errno, Error::err(errno))
1025
1108
  end
1026
1109
 
1027
1110
  def write(data)
@@ -1039,6 +1122,9 @@ class Mysql
1039
1122
  @pkt_nr = @pkt_nr + 1 & 0xff
1040
1123
  @sock.sync = true
1041
1124
  @sock.flush
1125
+ rescue
1126
+ errno = Error::CR_SERVER_LOST
1127
+ raise Error::new(errno, Error::err(errno))
1042
1128
  end
1043
1129
 
1044
1130
  def close()
@@ -1085,13 +1171,24 @@ class << Mysql
1085
1171
  end
1086
1172
  alias :connect :real_connect
1087
1173
 
1174
+ def finalizer(net)
1175
+ proc {
1176
+ net.clear
1177
+ begin
1178
+ net.write(Mysql::COM_QUIT.chr)
1179
+ net.close
1180
+ rescue # Ignore IOError if socket is already closed.
1181
+ end
1182
+ }
1183
+ end
1184
+
1088
1185
  def escape_string(str)
1089
1186
  str.gsub(/([\0\n\r\032\'\"\\])/) do
1090
1187
  case $1
1091
1188
  when "\0" then "\\0"
1092
1189
  when "\n" then "\\n"
1093
1190
  when "\r" then "\\r"
1094
- when "\032" then "\Z"
1191
+ when "\032" then "\\Z"
1095
1192
  else "\\"+$1
1096
1193
  end
1097
1194
  end
@@ -0,0 +1,9 @@
1
+ module ActiveRecord
2
+ module VERSION #:nodoc:
3
+ MAJOR = 2
4
+ MINOR = 0
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end