odbc-rails 1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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