activerecord-odbc-adapter 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/AUTHORS +16 -0
  2. data/COPYING +21 -0
  3. data/ChangeLog +139 -0
  4. data/LICENSE +5 -0
  5. data/NEWS +25 -0
  6. data/README +229 -0
  7. data/lib/active_record/connection_adapters/odbc_adapter.rb +1950 -0
  8. data/lib/active_record/vendor/odbcext_db2.rb +87 -0
  9. data/lib/active_record/vendor/odbcext_informix.rb +144 -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 +216 -0
  13. data/lib/active_record/vendor/odbcext_microsoftsqlserver_col.rb +40 -0
  14. data/lib/active_record/vendor/odbcext_mysql.rb +174 -0
  15. data/lib/active_record/vendor/odbcext_oracle.rb +219 -0
  16. data/lib/active_record/vendor/odbcext_postgresql.rb +158 -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_sqlanywhere.rb +115 -0
  20. data/lib/active_record/vendor/odbcext_sqlanywhere_col.rb +49 -0
  21. data/lib/active_record/vendor/odbcext_sybase.rb +213 -0
  22. data/lib/active_record/vendor/odbcext_sybase_col.rb +49 -0
  23. data/lib/active_record/vendor/odbcext_virtuoso.rb +158 -0
  24. data/lib/odbc_adapter.rb +28 -0
  25. data/support/lib/active_record/connection_adapters/abstract/schema_definitions.rb +259 -0
  26. data/support/odbc_rails.diff +367 -0
  27. data/support/pack_odbc.rb +119 -0
  28. data/support/rake/rails_plugin_package_task.rb +212 -0
  29. data/support/rake_fixes/README +6 -0
  30. data/support/rake_fixes/databases.dif +13 -0
  31. data/support/test/base_test.rb +1765 -0
  32. data/support/test/migration_test.rb +1007 -0
  33. data/test/connections/native_odbc/connection.rb +137 -0
  34. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  35. data/test/fixtures/db_definitions/db2.sql +237 -0
  36. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  37. data/test/fixtures/db_definitions/db22.sql +5 -0
  38. data/test/fixtures/db_definitions/informix.drop.sql +33 -0
  39. data/test/fixtures/db_definitions/informix.sql +223 -0
  40. data/test/fixtures/db_definitions/informix2.drop.sql +2 -0
  41. data/test/fixtures/db_definitions/informix2.sql +5 -0
  42. data/test/fixtures/db_definitions/ingres.drop.sql +68 -0
  43. data/test/fixtures/db_definitions/ingres.sql +252 -0
  44. data/test/fixtures/db_definitions/ingres2.drop.sql +2 -0
  45. data/test/fixtures/db_definitions/ingres2.sql +5 -0
  46. data/test/fixtures/db_definitions/mysql.drop.sql +33 -0
  47. data/test/fixtures/db_definitions/mysql.sql +238 -0
  48. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  49. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  50. data/test/fixtures/db_definitions/oracle_odbc.drop.sql +72 -0
  51. data/test/fixtures/db_definitions/oracle_odbc.sql +296 -0
  52. data/test/fixtures/db_definitions/oracle_odbc2.drop.sql +2 -0
  53. data/test/fixtures/db_definitions/oracle_odbc2.sql +6 -0
  54. data/test/fixtures/db_definitions/postgresql.drop.sql +38 -0
  55. data/test/fixtures/db_definitions/postgresql.sql +267 -0
  56. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  57. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  58. data/test/fixtures/db_definitions/progress.drop.sql +67 -0
  59. data/test/fixtures/db_definitions/progress.sql +255 -0
  60. data/test/fixtures/db_definitions/progress2.drop.sql +2 -0
  61. data/test/fixtures/db_definitions/progress2.sql +6 -0
  62. data/test/fixtures/db_definitions/sqlserver.drop.sql +35 -0
  63. data/test/fixtures/db_definitions/sqlserver.sql +247 -0
  64. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  65. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  66. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  67. data/test/fixtures/db_definitions/sybase.sql +222 -0
  68. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  69. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  70. data/test/fixtures/db_definitions/virtuoso.drop.sql +33 -0
  71. data/test/fixtures/db_definitions/virtuoso.sql +218 -0
  72. data/test/fixtures/db_definitions/virtuoso2.drop.sql +2 -0
  73. data/test/fixtures/db_definitions/virtuoso2.sql +5 -0
  74. metadata +166 -0
@@ -0,0 +1,139 @@
1
+ #
2
+ # $Id: odbcext_progress.rb,v 1.3 2008/04/13 22:46:09 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Extension module for Progress v9 and later using SQL-92 engine
6
+ # Copyright (C) 2006 OpenLink Software
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining
9
+ # a copy of this software and associated documentation files (the
10
+ # "Software"), to deal in the Software without restriction, including
11
+ # without limitation the rights to use, copy, modify, merge, publish,
12
+ # distribute, sublicense, and/or sell copies of the Software, and to
13
+ # permit persons to whom the Software is furnished to do so, subject
14
+ # to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be
17
+ # included in all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ #
27
+
28
+ module ODBCExt
29
+
30
+ # ------------------------------------------------------------------------
31
+ # Mandatory methods
32
+ #
33
+
34
+ # #last_insert_id must be implemented for any database which returns
35
+ # false from #prefetch_primary_key?
36
+ # (This adapter returns true for Progress)
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 PUB.#{sequence_name}.NEXTVAL from SYSPROGRESS.SYSCALCTABLE")['sequence_next']
50
+ end
51
+
52
+ # ------------------------------------------------------------------------
53
+ # Method redefinitions
54
+ #
55
+ # DBMS specific methods which override the default implementation
56
+ # provided by the ODBCAdapter core.
57
+
58
+ def quote_column_name(name)
59
+ @logger.unknown("ODBCAdapter#quote_column_name>") if @trace
60
+ @logger.unknown("args=[#{name}]") if @trace
61
+ name = name.to_s if name.class == Symbol
62
+ idQuoteChar = @dsInfo.info[ODBC::SQL_IDENTIFIER_QUOTE_CHAR]
63
+
64
+ return name if !idQuoteChar || ((idQuoteChar = idQuoteChar.strip).length == 0)
65
+ idQuoteChar = idQuoteChar[0]
66
+
67
+ # Avoid quoting any already quoted name
68
+ return name if name[0] == idQuoteChar && name[-1] == idQuoteChar
69
+
70
+ # If a DBMS's SQL_IDENTIFIER_CASE is SQL_IC_UPPER, this adapter's base
71
+ # implementation of #quote_column_name only quotes mixed case names.
72
+ # But for Progress v9 or later, for which we force SQL_IDENTIFIER_CASE to
73
+ # SQL_IC_UPPER (see DSInfo#new), we want to quote *ALL* column names.
74
+ # This is done because many of the Rails tests and fixtures use a column
75
+ # named 'type', but type is a reserved word in Progress SQL. Progress 9
76
+ # accepts the quoting of all column names because its
77
+ # SQL_QUOTED_IDENTIFIER_CASE behaviour is SQL_IC_MIXED.
78
+ idQuoteChar.chr + name + idQuoteChar.chr
79
+ end
80
+
81
+ def create_table(name, options = {})
82
+ @logger.unknown("ODBCAdapter#create_table>") if @trace
83
+ super(name, options)
84
+ # Some ActiveRecord tests insert using an explicit id value. Starting the
85
+ # primary key sequence from 10000 eliminates collisions (and subsequent
86
+ # complaints from Progress of integrity constraint violations) between id's
87
+ # generated from the sequence and explicitly supplied ids.
88
+ # Using explicit and generated id's together should be avoided.
89
+ #
90
+ # Currently, OpenEdge only supports sequences in the PUBLIC (PUB) schema.
91
+ execute "CREATE SEQUENCE PUB.#{name}_seq MINVALUE 10000" unless options[:id] == false
92
+ rescue Exception => e
93
+ @logger.unknown("exception=#{e}") if @trace
94
+ raise ActiveRecord::ActiveRecordError, e.message
95
+ end
96
+
97
+ def drop_table(name, options = {})
98
+ @logger.unknown("ODBCAdapter#drop_table>") if @trace
99
+ super(name, options)
100
+ execute "DROP SEQUENCE PUB.#{name}_seq"
101
+ rescue Exception => e
102
+ if e.message !~ /10520/
103
+ # Error "Sequence not found. (10520)" will be generated
104
+ # if the table was created with options[:id] == false
105
+ @logger.unknown("exception=#{e}") if @trace
106
+ raise ActiveRecord::ActiveRecordError, e.message
107
+ end
108
+ end
109
+
110
+ def change_column_default(table_name, column_name, default)
111
+ @logger.unknown("ODBCAdapter#change_column_default>") if @trace
112
+ @logger.unknown("args=[#{table_name}|#{column_name}]") if @trace
113
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} SET DEFAULT #{quote(default)}"
114
+ rescue Exception => e
115
+ @logger.unknown("exception=#{e}") if @trace
116
+ raise ActiveRecord::ActiveRecordError, e.message
117
+ end
118
+
119
+ def remove_column(table_name, column_name)
120
+ @logger.unknown("ODBCAdapter#remove_column>") if @trace
121
+ # Although this command is documented in the OpenEdge SQL Reference,
122
+ # it returns error -20024 ("Sorry, operation not yet implemented").
123
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
124
+ rescue Exception => e
125
+ @logger.unknown("exception=#{e}") if @trace
126
+ raise ActiveRecord::ActiveRecordError, e.message
127
+ end
128
+
129
+ def tables(name = nil)
130
+ # Hide system tables.
131
+ super(name).delete_if {|t| t =~ /^sys/i }
132
+ end
133
+
134
+ def indexes(table_name, name = nil)
135
+ # Hide primary key indexes
136
+ super(table_name, name).delete_if { |i| i.unique && i.name =~ /^sys/i }
137
+ end
138
+
139
+ end # module
@@ -0,0 +1,259 @@
1
+ #
2
+ # $Id: odbcext_progress89.rb,v 1.3 2008/04/13 22:46:09 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Extension module for Progress v8 and earlier using SQL-89 engine
6
+ #
7
+ # Copyright (C) 2006 OpenLink Software
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject
15
+ # to the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+
29
+ module ODBCExt
30
+
31
+ # ------------------------------------------------------------------------
32
+ # Mandatory methods
33
+ #
34
+
35
+ # #last_insert_id must be implemented for any database which returns
36
+ # false from #prefetch_primary_key?
37
+ # (This adapter returns true for Progress)
38
+ #def last_insert_id(table, sequence_name, stmt = nil)
39
+ #end
40
+
41
+ # #next_sequence_value must be implemented for any database which returns
42
+ # true from #prefetch_primary_key?
43
+ #
44
+ # Returns the next sequence value from a sequence generator. Not generally
45
+ # called directly; used by ActiveRecord to get the next primary key value
46
+ # when inserting a new database record (see #prefetch_primary_key?).
47
+ def next_sequence_value(sequence_name)
48
+ @logger.unknown("ODBCAdapter#next_sequence_value>") if @trace
49
+ #@logger.unknown("args=[#{sequence_name}]") if @trace
50
+ sequence_next_val(sequence_name.to_s)
51
+ rescue Exception => e
52
+ @logger.unknown("exception=#{e}") if @trace
53
+ raise ActiveRecord::ActiveRecordError, e.message
54
+ end
55
+
56
+ # ------------------------------------------------------------------------
57
+ # Method redefinitions
58
+ #
59
+ # DBMS specific methods which override the default implementation
60
+ # provided by the ODBCAdapter core.
61
+
62
+ # Progress SQL89 requires that the DEFAULT specification *follows*
63
+ # any NOT NULL constraint.
64
+ def add_column_options!(sql, options) #:nodoc:
65
+ @logger.unknown("ODBCAdapter#add_column_options!>") if @trace
66
+ @logger.unknown("args=[#{sql}]") if @trace
67
+ sql << " NOT NULL" if options[:null] == false
68
+ # Progress 89 doesn't accept 'DEFAULT NULL'
69
+ sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options) && !options[:default].nil?
70
+ rescue Exception => e
71
+ @logger.unknown("exception=#{e}") if @trace
72
+ raise StatementInvalid, e.message
73
+ end
74
+
75
+ def quote_column_name(name)
76
+ @logger.unknown("ODBCAdapter#quote_column_name>") if @trace
77
+ @logger.unknown("args=[#{name}]") if @trace
78
+ # Progress v8 or earlier doesn't support quoted identifiers.
79
+ # ODBC::SQL_IDENTIFIER_QUOTE_CHAR is typically " "
80
+ name.to_s
81
+ end
82
+
83
+ def quoted_date(value)
84
+ @logger.unknown("ODBCAdapter#quoted_date>") if @trace
85
+ @logger.unknown("args=[#{value}]") if @trace
86
+ # Progress v8 doesn't support a datetime or time type,
87
+ # only a date type.
88
+ if value.acts_like?(:time) # Time, DateTime
89
+ #%Q!{ts '#{value.strftime("%Y-%m-%d %H:%M:%S")}'}!
90
+ %Q!{d '#{value.strftime("%Y-%m-%d")}'}!
91
+ else # Date
92
+ %Q!{d '#{value.strftime("%Y-%m-%d")}'}!
93
+ end
94
+ end
95
+
96
+ # Progress SQL-89 doesn't support column aliases
97
+ # Strip 'AS <alias>' from all selects
98
+
99
+ def select_all(sql, name = nil)
100
+ super(remove_select_column_aliases(sql), name)
101
+ end
102
+
103
+ def select_one(sql, name = nil)
104
+ super(remove_select_column_aliases(sql), name)
105
+ end
106
+
107
+ def select_value(sql, name = nil)
108
+ super(remove_select_column_aliases(sql), name)
109
+ end
110
+
111
+ def select_values(sql, name = nil)
112
+ super(remove_select_column_aliases(sql), name)
113
+ end
114
+
115
+ def indexes(table_name, name = nil)
116
+ # Hide primary key indexes
117
+ super(table_name, name).delete_if { |i| i.unique && i.name =~ /^sql/ }
118
+ end
119
+
120
+ def create_table(name, options = {})
121
+ @logger.unknown("ODBCAdapter#create_table>") if @trace
122
+ super(name, options)
123
+ # Some ActiveRecord tests insert using an explicit id value. Starting the
124
+ # primary key sequence from 10000 eliminates collisions (and subsequent
125
+ # complaints from Progress of integrity constraint violations) between id's
126
+ # generated from the sequence and explicitly supplied ids.
127
+ # Using explicit and generated id's together should be avoided.
128
+ create_sequence("#{name}_seq", 10000) unless options[:id] == false
129
+ rescue Exception => e
130
+ @logger.unknown("exception=#{e}") if @trace
131
+ raise ActiveRecord::ActiveRecordError, e.message
132
+ end
133
+
134
+ def drop_table(name, options = {})
135
+ @logger.unknown("ODBCAdapter#drop_table>") if @trace
136
+ super(name, options)
137
+ drop_sequence("#{name}_seq")
138
+ rescue Exception => e
139
+ @logger.unknown("exception=#{e}") if @trace
140
+ raise ActiveRecord::ActiveRecordError, e.message
141
+ end
142
+
143
+ def change_column_default(table_name, column_name, default)
144
+ @logger.unknown("ODBCAdapter#change_column_default>") if @trace
145
+ @logger.unknown("args=[#{table_name}|#{column_name}]") if @trace
146
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DEFAULT #{quote(default)}"
147
+ rescue Exception => e
148
+ @logger.unknown("exception=#{e}") if @trace
149
+ raise ActiveRecord::ActiveRecordError, e.message
150
+ end
151
+
152
+ def remove_column(table_name, column_name)
153
+ @logger.unknown("ODBCAdapter#remove_column>") if @trace
154
+ # Although this command is documented in the Progress SQL Reference,
155
+ # it returns error 247 ('Unable to understand after -- "alter") if
156
+ # executed via the ODBC driver. (Executing the same command through
157
+ # the Procedure Editor works)
158
+ execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
159
+ rescue Exception => e
160
+ @logger.unknown("exception=#{e}") if @trace
161
+ raise ActiveRecord::ActiveRecordError, e.message
162
+ end
163
+
164
+ def remove_index(table_name, options = {})
165
+ @logger.unknown("ODBCAdapter#remove_index>") if @trace
166
+ execute "DROP INDEX #{index_name(table_name, options)}"
167
+ rescue Exception => e
168
+ @logger.unknown("exception=#{e}") if @trace
169
+ raise ActiveRecord::ActiveRecordError, e.message
170
+ end
171
+
172
+ private
173
+
174
+ def remove_select_column_aliases(sql)
175
+ sql.gsub(/\s+as\s+\w+/i, '')
176
+ end
177
+
178
+ # ------------------------------------------------------------------------
179
+ # Sequence simulation
180
+ #
181
+ # It appears a native Progress (<= v8) sequence can only be created
182
+ # using the Data Dictionary tool, not through SQL89 DDL. Also, there's no
183
+ # way to retrieve a native sequence's next value through SQL89.
184
+ # Instead of using Progress's built-in sequences, we simulate them.
185
+
186
+ SEQ_DFLT_START_VAL = 10000
187
+ SEQS_DFLT_TBL_NAME = "railsseqs"
188
+ @@seqs_table_exists = false
189
+
190
+ # Creates a simulated sequence
191
+ def create_sequence(name, start_val = SEQ_DFLT_START_VAL)
192
+ raise ActiveRecordError, "sequence start value <= 0" if start_val <= 0
193
+ ensure_sequences_table unless @@seqs_table_exists
194
+ sql = "INSERT INTO #{SEQS_DFLT_TBL_NAME}(SEQ_NAME, SEQ_NEXT_VAL) VALUES ('#{name.upcase}', #{start_val})"
195
+ @connection.do(sql)
196
+ end
197
+
198
+ # Drops a simulated sequence
199
+ def drop_sequence(name)
200
+ begin
201
+ sql = "DELETE FROM #{SEQS_DFLT_TBL_NAME} WHERE SEQ_NAME = '#{name.upcase}'"
202
+ @connection.do(sql)
203
+ rescue Exception => e
204
+ # Tables may be created without an accompanying sequence if
205
+ # #create_table wasn't used to create the table or :id => false was
206
+ # specified. So, the sequence table may not exist. Trap error 962:
207
+ # "Table <sequence table name> does not exist or cannot be accessed"
208
+ raise unless e.message =~ /962/
209
+ end
210
+ end
211
+
212
+ def sequence_next_val(name)
213
+ begin
214
+ begin_db_transaction
215
+ sql = "SELECT SEQ_NEXT_VAL FROM #{SEQS_DFLT_TBL_NAME} WHERE SEQ_NAME = '#{name.upcase}'"
216
+ sql << " FOR UPDATE"
217
+ next_val = select_value(sql, 'next_sequence_value')
218
+ if next_val.nil?
219
+ # The table doesn't yet have an accompanying sequence.
220
+ # Assume the table was created using #execute('CREATE TABLE...')
221
+ # instead of #create_table. (Rails uses the former method when
222
+ # creating the test database schema from the development database.)
223
+
224
+ # Progress commits DDL immediately, ending the current transaction
225
+ commit_db_transaction
226
+ create_sequence(name)
227
+ begin_db_transaction
228
+ next_val = SEQ_DFLT_START_VAL
229
+ end
230
+ sql = "UPDATE #{SEQS_DFLT_TBL_NAME} SET SEQ_NEXT_VAL = SEQ_NEXT_VAL + 1 "
231
+ sql << "WHERE SEQ_NAME = '#{name.upcase}'"
232
+ @connection.do(sql)
233
+ commit_db_transaction
234
+ rescue Exception => e
235
+ if e.message =~ /962/
236
+ # Sequence table doesn't exist yet. Can happen if tables are created
237
+ # using #execute instead of #create_table.
238
+ rollback_db_transaction
239
+ ensure_sequences_table
240
+ retry
241
+ end
242
+ rollback_db_transaction
243
+ raise
244
+ end
245
+
246
+ next_val
247
+ end
248
+
249
+ def ensure_sequences_table
250
+ unless tables(SEQS_DFLT_TBL_NAME).include?(SEQS_DFLT_TBL_NAME)
251
+ sql = "CREATE TABLE #{dbmsIdentCase(SEQS_DFLT_TBL_NAME)} ("
252
+ sql << "SEQ_NAME CHARACTER(32) NOT NULL UNIQUE, "
253
+ sql << "SEQ_NEXT_VAL INTEGER NOT NULL DEFAULT #{SEQ_DFLT_START_VAL})"
254
+ @connection.do(sql)
255
+ end
256
+ @@seq_table_exists = true
257
+ end
258
+
259
+ end # module
@@ -0,0 +1,115 @@
1
+ #
2
+ # $Id: odbcext_sqlanywhere.rb,v 1.2 2008/04/22 16:54:57 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
+ def last_insert_id(table, sequence_name, stmt = nil)
36
+ @logger.unknown("ODBCAdapter#last_insert_id>") if @trace
37
+ select_value("select @@IDENTITY", 'last_insert_id')
38
+ end
39
+
40
+ # ------------------------------------------------------------------------
41
+ # Optional methods
42
+ #
43
+ # These are supplied for a DBMS only if necessary.
44
+ # ODBCAdapter tests for optional methods using Object#respond_to?
45
+
46
+ # Pre action for ODBCAdapter#insert
47
+ def pre_insert(sql, name, pk, id_value, sequence_name)
48
+ @iiTable = get_table_name(sql)
49
+ @iiCol = get_autounique_column(@iiTable)
50
+ @iiEnabled = false
51
+
52
+ if @iiCol != nil
53
+ if query_contains_autounique_col(sql, @iiCol)
54
+ begin
55
+ @connection.do(enable_identity_insert(@iiTable, true))
56
+ @iiEnabled = true
57
+ rescue Exception => e
58
+ raise ActiveRecordError, "IDENTITY_INSERT could not be turned on"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # Post action for ODBCAdapter#insert
65
+ def post_insert(sql, name, pk, id_value, sequence_name)
66
+ if @iiEnabled
67
+ begin
68
+ @connection.do(enable_identity_insert(@iiTable, false))
69
+ rescue Exception => e
70
+ raise ActiveRecordError, "IDENTITY_INSERT could not be turned off"
71
+ end
72
+ end
73
+ end
74
+
75
+ # ------------------------------------------------------------------------
76
+ # Method redefinitions
77
+ #
78
+ # DBMS specific methods which override the default implementation
79
+ # provided by the ODBCAdapter core.
80
+
81
+ def get_table_name(sql)
82
+ if sql =~ /^\s*insert\s+into\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
83
+ $1
84
+ elsif sql =~ /from\s+([^\(\s]+)\s*/i
85
+ $1
86
+ else
87
+ nil
88
+ end end
89
+
90
+ def get_autounique_column(table_name)
91
+ @table_columns = {} unless @table_columns
92
+ @table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
93
+ @table_columns[table_name].each do |col|
94
+ return col.name if col.auto_unique?
95
+ end
96
+
97
+ return nil
98
+ end
99
+
100
+ def query_contains_autounique_col(sql, col)
101
+ sql =~ /(\[#{col}\])|("#{col}")/
102
+ end
103
+
104
+ def enable_identity_insert(table_name, enable = true)
105
+ if has_autounique_column(table_name)
106
+ "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
107
+ end
108
+ end
109
+
110
+ def has_autounique_column(table_name)
111
+ !get_autounique_column(table_name).nil?
112
+ end
113
+
114
+
115
+ end # module