odbc-rails 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/AUTHORS +16 -0
  2. data/COPYING +21 -0
  3. data/ChangeLog +89 -0
  4. data/LICENSE +5 -0
  5. data/NEWS +12 -0
  6. data/README +282 -0
  7. data/lib/active_record/connection_adapters/odbc_adapter.rb +1792 -0
  8. data/lib/active_record/vendor/odbcext_db2.rb +87 -0
  9. data/lib/active_record/vendor/odbcext_informix.rb +132 -0
  10. data/lib/active_record/vendor/odbcext_informix_col.rb +45 -0
  11. data/lib/active_record/vendor/odbcext_ingres.rb +156 -0
  12. data/lib/active_record/vendor/odbcext_microsoftsqlserver.rb +185 -0
  13. data/lib/active_record/vendor/odbcext_microsoftsqlserver_col.rb +40 -0
  14. data/lib/active_record/vendor/odbcext_mysql.rb +136 -0
  15. data/lib/active_record/vendor/odbcext_oracle.rb +220 -0
  16. data/lib/active_record/vendor/odbcext_postgresql.rb +179 -0
  17. data/lib/active_record/vendor/odbcext_progress.rb +139 -0
  18. data/lib/active_record/vendor/odbcext_progress89.rb +259 -0
  19. data/lib/active_record/vendor/odbcext_sybase.rb +212 -0
  20. data/lib/active_record/vendor/odbcext_sybase_col.rb +49 -0
  21. data/lib/active_record/vendor/odbcext_virtuoso.rb +146 -0
  22. data/lib/odbc_adapter.rb +28 -0
  23. data/support/lib/active_record/connection_adapters/abstract/schema_definitions.rb +259 -0
  24. data/support/odbc_rails.diff +707 -0
  25. data/support/pack_odbc.rb +119 -0
  26. data/support/rake/rails_plugin_package_task.rb +212 -0
  27. data/support/test/base_test.rb +1349 -0
  28. data/support/test/migration_test.rb +566 -0
  29. data/test/connections/native_odbc/connection.rb +95 -0
  30. data/test/fixtures/db_definitions/db2.drop.sql +30 -0
  31. data/test/fixtures/db_definitions/db2.sql +217 -0
  32. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  33. data/test/fixtures/db_definitions/db22.sql +5 -0
  34. data/test/fixtures/db_definitions/informix.drop.sql +30 -0
  35. data/test/fixtures/db_definitions/informix.sql +205 -0
  36. data/test/fixtures/db_definitions/informix2.drop.sql +2 -0
  37. data/test/fixtures/db_definitions/informix2.sql +5 -0
  38. data/test/fixtures/db_definitions/ingres.drop.sql +62 -0
  39. data/test/fixtures/db_definitions/ingres.sql +232 -0
  40. data/test/fixtures/db_definitions/ingres2.drop.sql +2 -0
  41. data/test/fixtures/db_definitions/ingres2.sql +5 -0
  42. data/test/fixtures/db_definitions/mysql.drop.sql +30 -0
  43. data/test/fixtures/db_definitions/mysql.sql +219 -0
  44. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  45. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  46. data/test/fixtures/db_definitions/oracle_odbc.drop.sql +64 -0
  47. data/test/fixtures/db_definitions/oracle_odbc.sql +257 -0
  48. data/test/fixtures/db_definitions/oracle_odbc2.drop.sql +2 -0
  49. data/test/fixtures/db_definitions/oracle_odbc2.sql +6 -0
  50. data/test/fixtures/db_definitions/progress.drop.sql +61 -0
  51. data/test/fixtures/db_definitions/progress.sql +234 -0
  52. data/test/fixtures/db_definitions/progress2.drop.sql +2 -0
  53. data/test/fixtures/db_definitions/progress2.sql +6 -0
  54. data/test/fixtures/db_definitions/sqlserver.drop.sql +30 -0
  55. data/test/fixtures/db_definitions/sqlserver.sql +203 -0
  56. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  57. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  58. data/test/fixtures/db_definitions/sybase.drop.sql +31 -0
  59. data/test/fixtures/db_definitions/sybase.sql +204 -0
  60. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  61. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  62. data/test/fixtures/db_definitions/virtuoso.drop.sql +30 -0
  63. data/test/fixtures/db_definitions/virtuoso.sql +200 -0
  64. data/test/fixtures/db_definitions/virtuoso2.drop.sql +2 -0
  65. data/test/fixtures/db_definitions/virtuoso2.sql +5 -0
  66. metadata +149 -0
@@ -0,0 +1,40 @@
1
+ #
2
+ # $Id: odbcext_microsoftsqlserver_col.rb,v 1.1 2006/12/06 14:42:11 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Copyright (C) 2006 OpenLink Software
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject
13
+ # to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #
26
+
27
+ module ODBCColumnExt
28
+
29
+ # Is the column a numeric autoincrementing column?
30
+ def auto_unique?
31
+ @autounique
32
+ end
33
+
34
+ private
35
+
36
+ def autoUnique?
37
+ @nativeType =~ /\bidentity\b/i
38
+ end
39
+
40
+ end # module
@@ -0,0 +1,136 @@
1
+ #
2
+ # $Id: odbcext_mysql.rb,v 1.1 2006/12/06 14:42:11 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Copyright (C) 2006 OpenLink Software
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject
13
+ # to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #
26
+
27
+ module ODBCExt
28
+
29
+ # ------------------------------------------------------------------------
30
+ # Mandatory methods
31
+ #
32
+ # The ODBCAdapter core doesn't not implement these methods
33
+
34
+ # #last_insert_id must be implemented for any database which returns
35
+ # false from #prefetch_primary_key?
36
+ #
37
+ # This method assumes that the table inserted into has a primary key defined
38
+ # as INT AUTOINCREMENT
39
+ def last_insert_id(table, sequence_name, stmt = nil)
40
+ @logger.unknown("ODBCAdapter#last_insert_id>") if @trace
41
+ select_value("select LAST_INSERT_ID()", 'last_insert_id')
42
+ end
43
+
44
+ # ------------------------------------------------------------------------
45
+ # Optional methods
46
+ #
47
+ # These are supplied for a DBMS only if necessary.
48
+ # ODBCAdapter tests for optional methods using Object#respond_to?
49
+
50
+ # Pre action for ODBCAdapter#insert
51
+ # def pre_insert(sql, name, pk, id_value, sequence_name)
52
+ # end
53
+
54
+ # Post action for ODBCAdapter#insert
55
+ # def post_insert(sql, name, pk, id_value, sequence_name)
56
+ # end
57
+
58
+ # ------------------------------------------------------------------------
59
+ # Method redefinitions
60
+ #
61
+ # DBMS specific methods which override the default implementation
62
+ # provided by the ODBCAdapter core.
63
+
64
+ def quote_string(string)
65
+ @logger.unknown("ODBCAdapter#quote_string>") if @trace
66
+
67
+ # MySQL requires backslashes to be escaped
68
+ string.gsub(/\\/, '\&\&').gsub(/'/, "''")
69
+ end
70
+
71
+ def create_table(name, options = {})
72
+ @logger.unknown("ODBCAdapter#create_table>") if @trace
73
+ super(name, {:options => "ENGINE=InnoDB"}.merge(options))
74
+ rescue Exception => e
75
+ @logger.unknown("exception=#{e}") if @trace
76
+ raise
77
+ end
78
+
79
+ def rename_table(name, new_name)
80
+ @logger.unknown("ODBCAdapter#rename_table>") if @trace
81
+ execute "RENAME TABLE #{name} TO #{new_name}"
82
+ rescue Exception => e
83
+ @logger.unknown("exception=#{e}") if @trace
84
+ raise
85
+ end
86
+
87
+ def change_column(table_name, column_name, type, options = {})
88
+ @logger.unknown("ODBCAdapter#change_column>") if @trace
89
+ # column_name.to_s used in case column_name is a symbol
90
+ options[:default] ||= columns(table_name).find { |c| c.name == column_name.to_s }.default
91
+ change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
92
+ add_column_options!(change_column_sql, options)
93
+ execute(change_column_sql)
94
+ rescue Exception => e
95
+ @logger.unknown("exception=#{e}") if @trace
96
+ raise
97
+ end
98
+
99
+ def rename_column(table_name, column_name, new_column_name)
100
+ @logger.unknown("ODBCAdapter#rename_column>") if @trace
101
+ col = columns(table_name).find{ |c| c.name == column_name.to_s }
102
+ current_type = col.sql_type
103
+ current_type << "(#{col.limit})" if col.limit
104
+ execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
105
+ rescue Exception => e
106
+ @logger.unknown("exception=#{e}") if @trace
107
+ raise
108
+ end
109
+
110
+ def change_column_default(table_name, column_name, default)
111
+ @logger.unknown("ODBCAdapter#change_column_default>") if @trace
112
+ col = columns(table_name).find{ |c| c.name == column_name.to_s }
113
+ current_type = col.sql_type
114
+ current_type << "(#{col.limit})" if col.limit
115
+ change_column(table_name, column_name, current_type, { :default => default })
116
+ rescue Exception => e
117
+ @logger.unknown("exception=#{e}") if @trace
118
+ raise
119
+ end
120
+
121
+ def indexes(table_name, name = nil)
122
+ # Skip primary key indexes
123
+ super(table_name, name).delete_if { |i| i.unique && i.name =~ /^PRIMARY$/ }
124
+ end
125
+
126
+ def structure_dump
127
+ @logger.unknown("ODBCAdapter#structure_dump>") if @trace
128
+ select_all("SHOW TABLES").inject("") do |structure, table|
129
+ structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
130
+ end
131
+ rescue Exception => e
132
+ @logger.unknown("exception=#{e}") if @trace
133
+ raise
134
+ end
135
+
136
+ end
@@ -0,0 +1,220 @@
1
+ #
2
+ # $Id: odbcext_oracle.rb,v 1.1 2006/12/06 14:42:11 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Copyright (C) 2006 OpenLink Software
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject
13
+ # to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #
26
+
27
+ module ODBCExt
28
+
29
+ # ------------------------------------------------------------------------
30
+ # Mandatory methods
31
+ #
32
+
33
+ # #last_insert_id must be implemented for any database which returns
34
+ # false from #prefetch_primary_key?
35
+ # (This adapter returns true for Oracle)
36
+
37
+ #def last_insert_id(table, sequence_name, stmt = nil)
38
+ #end
39
+
40
+ # #next_sequence_value must be implemented for any database which returns
41
+ # true from #prefetch_primary_key?
42
+ #
43
+ # Returns the next sequence value from a sequence generator. Not generally
44
+ # called directly; used by ActiveRecord to get the next primary key value
45
+ # when inserting a new database record (see #prefetch_primary_key?).
46
+ def next_sequence_value(sequence_name)
47
+ @logger.unknown("ODBCAdapter#next_sequence_value>") if @trace
48
+ #@logger.unknown("args=[#{sequence_name}]") if @trace
49
+ select_one("select #{sequence_name}.nextval id from dual")['id']
50
+ end
51
+
52
+ # ------------------------------------------------------------------------
53
+ # Optional methods
54
+ #
55
+ # These are supplied for a DBMS only if necessary.
56
+ # ODBCAdapter tests for optional methods using Object#respond_to?
57
+
58
+ # Pre action for ODBCAdapter#insert
59
+ # def pre_insert(sql, name, pk, id_value, sequence_name)
60
+ # end
61
+
62
+ # Post action for ODBCAdapter#insert
63
+ # def post_insert(sql, name, pk, id_value, sequence_name)
64
+ # end
65
+
66
+ # ------------------------------------------------------------------------
67
+ # Method redefinitions
68
+ #
69
+ # DBMS specific methods which override the default implementation
70
+ # provided by the ODBCAdapter core.
71
+
72
+ def quoted_date(value)
73
+ @logger.unknown("ODBCAdapter#quoted_date>") if @trace
74
+ # Ideally, we'd return an ODBC date or timestamp literal escape
75
+ # sequence, but not all ODBC drivers support them.
76
+ case value
77
+ when Time, DateTime
78
+ #%Q!{ts '#{value.strftime("%Y-%m-%d %H:%M:%S")}'}!
79
+ "to_timestamp(\'#{value.strftime("%Y-%m-%d %H:%M:%S")}\', \'YYYY-MM-DD HH24:MI:SS\')"
80
+ when Date
81
+ #%Q!{d '#{value.strftime("%Y-%m-%d")}'}!
82
+ "to_timestamp(\'#{value.strftime("%Y-%m-%d")}\', \'YYYY-MM-DD\')"
83
+ end
84
+ end
85
+
86
+ def create_table(name, options = {})
87
+ @logger.unknown("ODBCAdapter#create_table>") if @trace
88
+ super(name, options)
89
+ # Some ActiveRecord tests insert using an explicit id value. Starting the
90
+ # primary key sequence from 10000 eliminates collisions (and subsequent
91
+ # complaints from Oracle of integrity constraint violations) between id's
92
+ # generated from the sequence and explicitly supplied ids.
93
+ # Using explicit and generated id's together should be avoided.
94
+ execute "CREATE SEQUENCE #{name}_seq START WITH 10000" unless options[:id] == false
95
+ rescue Exception => e
96
+ @logger.unknown("exception=#{e}") if @trace
97
+ raise
98
+ end
99
+
100
+ def rename_table(name, new_name)
101
+ @logger.unknown("ODBCAdapter#rename_table>") if @trace
102
+ execute "RENAME #{name} TO #{new_name}"
103
+ execute "RENAME #{name}_seq TO #{new_name}_seq"
104
+ rescue Exception => e
105
+ @logger.unknown("exception=#{e}") if @trace
106
+ raise
107
+ end
108
+
109
+ def drop_table(name)
110
+ @logger.unknown("ODBCAdapter#drop_table>") if @trace
111
+ super(name)
112
+ execute "DROP SEQUENCE #{name}_seq"
113
+ rescue Exception => e
114
+ if e.message !~ /ORA-02289/i
115
+ # Error "ORA-02289: sequence does not exist" will be generated
116
+ # if the table was created with options[:id] == false
117
+ @logger.unknown("exception=#{e}") if @trace
118
+ raise
119
+ end
120
+ end
121
+
122
+ def remove_column(table_name, column_name)
123
+ @logger.unknown("ODBCAdapter#remove_column>") if @trace
124
+ execute "ALTER TABLE #{table_name} DROP COLUMN #{quote_column_name(column_name)}"
125
+ rescue Exception => e
126
+ @logger.unknown("exception=#{e}") if @trace
127
+ raise
128
+ end
129
+
130
+ def change_column(table_name, column_name, type, options = {})
131
+ @logger.unknown("ODBCAdapter#change_column>") if @trace
132
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
133
+ add_column_options!(change_column_sql, options)
134
+ execute(change_column_sql)
135
+ rescue Exception => e
136
+ @logger.unknown("exception=#{e}") if @trace
137
+ raise
138
+ end
139
+
140
+ def change_column_default(table_name, column_name, default)
141
+ @logger.unknown("ODBCAdapter#change_column_default>") if @trace
142
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
143
+ rescue Exception => e
144
+ @logger.unknown("exception=#{e}") if @trace
145
+ raise
146
+ end
147
+
148
+ def rename_column(table_name, column_name, new_column_name)
149
+ @logger.unknown("ODBCAdapter#rename_column>") if @trace
150
+ execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
151
+ rescue Exception => e
152
+ @logger.unknown("exception=#{e}") if @trace
153
+ raise
154
+ end
155
+
156
+ def remove_index(table_name, options = {})
157
+ @logger.unknown("ODBCAdapter#remove_index>") if @trace
158
+ execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
159
+ rescue Exception => e
160
+ @logger.unknown("exception=#{e}") if @trace
161
+ raise
162
+ end
163
+
164
+ def tables(name = nil)
165
+ # Hide dropped tables in Oracle's recyclebin.
166
+ super(name).delete_if {|t| t =~ /^BIN\$/i }
167
+ end
168
+
169
+ def indexes(table_name, name = nil)
170
+ # Oracle creates a unique index for a table's primary key.
171
+ # Hide any such index. Oracle uses system-generated names
172
+ # beginning with "SYS_" for implicitly generated schema objects.
173
+ #
174
+ # If this isn't done...
175
+ # Rails' 'rake test_units' attempts to create this index explicitly,
176
+ # but Oracle rejects this as the index has already been created
177
+ # automatically when the table was defined.
178
+ super(table_name, name).delete_if { |i| i.unique && i.name =~ /^SYS_/i }
179
+ end
180
+
181
+ def structure_dump
182
+ @logger.unknown("ODBCAdapter#structure_dump>") if @trace
183
+ s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
184
+ structure << "create sequence #{seq.to_a.first.last};\n\n"
185
+ end
186
+
187
+ select_all("select table_name from user_tables").inject(s) do |structure, table|
188
+ ddl = "create table #{table.to_a.first.last} (\n "
189
+ cols = select_all(%Q{
190
+ select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
191
+ from user_tab_columns
192
+ where table_name = '#{table.to_a.first.last}'
193
+ order by column_id
194
+ }).map do |row|
195
+ col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
196
+ if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
197
+ col << "(#{row['data_precision'].to_i}"
198
+ col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
199
+ col << ')'
200
+ elsif row['data_type'].include?('CHAR')
201
+ col << "(#{row['data_length'].to_i})"
202
+ end
203
+ col << " default #{row['data_default']}" if !row['data_default'].nil?
204
+ col << ' not null' if row['nullable'] == 'N'
205
+ col
206
+ end
207
+ ddl << cols.join(",\n ")
208
+ ddl << ");\n\n"
209
+ structure << ddl
210
+ end
211
+ rescue Exception => e
212
+ @logger.unknown("exception=#{e}") if @trace
213
+ raise
214
+ end
215
+
216
+ # ------------------------------------------------------------------------
217
+ # Private methods to support methods above
218
+ #
219
+
220
+ end # module
@@ -0,0 +1,179 @@
1
+ #
2
+ # $Id: odbcext_postgresql.rb,v 1.1 2006/12/06 14:42:11 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Copyright (C) 2006 OpenLink Software
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject
13
+ # to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #
26
+
27
+ module ODBCExt
28
+
29
+ # ------------------------------------------------------------------------
30
+ # Mandatory methods
31
+ #
32
+
33
+ # #last_insert_id must be implemented for any database which returns
34
+ # false from #prefetch_primary_key?
35
+
36
+ #def last_insert_id(table, sequence_name, stmt = nil)
37
+ #end
38
+
39
+ # ------------------------------------------------------------------------
40
+ # Optional methods
41
+ #
42
+ # These are supplied for a DBMS only if necessary.
43
+ # ODBCAdapter tests for optional methods using Object#respond_to?
44
+
45
+ # Pre action for ODBCAdapter#insert
46
+ # def pre_insert(sql, name, pk, id_value, sequence_name)
47
+ # end
48
+
49
+ # Post action for ODBCAdapter#insert
50
+ # def post_insert(sql, name, pk, id_value, sequence_name)
51
+ # end
52
+
53
+ # ------------------------------------------------------------------------
54
+ # Method redefinitions
55
+ #
56
+ # DBMS specific methods which override the default implementation
57
+ # provided by the ODBCAdapter core.
58
+
59
+ def default_sequence_name(table, primary_key=nil)
60
+ @logger.unknown("ODBCAdapter#default_sequence_name>") if @trace
61
+ #@logger.unknown("args=[#{table}|#{primary_key}]") if @trace
62
+ default_pk, default_seq = pk_and_sequence_for(table)
63
+ default_seq || "#{table}_#{primary_key || default_pk || 'id'}_seq"
64
+ rescue Exception => e
65
+ @logger.unknown("exception=#{e}") if @trace
66
+ raise
67
+ end
68
+
69
+ def rename_table(name, new_name)
70
+ @logger.unknown("ODBCAdapter#rename_table>") if @trace
71
+ execute "ALTER TABLE #{name} RENAME TO #{new_name}"
72
+ rescue Exception => e
73
+ @logger.unknown("exception=#{e}") if @trace
74
+ raise
75
+ end
76
+
77
+ def add_column(table_name, column_name, type, options = {})
78
+ @logger.unknown("ODBCAdapter#add_column>") if @trace
79
+ sql_commands = ["ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"]
80
+ if options[:default]
81
+ sql_commands << "ALTER TABLE #{table_name} ALTER #{column_name} SET DEFAULT '#{options[:default]}'"
82
+ end
83
+ if options[:null] == false
84
+ sql_commands << "ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL"
85
+ end
86
+ sql_commands.each { |cmd| execute(cmd) }
87
+ rescue Exception => e
88
+ @logger.unknown("exception=#{e}") if @trace
89
+ raise
90
+ end
91
+
92
+ def change_column(table_name, column_name, type, options = {})
93
+ @logger.unknown("ODBCAdapter#change_column>") if @trace
94
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type}"
95
+ change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
96
+ rescue Exception => e
97
+ @logger.unknown("exception=#{e}") if @trace
98
+ raise
99
+ end
100
+
101
+ def change_column_default(table_name, column_name, default)
102
+ @logger.unknown("ODBCAdapter#change_column_default>") if @trace
103
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT '#{default}'"
104
+ rescue Exception => e
105
+ @logger.unknown("exception=#{e}") if @trace
106
+ raise
107
+ end
108
+
109
+ def rename_column(table_name, column_name, new_column_name)
110
+ @logger.unknown("ODBCAdapter#rename_column>") if @trace
111
+ execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
112
+ rescue Exception => e
113
+ @logger.unknown("exception=#{e}") if @trace
114
+ raise
115
+ end
116
+
117
+ def remove_index(table_name, options = {})
118
+ @logger.unknown("ODBCAdapter#remove_index>") if @trace
119
+ if Hash === options
120
+ index_name = options[:name]
121
+ else
122
+ index_name = "#{table_name}_#{options}_index"
123
+ end
124
+ execute "DROP INDEX #{index_name}"
125
+ rescue Exception => e
126
+ @logger.unknown("exception=#{e}") if @trace
127
+ raise
128
+ end
129
+
130
+ # ------------------------------------------------------------------------
131
+ # Private methods to support methods above
132
+ #
133
+ private
134
+
135
+ # Find a table's primary key and sequence.
136
+ def pk_and_sequence_for(table)
137
+ # First try looking for a sequence with a dependency on the
138
+ # given table's primary key.
139
+ result = select_all(<<-end_sql, 'PK and serial sequence')[0]
140
+ SELECT attr.attname, name.nspname, seq.relname
141
+ FROM pg_class seq,
142
+ pg_attribute attr,
143
+ pg_depend dep,
144
+ pg_namespace name,
145
+ pg_constraint cons
146
+ WHERE seq.oid = dep.objid
147
+ AND seq.relnamespace = name.oid
148
+ AND seq.relkind = 'S'
149
+ AND attr.attrelid = dep.refobjid
150
+ AND attr.attnum = dep.refobjsubid
151
+ AND attr.attrelid = cons.conrelid
152
+ AND attr.attnum = cons.conkey[1]
153
+ AND cons.contype = 'p'
154
+ AND dep.refobjid = '#{table}'::regclass
155
+ end_sql
156
+
157
+ if result.nil? or result.empty?
158
+ # If that fails, try parsing the primary key's default value.
159
+ # Support the 7.x and 8.0 nextval('foo'::text) as well as
160
+ # the 8.1+ nextval('foo'::regclass).
161
+ result = select_all(<<-end_sql, 'PK and custom sequence')[0]
162
+ SELECT attr.attname, name.nspname, split_part(def.adsrc, '\\\'', 2)
163
+ FROM pg_class t
164
+ JOIN pg_namespace name ON (t.relnamespace = name.oid)
165
+ JOIN pg_attribute attr ON (t.oid = attrelid)
166
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
167
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
168
+ WHERE t.oid = '#{table}'::regclass
169
+ AND cons.contype = 'p'
170
+ AND def.adsrc ~* 'nextval'
171
+ end_sql
172
+ end
173
+ # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, join the current namespace
174
+ result.last['.'] ? [result.first, result.last] : [result.first, "#{result[1]}.#{result[2]}"]
175
+ rescue
176
+ nil
177
+ end
178
+
179
+ end # module