activerecord_authorails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. data/CHANGELOG +3043 -0
  2. data/README +360 -0
  3. data/RUNNING_UNIT_TESTS +64 -0
  4. data/Rakefile +226 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/associations.rb +87 -0
  7. data/examples/shared_setup.rb +15 -0
  8. data/examples/validation.rb +85 -0
  9. data/install.rb +30 -0
  10. data/lib/active_record.rb +85 -0
  11. data/lib/active_record/acts/list.rb +244 -0
  12. data/lib/active_record/acts/nested_set.rb +211 -0
  13. data/lib/active_record/acts/tree.rb +89 -0
  14. data/lib/active_record/aggregations.rb +191 -0
  15. data/lib/active_record/associations.rb +1637 -0
  16. data/lib/active_record/associations/association_collection.rb +190 -0
  17. data/lib/active_record/associations/association_proxy.rb +158 -0
  18. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  19. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  20. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
  21. data/lib/active_record/associations/has_many_association.rb +210 -0
  22. data/lib/active_record/associations/has_many_through_association.rb +247 -0
  23. data/lib/active_record/associations/has_one_association.rb +80 -0
  24. data/lib/active_record/attribute_methods.rb +75 -0
  25. data/lib/active_record/base.rb +2164 -0
  26. data/lib/active_record/calculations.rb +270 -0
  27. data/lib/active_record/callbacks.rb +367 -0
  28. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
  34. data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
  35. data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
  36. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
  38. data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
  39. data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
  40. data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
  42. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
  43. data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
  44. data/lib/active_record/deprecated_associations.rb +104 -0
  45. data/lib/active_record/deprecated_finders.rb +44 -0
  46. data/lib/active_record/fixtures.rb +628 -0
  47. data/lib/active_record/locking/optimistic.rb +106 -0
  48. data/lib/active_record/locking/pessimistic.rb +77 -0
  49. data/lib/active_record/migration.rb +394 -0
  50. data/lib/active_record/observer.rb +178 -0
  51. data/lib/active_record/query_cache.rb +64 -0
  52. data/lib/active_record/reflection.rb +222 -0
  53. data/lib/active_record/schema.rb +58 -0
  54. data/lib/active_record/schema_dumper.rb +149 -0
  55. data/lib/active_record/timestamp.rb +51 -0
  56. data/lib/active_record/transactions.rb +136 -0
  57. data/lib/active_record/validations.rb +843 -0
  58. data/lib/active_record/vendor/db2.rb +362 -0
  59. data/lib/active_record/vendor/mysql.rb +1214 -0
  60. data/lib/active_record/vendor/simple.rb +693 -0
  61. data/lib/active_record/version.rb +9 -0
  62. data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
  63. data/lib/active_record/wrappings.rb +58 -0
  64. data/lib/active_record/xml_serialization.rb +308 -0
  65. data/test/aaa_create_tables_test.rb +59 -0
  66. data/test/abstract_unit.rb +77 -0
  67. data/test/active_schema_test_mysql.rb +31 -0
  68. data/test/adapter_test.rb +87 -0
  69. data/test/adapter_test_sqlserver.rb +81 -0
  70. data/test/aggregations_test.rb +95 -0
  71. data/test/all.sh +8 -0
  72. data/test/ar_schema_test.rb +33 -0
  73. data/test/association_inheritance_reload.rb +14 -0
  74. data/test/associations/callbacks_test.rb +126 -0
  75. data/test/associations/cascaded_eager_loading_test.rb +138 -0
  76. data/test/associations/eager_test.rb +393 -0
  77. data/test/associations/extension_test.rb +42 -0
  78. data/test/associations/join_model_test.rb +497 -0
  79. data/test/associations_test.rb +1809 -0
  80. data/test/attribute_methods_test.rb +49 -0
  81. data/test/base_test.rb +1586 -0
  82. data/test/binary_test.rb +37 -0
  83. data/test/calculations_test.rb +219 -0
  84. data/test/callbacks_test.rb +377 -0
  85. data/test/class_inheritable_attributes_test.rb +32 -0
  86. data/test/column_alias_test.rb +17 -0
  87. data/test/connection_test_firebird.rb +8 -0
  88. data/test/connections/native_db2/connection.rb +25 -0
  89. data/test/connections/native_firebird/connection.rb +26 -0
  90. data/test/connections/native_frontbase/connection.rb +27 -0
  91. data/test/connections/native_mysql/connection.rb +24 -0
  92. data/test/connections/native_openbase/connection.rb +21 -0
  93. data/test/connections/native_oracle/connection.rb +27 -0
  94. data/test/connections/native_postgresql/connection.rb +23 -0
  95. data/test/connections/native_sqlite/connection.rb +34 -0
  96. data/test/connections/native_sqlite3/connection.rb +34 -0
  97. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  98. data/test/connections/native_sqlserver/connection.rb +23 -0
  99. data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
  100. data/test/connections/native_sybase/connection.rb +23 -0
  101. data/test/copy_table_sqlite.rb +64 -0
  102. data/test/datatype_test_postgresql.rb +52 -0
  103. data/test/default_test_firebird.rb +16 -0
  104. data/test/defaults_test.rb +60 -0
  105. data/test/deprecated_associations_test.rb +396 -0
  106. data/test/deprecated_finder_test.rb +151 -0
  107. data/test/empty_date_time_test.rb +25 -0
  108. data/test/finder_test.rb +504 -0
  109. data/test/fixtures/accounts.yml +28 -0
  110. data/test/fixtures/author.rb +99 -0
  111. data/test/fixtures/author_favorites.yml +4 -0
  112. data/test/fixtures/authors.yml +7 -0
  113. data/test/fixtures/auto_id.rb +4 -0
  114. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  115. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  116. data/test/fixtures/bad_fixtures/blank_line +3 -0
  117. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  118. data/test/fixtures/bad_fixtures/missing_value +1 -0
  119. data/test/fixtures/binary.rb +2 -0
  120. data/test/fixtures/categories.yml +14 -0
  121. data/test/fixtures/categories/special_categories.yml +9 -0
  122. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  123. data/test/fixtures/categories_ordered.yml +7 -0
  124. data/test/fixtures/categories_posts.yml +23 -0
  125. data/test/fixtures/categorization.rb +5 -0
  126. data/test/fixtures/categorizations.yml +17 -0
  127. data/test/fixtures/category.rb +20 -0
  128. data/test/fixtures/column_name.rb +3 -0
  129. data/test/fixtures/comment.rb +23 -0
  130. data/test/fixtures/comments.yml +59 -0
  131. data/test/fixtures/companies.yml +55 -0
  132. data/test/fixtures/company.rb +107 -0
  133. data/test/fixtures/company_in_module.rb +59 -0
  134. data/test/fixtures/computer.rb +3 -0
  135. data/test/fixtures/computers.yml +4 -0
  136. data/test/fixtures/course.rb +3 -0
  137. data/test/fixtures/courses.yml +7 -0
  138. data/test/fixtures/customer.rb +55 -0
  139. data/test/fixtures/customers.yml +17 -0
  140. data/test/fixtures/db_definitions/db2.drop.sql +32 -0
  141. data/test/fixtures/db_definitions/db2.sql +231 -0
  142. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  143. data/test/fixtures/db_definitions/db22.sql +5 -0
  144. data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
  145. data/test/fixtures/db_definitions/firebird.sql +304 -0
  146. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  148. data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
  149. data/test/fixtures/db_definitions/frontbase.sql +268 -0
  150. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  151. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  152. data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
  153. data/test/fixtures/db_definitions/mysql.sql +234 -0
  154. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  156. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  157. data/test/fixtures/db_definitions/openbase.sql +302 -0
  158. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  159. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  160. data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
  161. data/test/fixtures/db_definitions/oracle.sql +325 -0
  162. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  163. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  164. data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
  165. data/test/fixtures/db_definitions/postgresql.sql +263 -0
  166. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  167. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  168. data/test/fixtures/db_definitions/schema.rb +60 -0
  169. data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
  170. data/test/fixtures/db_definitions/sqlite.sql +215 -0
  171. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  172. data/test/fixtures/db_definitions/sqlite2.sql +5 -0
  173. data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
  174. data/test/fixtures/db_definitions/sqlserver.sql +243 -0
  175. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  176. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  177. data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
  178. data/test/fixtures/db_definitions/sybase.sql +218 -0
  179. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  180. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  181. data/test/fixtures/default.rb +2 -0
  182. data/test/fixtures/developer.rb +52 -0
  183. data/test/fixtures/developers.yml +21 -0
  184. data/test/fixtures/developers_projects.yml +17 -0
  185. data/test/fixtures/developers_projects/david_action_controller +3 -0
  186. data/test/fixtures/developers_projects/david_active_record +3 -0
  187. data/test/fixtures/developers_projects/jamis_active_record +2 -0
  188. data/test/fixtures/edge.rb +5 -0
  189. data/test/fixtures/edges.yml +6 -0
  190. data/test/fixtures/entrant.rb +3 -0
  191. data/test/fixtures/entrants.yml +14 -0
  192. data/test/fixtures/fk_test_has_fk.yml +3 -0
  193. data/test/fixtures/fk_test_has_pk.yml +2 -0
  194. data/test/fixtures/flowers.jpg +0 -0
  195. data/test/fixtures/funny_jokes.yml +10 -0
  196. data/test/fixtures/joke.rb +6 -0
  197. data/test/fixtures/keyboard.rb +3 -0
  198. data/test/fixtures/legacy_thing.rb +3 -0
  199. data/test/fixtures/legacy_things.yml +3 -0
  200. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  201. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  202. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  203. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  204. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  205. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  206. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  207. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  208. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  209. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  210. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  211. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  212. data/test/fixtures/mixed_case_monkey.rb +3 -0
  213. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  214. data/test/fixtures/mixin.rb +63 -0
  215. data/test/fixtures/mixins.yml +127 -0
  216. data/test/fixtures/movie.rb +5 -0
  217. data/test/fixtures/movies.yml +7 -0
  218. data/test/fixtures/naked/csv/accounts.csv +1 -0
  219. data/test/fixtures/naked/yml/accounts.yml +1 -0
  220. data/test/fixtures/naked/yml/companies.yml +1 -0
  221. data/test/fixtures/naked/yml/courses.yml +1 -0
  222. data/test/fixtures/order.rb +4 -0
  223. data/test/fixtures/people.yml +3 -0
  224. data/test/fixtures/person.rb +4 -0
  225. data/test/fixtures/post.rb +58 -0
  226. data/test/fixtures/posts.yml +48 -0
  227. data/test/fixtures/project.rb +27 -0
  228. data/test/fixtures/projects.yml +7 -0
  229. data/test/fixtures/reader.rb +4 -0
  230. data/test/fixtures/readers.yml +4 -0
  231. data/test/fixtures/reply.rb +37 -0
  232. data/test/fixtures/subject.rb +4 -0
  233. data/test/fixtures/subscriber.rb +6 -0
  234. data/test/fixtures/subscribers/first +2 -0
  235. data/test/fixtures/subscribers/second +2 -0
  236. data/test/fixtures/tag.rb +7 -0
  237. data/test/fixtures/tagging.rb +6 -0
  238. data/test/fixtures/taggings.yml +18 -0
  239. data/test/fixtures/tags.yml +7 -0
  240. data/test/fixtures/task.rb +3 -0
  241. data/test/fixtures/tasks.yml +7 -0
  242. data/test/fixtures/topic.rb +25 -0
  243. data/test/fixtures/topics.yml +22 -0
  244. data/test/fixtures/vertex.rb +9 -0
  245. data/test/fixtures/vertices.yml +4 -0
  246. data/test/fixtures_test.rb +401 -0
  247. data/test/inheritance_test.rb +205 -0
  248. data/test/lifecycle_test.rb +137 -0
  249. data/test/locking_test.rb +190 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +768 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_nested_set_test.rb +196 -0
  254. data/test/mixin_test.rb +550 -0
  255. data/test/modules_test.rb +34 -0
  256. data/test/multiple_db_test.rb +60 -0
  257. data/test/pk_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +159 -0
  260. data/test/schema_authorization_test_postgresql.rb +75 -0
  261. data/test/schema_dumper_test.rb +96 -0
  262. data/test/schema_test_postgresql.rb +64 -0
  263. data/test/synonym_test_oracle.rb +17 -0
  264. data/test/table_name_test_sqlserver.rb +23 -0
  265. data/test/threaded_connections_test.rb +48 -0
  266. data/test/transactions_test.rb +230 -0
  267. data/test/unconnected_test.rb +32 -0
  268. data/test/validations_test.rb +1097 -0
  269. data/test/xml_serialization_test.rb +125 -0
  270. metadata +365 -0
@@ -0,0 +1,861 @@
1
+ # Requires FrontBase Ruby bindings (gem install ruby-frontbase)
2
+
3
+ require 'active_record/connection_adapters/abstract_adapter'
4
+
5
+ FB_TRACE = false
6
+
7
+ module ActiveRecord
8
+
9
+ class Base
10
+ class << self
11
+ # Establishes a connection to the database that's used by all Active Record objects.
12
+ def frontbase_connection(config) # :nodoc:
13
+ # FrontBase only supports one unnamed sequence per table
14
+ define_attr_method(:set_sequence_name, :sequence_name, &Proc.new {|*args| nil})
15
+
16
+ config = config.symbolize_keys
17
+ database = config[:database]
18
+ port = config[:port]
19
+ host = config[:host]
20
+ username = config[:username]
21
+ password = config[:password]
22
+ dbpassword = config[:dbpassword]
23
+ session_name = config[:session_name]
24
+
25
+ dbpassword = '' if dbpassword.nil?
26
+
27
+ # Turn off colorization since it makes tail/less output difficult
28
+ self.colorize_logging = false
29
+
30
+ require_library_or_gem 'frontbase' unless self.class.const_defined? :FBSQL_Connect
31
+
32
+ # Check bindings version
33
+ version = "0.0.0"
34
+ version = FBSQL_Connect::FB_BINDINGS_VERSION if defined? FBSQL_Connect::FB_BINDINGS_VERSION
35
+
36
+ if ActiveRecord::ConnectionAdapters::FrontBaseAdapter.compare_versions(version,"1.0.0") == -1
37
+ raise AdapterNotFound,
38
+ 'The FrontBase adapter requires ruby-frontbase version 1.0.0 or greater; you appear ' <<
39
+ "to be running an older version (#{version}) -- please update ruby-frontbase (gem install ruby-frontbase)."
40
+ end
41
+ connection = FBSQL_Connect.connect(host, port, database, username, password, dbpassword, session_name)
42
+ ConnectionAdapters::FrontBaseAdapter.new(connection, logger, [host, port, database, username, password, dbpassword, session_name], config)
43
+ end
44
+ end
45
+ end
46
+
47
+ module ConnectionAdapters
48
+
49
+ # From EOF Documentation....
50
+ # buffer should have space for EOUniqueBinaryKeyLength (12) bytes.
51
+ # Assigns a world-wide unique ID made up of:
52
+ # < Sequence [2], ProcessID [2] , Time [4], IP Addr [4] >
53
+
54
+ class TwelveByteKey < String #:nodoc:
55
+ @@mutex = Mutex.new
56
+ @@sequence_number = rand(65536)
57
+ @@key_cached_pid_component = nil
58
+ @@key_cached_ip_component = nil
59
+
60
+ def initialize(string = nil)
61
+ # Generate a unique key
62
+ if string.nil?
63
+ new_key = replace('_' * 12)
64
+
65
+ new_key[0..1] = self.class.key_sequence_component
66
+ new_key[2..3] = self.class.key_pid_component
67
+ new_key[4..7] = self.class.key_time_component
68
+ new_key[8..11] = self.class.key_ip_component
69
+ new_key
70
+ else
71
+ if string.size == 24
72
+ string.gsub!(/[[:xdigit:]]{2}/) { |x| x.hex.chr }
73
+ end
74
+ raise "string is not 12 bytes long" unless string.size == 12
75
+ super(string)
76
+ end
77
+ end
78
+
79
+ def inspect
80
+ unpack("H*").first.upcase
81
+ end
82
+
83
+ alias_method :to_s, :inspect
84
+
85
+ private
86
+
87
+ class << self
88
+ def key_sequence_component
89
+ seq = nil
90
+ @@mutex.synchronize do
91
+ seq = @@sequence_number
92
+ @@sequence_number = (@@sequence_number + 1) % 65536
93
+ end
94
+
95
+ sequence_component = "__"
96
+ sequence_component[0] = seq >> 8
97
+ sequence_component[1] = seq
98
+ sequence_component
99
+ end
100
+
101
+ def key_pid_component
102
+ if @@key_cached_pid_component.nil?
103
+ @@mutex.synchronize do
104
+ pid = $$
105
+ pid_component = "__"
106
+ pid_component[0] = pid >> 8
107
+ pid_component[1] = pid
108
+ @@key_cached_pid_component = pid_component
109
+ end
110
+ end
111
+ @@key_cached_pid_component
112
+ end
113
+
114
+ def key_time_component
115
+ time = Time.new.to_i
116
+ time_component = "____"
117
+ time_component[0] = (time & 0xFF000000) >> 24
118
+ time_component[1] = (time & 0x00FF0000) >> 16
119
+ time_component[2] = (time & 0x0000FF00) >> 8
120
+ time_component[3] = (time & 0x000000FF)
121
+ time_component
122
+ end
123
+
124
+ def key_ip_component
125
+ if @@key_cached_ip_component.nil?
126
+ @@mutex.synchronize do
127
+ old_lookup_flag = BasicSocket.do_not_reverse_lookup
128
+ BasicSocket.do_not_reverse_lookup = true
129
+ udpsocket = UDPSocket.new
130
+ udpsocket.connect("17.112.152.32",1)
131
+ ip_string = udpsocket.addr[3]
132
+ BasicSocket.do_not_reverse_lookup = old_lookup_flag
133
+ packed = Socket.pack_sockaddr_in(0,ip_string)
134
+ addr_subset = packed[4..7]
135
+ ip = addr_subset[0] << 24 | addr_subset[1] << 16 | addr_subset[2] << 8 | addr_subset[3]
136
+ ip_component = "____"
137
+ ip_component[0] = (ip & 0xFF000000) >> 24
138
+ ip_component[1] = (ip & 0x00FF0000) >> 16
139
+ ip_component[2] = (ip & 0x0000FF00) >> 8
140
+ ip_component[3] = (ip & 0x000000FF)
141
+ @@key_cached_ip_component = ip_component
142
+ end
143
+ end
144
+ @@key_cached_ip_component
145
+ end
146
+ end
147
+ end
148
+
149
+ class FrontBaseColumn < Column #:nodoc:
150
+ attr_reader :fb_autogen
151
+
152
+ def initialize(base, name, type, typename, limit, precision, scale, default, nullable)
153
+
154
+ @base = base
155
+ @name = name
156
+ @type = simplified_type(type,typename,limit)
157
+ @limit = limit
158
+ @precision = precision
159
+ @scale = scale
160
+ @default = default
161
+ @null = nullable == "YES"
162
+ @text = [:string, :text].include? @type
163
+ @number = [:float, :integer, :decimal].include? @type
164
+ @fb_autogen = false
165
+
166
+ if @default
167
+ @default.gsub!(/^'(.*)'$/,'\1') if @text
168
+ @fb_autogen = @default.include?("SELECT UNIQUE FROM")
169
+ case @type
170
+ when :boolean
171
+ @default = @default == "TRUE"
172
+ when :binary
173
+ if @default != "X''"
174
+ buffer = ""
175
+ @default.scan(/../) { |h| buffer << h.hex.chr }
176
+ @default = buffer
177
+ else
178
+ @default = ""
179
+ end
180
+ else
181
+ @default = type_cast(@default)
182
+ end
183
+ end
184
+ end
185
+
186
+ # Casts value (which is a String) to an appropriate instance.
187
+ def type_cast(value)
188
+ if type == :twelvebytekey
189
+ ActiveRecord::ConnectionAdapters::TwelveByteKey.new(value)
190
+ else
191
+ super(value)
192
+ end
193
+ end
194
+
195
+ def type_cast_code(var_name)
196
+ if type == :twelvebytekey
197
+ "ActiveRecord::ConnectionAdapters::TwelveByteKey.new(#{var_name})"
198
+ else
199
+ super(var_name)
200
+ end
201
+ end
202
+
203
+ private
204
+ def simplified_type(field_type, type_name,limit)
205
+ ret_type = :string
206
+ puts "typecode: [#{field_type}] [#{type_name}]" if FB_TRACE
207
+
208
+ # 12 byte primary keys are a special case that Apple's EOF
209
+ # used heavily. Optimize for this case
210
+ if field_type == 11 && limit == 96
211
+ ret_type = :twelvebytekey # BIT(96)
212
+ else
213
+ ret_type = case field_type
214
+ when 1 then :boolean # BOOLEAN
215
+ when 2 then :integer # INTEGER
216
+ when 4 then :float # FLOAT
217
+ when 10 then :string # CHARACTER VARYING
218
+ when 11 then :bitfield # BIT
219
+ when 13 then :date # DATE
220
+ when 14 then :time # TIME
221
+ when 16 then :timestamp # TIMESTAMP
222
+ when 20 then :text # CLOB
223
+ when 21 then :binary # BLOB
224
+ when 22 then :integer # TINYINT
225
+ else
226
+ puts "ERROR: Unknown typecode: [#{field_type}] [#{type_name}]"
227
+ end
228
+ end
229
+ puts "ret_type: #{ret_type.inspect}" if FB_TRACE
230
+ ret_type
231
+ end
232
+ end
233
+
234
+ class FrontBaseAdapter < AbstractAdapter
235
+
236
+ class << self
237
+ def compare_versions(v1, v2)
238
+ v1_seg = v1.split(".")
239
+ v2_seg = v2.split(".")
240
+ 0.upto([v1_seg.length,v2_seg.length].min) do |i|
241
+ step = (v1_seg[i].to_i <=> v2_seg[i].to_i)
242
+ return step unless step == 0
243
+ end
244
+ return v1_seg.length <=> v2_seg.length
245
+ end
246
+ end
247
+
248
+ def initialize(connection, logger, connection_options, config)
249
+ super(connection, logger)
250
+ @connection_options, @config = connection_options, config
251
+ @transaction_mode = :pessimistic
252
+
253
+ # Start out in auto-commit mode
254
+ self.rollback_db_transaction
255
+
256
+ # threaded_connections_test.rb will fail unless we set the session
257
+ # to optimistic locking mode
258
+ # set_pessimistic_transactions
259
+ # execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
260
+ end
261
+
262
+ # Returns the human-readable name of the adapter. Use mixed case - one
263
+ # can always use downcase if needed.
264
+ def adapter_name #:nodoc:
265
+ 'FrontBase'
266
+ end
267
+
268
+ # Does this adapter support migrations? Backend specific, as the
269
+ # abstract adapter always returns +false+.
270
+ def supports_migrations? #:nodoc:
271
+ true
272
+ end
273
+
274
+ def native_database_types #:nodoc:
275
+ {
276
+ :primary_key => "INTEGER DEFAULT UNIQUE PRIMARY KEY",
277
+ :string => { :name => "VARCHAR", :limit => 255 },
278
+ :text => { :name => "CLOB" },
279
+ :integer => { :name => "INTEGER" },
280
+ :float => { :name => "FLOAT" },
281
+ :decimal => { :name => "DECIMAL" },
282
+ :datetime => { :name => "TIMESTAMP" },
283
+ :timestamp => { :name => "TIMESTAMP" },
284
+ :time => { :name => "TIME" },
285
+ :date => { :name => "DATE" },
286
+ :binary => { :name => "BLOB" },
287
+ :boolean => { :name => "BOOLEAN" },
288
+ :twelvebytekey => { :name => "BYTE", :limit => 12}
289
+ }
290
+ end
291
+
292
+
293
+ # QUOTING ==================================================
294
+
295
+ # Quotes the column value to help prevent
296
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
297
+ def quote(value, column = nil)
298
+ return value.quoted_id if value.respond_to?(:quoted_id)
299
+
300
+ retvalue = "<INVALID>"
301
+
302
+ puts "quote(#{value.inspect}(#{value.class}),#{column.type.inspect})" if FB_TRACE
303
+ # If a column was passed in, use column type information
304
+ unless value.nil?
305
+ if column
306
+ retvalue = case column.type
307
+ when :string
308
+ if value.kind_of?(String)
309
+ "'#{quote_string(value.to_s)}'" # ' (for ruby-mode)
310
+ else
311
+ "'#{quote_string(value.to_yaml)}'"
312
+ end
313
+ when :integer
314
+ if value.kind_of?(TrueClass)
315
+ '1'
316
+ elsif value.kind_of?(FalseClass)
317
+ '0'
318
+ else
319
+ value.to_i.to_s
320
+ end
321
+ when :float
322
+ value.to_f.to_s
323
+ when :decimal
324
+ value.to_d.to_s("F")
325
+ when :datetime, :timestamp
326
+ "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
327
+ when :time
328
+ "TIME '#{value.strftime("%H:%M:%S")}'"
329
+ when :date
330
+ "DATE '#{value.strftime("%Y-%m-%d")}'"
331
+ when :twelvebytekey
332
+ value = value.to_s.unpack("H*").first unless value.kind_of?(TwelveByteKey)
333
+ "X'#{value.to_s}'"
334
+ when :boolean
335
+ value = quoted_true if value.kind_of?(TrueClass)
336
+ value = quoted_false if value.kind_of?(FalseClass)
337
+ value
338
+ when :binary
339
+ blob_handle = @connection.create_blob(value.to_s)
340
+ puts "SQL -> Insert #{value.to_s.length} byte blob as #{retvalue}" if FB_TRACE
341
+ blob_handle.handle
342
+ when :text
343
+ if value.kind_of?(String)
344
+ clobdata = value.to_s # ' (for ruby-mode)
345
+ else
346
+ clobdata = value.to_yaml
347
+ end
348
+ clob_handle = @connection.create_clob(clobdata)
349
+ puts "SQL -> Insert #{value.to_s.length} byte clob as #{retvalue}" if FB_TRACE
350
+ clob_handle.handle
351
+ else
352
+ raise "*** UNKNOWN TYPE: #{column.type.inspect}"
353
+ end # case
354
+ # Since we don't have column type info, make a best guess based
355
+ # on the Ruby class of the value
356
+ else
357
+ retvalue = case value
358
+ when ActiveRecord::ConnectionAdapters::TwelveByteKey
359
+ s = value.unpack("H*").first
360
+ "X'#{s}'"
361
+ when String
362
+ if column && column.type == :binary
363
+ s = value.unpack("H*").first
364
+ "X'#{s}'"
365
+ elsif column && [:integer, :float, :decimal].include?(column.type)
366
+ value.to_s
367
+ else
368
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
369
+ end
370
+ when NilClass
371
+ "NULL"
372
+ when TrueClass
373
+ (column && column.type == :integer ? '1' : quoted_true)
374
+ when FalseClass
375
+ (column && column.type == :integer ? '0' : quoted_false)
376
+ when Float, Fixnum, Bignum, BigDecimal
377
+ value.to_s
378
+ when Time, Date, DateTime
379
+ if column
380
+ case column.type
381
+ when :date
382
+ "DATE '#{value.strftime("%Y-%m-%d")}'"
383
+ when :time
384
+ "TIME '#{value.strftime("%H:%M:%S")}'"
385
+ when :timestamp
386
+ "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
387
+ else
388
+ raise NotImplementedError, "Unknown column type!"
389
+ end # case
390
+ else # Column wasn't passed in, so try to guess the right type
391
+ if value.kind_of? Date
392
+ "DATE '#{value.strftime("%Y-%m-%d")}'"
393
+ else
394
+ if [:hour, :min, :sec].all? {|part| value.send(:part).zero? }
395
+ "TIME '#{value.strftime("%H:%M:%S")}'"
396
+ else
397
+ "TIMESTAMP '#{quoted_date(value)}'"
398
+ end
399
+ end
400
+ end #if column
401
+ else
402
+ "'#{quote_string(value.to_yaml)}'"
403
+ end #case
404
+ end
405
+ else
406
+ retvalue = "NULL"
407
+ end
408
+
409
+ retvalue
410
+ end # def
411
+
412
+ # Quotes a string, escaping any ' (single quote) characters.
413
+ def quote_string(s)
414
+ s.gsub(/'/, "''") # ' (for ruby-mode)
415
+ end
416
+
417
+ def quote_column_name(name) #:nodoc:
418
+ %( "#{name}" )
419
+ end
420
+
421
+ def quoted_true
422
+ "true"
423
+ end
424
+
425
+ def quoted_false
426
+ "false"
427
+ end
428
+
429
+
430
+ # CONNECTION MANAGEMENT ====================================
431
+
432
+ def active?
433
+ true if @connection.status == 1
434
+ rescue => e
435
+ false
436
+ end
437
+
438
+ def reconnect!
439
+ @connection.close rescue nil
440
+ @connection = FBSQL_Connect.connect(*@connection_options.first(7))
441
+ end
442
+
443
+ # Close this connection
444
+ def disconnect!
445
+ @connection.close rescue nil
446
+ @active = false
447
+ end
448
+
449
+ # DATABASE STATEMENTS ======================================
450
+
451
+ # Returns an array of record hashes with the column names as keys and
452
+ # column values as values.
453
+ def select_all(sql, name = nil) #:nodoc:
454
+ fbsql = cleanup_fb_sql(sql)
455
+ return_value = []
456
+ fbresult = execute(sql, name)
457
+ puts "select_all SQL -> #{fbsql}" if FB_TRACE
458
+ columns = fbresult.columns
459
+
460
+ fbresult.each do |row|
461
+ puts "SQL <- #{row.inspect}" if FB_TRACE
462
+ hashed_row = {}
463
+ colnum = 0
464
+ row.each do |col|
465
+ hashed_row[columns[colnum]] = col
466
+ if col.kind_of?(FBSQL_LOB)
467
+ hashed_row[columns[colnum]] = col.read
468
+ end
469
+ colnum += 1
470
+ end
471
+ puts "raw row: #{hashed_row.inspect}" if FB_TRACE
472
+ return_value << hashed_row
473
+ end
474
+ return_value
475
+ end
476
+
477
+ def select_one(sql, name = nil) #:nodoc:
478
+ fbsql = cleanup_fb_sql(sql)
479
+ return_value = []
480
+ fbresult = execute(fbsql, name)
481
+ puts "SQL -> #{fbsql}" if FB_TRACE
482
+ columns = fbresult.columns
483
+
484
+ fbresult.each do |row|
485
+ puts "SQL <- #{row.inspect}" if FB_TRACE
486
+ hashed_row = {}
487
+ colnum = 0
488
+ row.each do |col|
489
+ hashed_row[columns[colnum]] = col
490
+ if col.kind_of?(FBSQL_LOB)
491
+ hashed_row[columns[colnum]] = col.read
492
+ end
493
+ colnum += 1
494
+ end
495
+ return_value << hashed_row
496
+ break
497
+ end
498
+ fbresult.clear
499
+ return_value.first
500
+ end
501
+
502
+ def query(sql, name = nil) #:nodoc:
503
+ fbsql = cleanup_fb_sql(sql)
504
+ puts "SQL(query) -> #{fbsql}" if FB_TRACE
505
+ log(fbsql, name) { @connection.query(fbsql) }
506
+ rescue => e
507
+ puts "FB Exception: #{e.inspect}" if FB_TRACE
508
+ raise e
509
+ end
510
+
511
+ def execute(sql, name = nil) #:nodoc:
512
+ fbsql = cleanup_fb_sql(sql)
513
+ puts "SQL(execute) -> #{fbsql}" if FB_TRACE
514
+ log(fbsql, name) { @connection.query(fbsql) }
515
+ rescue ActiveRecord::StatementInvalid => e
516
+ if e.message.scan(/Table name - \w* - exists/).empty?
517
+ puts "FB Exception: #{e.inspect}" if FB_TRACE
518
+ raise e
519
+ end
520
+ end
521
+
522
+ # Returns the last auto-generated ID from the affected table.
523
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
524
+ puts "SQL -> #{sql.inspect}" if FB_TRACE
525
+ execute(sql, name)
526
+ id_value || pk
527
+ end
528
+
529
+ # Executes the update statement and returns the number of rows affected.
530
+ def update(sql, name = nil) #:nodoc:
531
+ puts "SQL -> #{sql.inspect}" if FB_TRACE
532
+ execute(sql, name).num_rows
533
+ end
534
+
535
+ alias_method :delete, :update #:nodoc:
536
+
537
+ def set_pessimistic_transactions
538
+ if @transaction_mode == :optimistic
539
+ execute "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, LOCKING PESSIMISTIC, READ WRITE"
540
+ @transaction_mode = :pessimistic
541
+ end
542
+ end
543
+
544
+ def set_optimistic_transactions
545
+ if @transaction_mode == :pessimistic
546
+ execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
547
+ @transaction_mode = :optimistic
548
+ end
549
+ end
550
+
551
+ def begin_db_transaction #:nodoc:
552
+ execute "SET COMMIT FALSE" rescue nil
553
+ end
554
+
555
+ def commit_db_transaction #:nodoc:
556
+ execute "COMMIT"
557
+ ensure
558
+ execute "SET COMMIT TRUE"
559
+ end
560
+
561
+ def rollback_db_transaction #:nodoc:
562
+ execute "ROLLBACK"
563
+ ensure
564
+ execute "SET COMMIT TRUE"
565
+ end
566
+
567
+ def add_limit_offset!(sql, options) #:nodoc:
568
+ if limit = options[:limit]
569
+ offset = options[:offset] || 0
570
+
571
+ # Here is the full syntax FrontBase supports:
572
+ # (from gclem@frontbase.com)
573
+ #
574
+ # TOP <limit - unsigned integer>
575
+ # TOP ( <offset expr>, <limit expr>)
576
+
577
+ # "TOP 0" is not allowed, so we have
578
+ # to use a cheap trick.
579
+ if limit.zero?
580
+ case sql
581
+ when /WHERE/i
582
+ sql.sub!(/WHERE/i, 'WHERE 0 = 1 AND ')
583
+ when /ORDER\s+BY/i
584
+ sql.sub!(/ORDER\s+BY/i, 'WHERE 0 = 1 ORDER BY')
585
+ else
586
+ sql << 'WHERE 0 = 1'
587
+ end
588
+ else
589
+ if offset.zero?
590
+ sql.replace sql.gsub("SELECT ","SELECT TOP #{limit} ")
591
+ else
592
+ sql.replace sql.gsub("SELECT ","SELECT TOP(#{offset},#{limit}) ")
593
+ end
594
+ end
595
+ end
596
+ end
597
+
598
+ def prefetch_primary_key?(table_name = nil)
599
+ true
600
+ end
601
+
602
+ # Returns the next sequence value from a sequence generator. Not generally
603
+ # called directly; used by ActiveRecord to get the next primary key value
604
+ # when inserting a new database record (see #prefetch_primary_key?).
605
+ def next_sequence_value(sequence_name)
606
+ unique = select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value")
607
+ # The test cases cannot handle a zero primary key
608
+ unique.zero? ? select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value") : unique
609
+ end
610
+
611
+ def default_sequence_name(table, column)
612
+ table
613
+ end
614
+
615
+ # Set the sequence to the max value of the table's column.
616
+ def reset_sequence!(table, column, sequence = nil)
617
+ klasses = classes_for_table_name(table)
618
+ klass = klasses.nil? ? nil : klasses.first
619
+ pk = klass.primary_key unless klass.nil?
620
+ if pk && klass.columns_hash[pk].type == :integer
621
+ execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
622
+ end
623
+ end
624
+
625
+ def classes_for_table_name(table)
626
+ ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
627
+ end
628
+
629
+ def reset_pk_sequence!(table, pk = nil, sequence = nil)
630
+ klasses = classes_for_table_name(table)
631
+ klass = klasses.nil? ? nil : klasses.first
632
+ pk = klass.primary_key unless klass.nil?
633
+ if pk && klass.columns_hash[pk].type == :integer
634
+ mpk = select_value("SELECT MAX(#{pk}) FROM #{table}")
635
+ execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
636
+ end
637
+ end
638
+
639
+ # SCHEMA STATEMENTS ========================================
640
+
641
+ def structure_dump #:nodoc:
642
+ select_all("SHOW TABLES").inject('') do |structure, table|
643
+ structure << select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] << ";\n\n"
644
+ end
645
+ end
646
+
647
+ def recreate_database(name) #:nodoc:
648
+ drop_database(name)
649
+ create_database(name)
650
+ end
651
+
652
+ def create_database(name) #:nodoc:
653
+ execute "CREATE DATABASE #{name}"
654
+ end
655
+
656
+ def drop_database(name) #:nodoc:
657
+ execute "DROP DATABASE #{name}"
658
+ end
659
+
660
+ def current_database
661
+ select_value('SELECT "CATALOG_NAME" FROM INFORMATION_SCHEMA.CATALOGS').downcase
662
+ end
663
+
664
+ def tables(name = nil) #:nodoc:
665
+ select_values(<<-SQL, nil)
666
+ SELECT "TABLE_NAME"
667
+ FROM INFORMATION_SCHEMA.TABLES AS T0,
668
+ INFORMATION_SCHEMA.SCHEMATA AS T1
669
+ WHERE T0.SCHEMA_PK = T1.SCHEMA_PK
670
+ AND "SCHEMA_NAME" = CURRENT_SCHEMA
671
+ SQL
672
+ end
673
+
674
+ def indexes(table_name, name = nil)#:nodoc:
675
+ indexes = []
676
+ current_index = nil
677
+ sql = <<-SQL
678
+ SELECT INDEX_NAME, T2.ORDINAL_POSITION, INDEX_COLUMN_COUNT, INDEX_TYPE,
679
+ "COLUMN_NAME", IS_NULLABLE
680
+ FROM INFORMATION_SCHEMA.TABLES AS T0,
681
+ INFORMATION_SCHEMA.INDEXES AS T1,
682
+ INFORMATION_SCHEMA.INDEX_COLUMN_USAGE AS T2,
683
+ INFORMATION_SCHEMA.COLUMNS AS T3
684
+ WHERE T0."TABLE_NAME" = '#{table_name}'
685
+ AND INDEX_TYPE <> 0
686
+ AND T0.TABLE_PK = T1.TABLE_PK
687
+ AND T0.TABLE_PK = T2.TABLE_PK
688
+ AND T0.TABLE_PK = T3.TABLE_PK
689
+ AND T1.INDEXES_PK = T2.INDEX_PK
690
+ AND T2.COLUMN_PK = T3.COLUMN_PK
691
+ ORDER BY INDEX_NAME, T2.ORDINAL_POSITION
692
+ SQL
693
+
694
+ columns = []
695
+ query(sql).each do |row|
696
+ index_name = row[0]
697
+ ord_position = row[1]
698
+ ndx_colcount = row[2]
699
+ index_type = row[3]
700
+ column_name = row[4]
701
+
702
+ is_unique = index_type == 1
703
+
704
+ columns << column_name
705
+ if ord_position == ndx_colcount
706
+ indexes << IndexDefinition.new(table_name, index_name, is_unique , columns)
707
+ columns = []
708
+ end
709
+ end
710
+ indexes
711
+ end
712
+
713
+ def columns(table_name, name = nil)#:nodoc:
714
+ sql = <<-SQL
715
+ SELECT "TABLE_NAME", "COLUMN_NAME", ORDINAL_POSITION, IS_NULLABLE, COLUMN_DEFAULT,
716
+ DATA_TYPE, DATA_TYPE_CODE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION,
717
+ NUMERIC_PRECISION_RADIX, NUMERIC_SCALE, DATETIME_PRECISION, DATETIME_PRECISION_LEADING
718
+ FROM INFORMATION_SCHEMA.TABLES T0,
719
+ INFORMATION_SCHEMA.COLUMNS T1,
720
+ INFORMATION_SCHEMA.DATA_TYPE_DESCRIPTOR T3
721
+ WHERE "TABLE_NAME" = '#{table_name}'
722
+ AND T0.TABLE_PK = T1.TABLE_PK
723
+ AND T0.TABLE_PK = T3.TABLE_OR_DOMAIN_PK
724
+ AND T1.COLUMN_PK = T3.COLUMN_NAME_PK
725
+ ORDER BY T1.ORDINAL_POSITION
726
+ SQL
727
+
728
+ rawresults = query(sql,name)
729
+ columns = []
730
+ rawresults.each do |field|
731
+ args = [base = field[0],
732
+ name = field[1],
733
+ typecode = field[6],
734
+ typestring = field[5],
735
+ limit = field[7],
736
+ precision = field[8],
737
+ scale = field[9],
738
+ default = field[4],
739
+ nullable = field[3]]
740
+ columns << FrontBaseColumn.new(*args)
741
+ end
742
+ columns
743
+ end
744
+
745
+ def create_table(name, options = {})
746
+ table_definition = TableDefinition.new(self)
747
+ table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
748
+
749
+ yield table_definition
750
+
751
+ if options[:force]
752
+ drop_table(name) rescue nil
753
+ end
754
+
755
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
756
+ create_sql << "#{name} ("
757
+ create_sql << table_definition.to_sql
758
+ create_sql << ") #{options[:options]}"
759
+ begin_db_transaction
760
+ execute create_sql
761
+ commit_db_transaction
762
+ rescue ActiveRecord::StatementInvalid => e
763
+ raise e unless e.message.match(/Table name - \w* - exists/)
764
+ end
765
+
766
+ def rename_table(name, new_name)
767
+ columns = columns(name)
768
+ pkcol = columns.find {|c| c.fb_autogen}
769
+ execute "ALTER TABLE NAME #{name} TO #{new_name}"
770
+ if pkcol
771
+ change_column_default(new_name,pkcol.name,"UNIQUE")
772
+ begin_db_transaction
773
+ mpk = select_value("SELECT MAX(#{pkcol.name}) FROM #{new_name}")
774
+ mpk = 0 if mpk.nil?
775
+ execute "SET UNIQUE=#{mpk} FOR #{new_name}"
776
+ commit_db_transaction
777
+ end
778
+ end
779
+
780
+ # Drops a table from the database.
781
+ def drop_table(name, options = {})
782
+ execute "DROP TABLE #{name} RESTRICT"
783
+ rescue ActiveRecord::StatementInvalid => e
784
+ raise e unless e.message.match(/Referenced TABLE - \w* - does not exist/)
785
+ end
786
+
787
+ # Adds a new column to the named table.
788
+ # See TableDefinition#column for details of the options you can use.
789
+ def add_column(table_name, column_name, type, options = {})
790
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
791
+ options[:type] = type
792
+ add_column_options!(add_column_sql, options)
793
+ execute(add_column_sql)
794
+ end
795
+
796
+ def add_column_options!(sql, options) #:nodoc:
797
+ default_value = quote(options[:default], options[:column])
798
+ if options_include_default?(options)
799
+ if options[:type] == :boolean
800
+ default_value = options[:default] == 0 ? quoted_false : quoted_true
801
+ end
802
+ sql << " DEFAULT #{default_value}"
803
+ end
804
+ sql << " NOT NULL" if options[:null] == false
805
+ end
806
+
807
+ # Removes the column from the table definition.
808
+ # ===== Examples
809
+ # remove_column(:suppliers, :qualification)
810
+ def remove_column(table_name, column_name)
811
+ execute "ALTER TABLE #{table_name} DROP #{column_name} RESTRICT"
812
+ end
813
+
814
+ def remove_index(table_name, options = {}) #:nodoc:
815
+ if options[:unique]
816
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{quote_column_name(index_name(table_name, options))} RESTRICT"
817
+ else
818
+ execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
819
+ end
820
+ end
821
+
822
+ def change_column_default(table_name, column_name, default) #:nodoc:
823
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{default}" if default != "NULL"
824
+ end
825
+
826
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
827
+ change_column_sql = %( ALTER COLUMN "#{table_name}"."#{column_name}" TO #{type_to_sql(type, options[:limit])} )
828
+ execute(change_column_sql)
829
+ change_column_sql = %( ALTER TABLE "#{table_name}" ALTER COLUMN "#{column_name}" )
830
+
831
+ if options_include_default?(options)
832
+ default_value = quote(options[:default], options[:column])
833
+ if type == :boolean
834
+ default_value = options[:default] == 0 ? quoted_false : quoted_true
835
+ end
836
+ change_column_sql << " SET DEFAULT #{default_value}"
837
+ end
838
+
839
+ execute(change_column_sql)
840
+
841
+ # change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
842
+ # add_column_options!(change_column_sql, options)
843
+ # execute(change_column_sql)
844
+ end
845
+
846
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
847
+ execute %( ALTER COLUMN NAME "#{table_name}"."#{column_name}" TO "#{new_column_name}" )
848
+ end
849
+
850
+ private
851
+
852
+ # Clean up sql to make it something FrontBase can digest
853
+ def cleanup_fb_sql(sql) #:nodoc:
854
+ # Turn non-standard != into standard <>
855
+ cleansql = sql.gsub("!=", "<>")
856
+ # Strip blank lines and comments
857
+ cleansql.split("\n").reject { |line| line.match(/^(?:\s*|--.*)$/) } * "\n"
858
+ end
859
+ end
860
+ end
861
+ end